diff --git a/examples/go/clients/builder-codes/go.mod b/examples/go/clients/builder-codes/go.mod new file mode 100644 index 0000000000..c012c43ba1 --- /dev/null +++ b/examples/go/clients/builder-codes/go.mod @@ -0,0 +1,35 @@ +module github.com/x402-foundation/x402/examples/go/clients/builder-codes + +go 1.25.4 + +replace github.com/x402-foundation/x402/go => ../../../../go + +require ( + github.com/joho/godotenv v1.5.1 + github.com/x402-foundation/x402/go v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/go-ethereum v1.16.7 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect +) diff --git a/examples/go/clients/builder-codes/go.sum b/examples/go/clients/builder-codes/go.sum new file mode 100644 index 0000000000..ff25b651d2 --- /dev/null +++ b/examples/go/clients/builder-codes/go.sum @@ -0,0 +1,197 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= +github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= +github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= +github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= +github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/go/clients/builder-codes/main.go b/examples/go/clients/builder-codes/main.go new file mode 100644 index 0000000000..d734c3fca6 --- /dev/null +++ b/examples/go/clients/builder-codes/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + "github.com/joho/godotenv" + x402 "github.com/x402-foundation/x402/go" + x402http "github.com/x402-foundation/x402/go/http" + evm "github.com/x402-foundation/x402/go/mechanisms/evm/exact/client" + evmsigners "github.com/x402-foundation/x402/go/signers/evm" +) + +// peek is a logging RoundTripper that prints the buildercode extension data +// flowing in both directions: the codes the server declared in its 402, and +// the codes the SDK forwarded back in the X-Payment header. +type peek struct{ inner http.RoundTripper } + +func (p *peek) RoundTrip(req *http.Request) (*http.Response, error) { + if h := req.Header.Get("X-Payment"); h != "" { + if raw, err := base64.StdEncoding.DecodeString(h); err == nil { + var payload map[string]any + if json.Unmarshal(raw, &payload) == nil { + if ext, ok := payload["extensions"]; ok { + b, _ := json.MarshalIndent(ext, "", " ") + fmt.Printf("➡️ payload.extensions sent:\n%s\n", b) + } + } + } + } + + resp, err := p.inner.RoundTrip(req) + if err != nil { + return resp, err + } + + // Server returns 402 with PaymentRequired in the Payment-Required header, + // not the body. Decode and log the extensions so we can confirm what the + // server declared. + if resp.StatusCode == http.StatusPaymentRequired { + if hdr := resp.Header.Get("Payment-Required"); hdr != "" { + if raw, err := base64.StdEncoding.DecodeString(hdr); err == nil { + var pr map[string]any + if json.Unmarshal(raw, &pr) == nil { + if ext, ok := pr["extensions"]; ok { + b, _ := json.MarshalIndent(ext, "", " ") + fmt.Printf("⬅️ 402 extensions:\n%s\n", b) + } + } + } + } + // Restore body in case downstream wants to read it + body, _ := io.ReadAll(resp.Body) + resp.Body = io.NopCloser(bytes.NewReader(body)) + } + return resp, nil +} + +func main() { + _ = godotenv.Load() + + priv := os.Getenv("EVM_PRIVATE_KEY_CLIENT") + url := os.Getenv("SERVER_URL") + if priv == "" || url == "" { + fmt.Println("EVM_PRIVATE_KEY_CLIENT and SERVER_URL required") + os.Exit(1) + } + + signer, err := evmsigners.NewClientSignerFromPrivateKey(priv) + if err != nil { + fmt.Printf("signer: %v\n", err) + os.Exit(1) + } + + x402Client := x402.Newx402Client() + x402Client.Register("eip155:*", evm.NewExactEvmScheme(signer, nil)) + + httpInner := &http.Client{Transport: &peek{inner: http.DefaultTransport}} + wrapped := x402http.WrapHTTPClientWithPayment(httpInner, x402http.Newx402HTTPClient(x402Client)) + + resp, err := wrapped.Get(url) + if err != nil { + fmt.Printf("request: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + fmt.Printf("\n✅ %s\n%s\n", resp.Status, body) + + if pr := resp.Header.Get("PAYMENT-RESPONSE"); pr != "" { + if raw, err := base64.StdEncoding.DecodeString(pr); err == nil { + fmt.Printf("\n💰 settlement: %s\n", raw) + } + } +} diff --git a/examples/go/facilitator/builder-codes/go.mod b/examples/go/facilitator/builder-codes/go.mod new file mode 100644 index 0000000000..2688cf630e --- /dev/null +++ b/examples/go/facilitator/builder-codes/go.mod @@ -0,0 +1,68 @@ +module github.com/x402-foundation/x402/examples/go/facilitator/builder-codes + +go 1.25.4 + +replace github.com/x402-foundation/x402/go => ../../../../go + +require ( + github.com/ethereum/go-ethereum v1.17.3 + github.com/gin-gonic/gin v1.11.0 + github.com/joho/godotenv v1.5.1 + github.com/x402-foundation/x402/go v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/consensys/gnark-crypto v0.18.1 // indirect + github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/goccy/go-json v0.10.4 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.55.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.16 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + golang.org/x/tools v0.40.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/examples/go/facilitator/builder-codes/go.sum b/examples/go/facilitator/builder-codes/go.sum new file mode 100644 index 0000000000..952b6af253 --- /dev/null +++ b/examples/go/facilitator/builder-codes/go.sum @@ -0,0 +1,263 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= +github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI= +github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc= +github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= +github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q5LDd4Q= +github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac= +github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= +github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/go/facilitator/builder-codes/main.go b/examples/go/facilitator/builder-codes/main.go new file mode 100644 index 0000000000..c9f623a099 --- /dev/null +++ b/examples/go/facilitator/builder-codes/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/gin-gonic/gin" + "github.com/joho/godotenv" + x402 "github.com/x402-foundation/x402/go" + "github.com/x402-foundation/x402/go/extensions/buildercode" + evm "github.com/x402-foundation/x402/go/mechanisms/evm/exact/facilitator" +) + +const ( + DefaultPort = "4022" +) + +func main(){ + _ = godotenv.Load() + + privKey := os.Getenv("EVM_PRIVATE_KEY_FAC") + builderW := os.Getenv("BUILDER_CODE_W") + + if privKey == "" || builderW == "" { + fmt.Println("EVM_PRIVATE_KEY_FAC and BUILDER_CODE_W are required") + os.Exit(1) +} + + signer, err := newFacilitatorEvmSigner(privKey, DefaultEvmRPC) + if err != nil { + fmt.Printf("signer intit failed: %v\n", err) + os.Exit(1) + } + + facilitator := x402.Newx402Facilitator() + + evmNet := x402.Network("eip155:84532") + facilitator.Register( + []x402.Network{evmNet}, + evm.NewExactEvmScheme(signer, &evm.ExactEvmSchemeConfig{ + DeployERC4337WithEIP6492: true, + }), + ) + + bcExt, err := buildercode.NewFacilitatorExtension(buildercode.FacilitatorConfig{ + BuilderCode: builderW, + }) + + if err != nil { + fmt.Printf("invalid BUILDER_CODE_W:: %v\n", err) + } + facilitator.RegisterExtension(bcExt) + fmt.Printf("builder-code attribution: w=%s\n", builderW) + + facilitator.OnAfterSettle(func(ctx x402.FacilitatorSettleResultContext) error{ + fmt.Printf("settled tx=%s (w=%s in suffix)\n", ctx.Result.Transaction, builderW) + return nil + }) + + + gin.SetMode(gin.ReleaseMode) + r := gin.New() + r.Use(gin.Recovery()) + + r.GET("/supported", func(c *gin.Context) { + c.JSON(http.StatusOK, facilitator.GetSupported()) + }) + + r.POST("/verify", func(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + var body struct { + PaymentPayload json.RawMessage `json:"paymentPayload"` + PaymentRequirements json.RawMessage `json:"paymentRequirements"` + } + if err := c.BindJSON(&body); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + res, err := facilitator.Verify(ctx, body.PaymentPayload, body.PaymentRequirements) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, res) + }) + + r.POST("/settle", func(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 60*time.Second) + defer cancel() + var body struct { + PaymentPayload json.RawMessage `json:"paymentPayload"` + PaymentRequirements json.RawMessage `json:"paymentRequirements"` + } + if err := c.BindJSON(&body); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + res, err := facilitator.Settle(ctx, body.PaymentPayload, body.PaymentRequirements) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, res) + }) + + fmt.Printf("🚀 facilitator on :%s\n", DefaultPort) + _ = r.Run(":" + DefaultPort) + +} \ No newline at end of file diff --git a/examples/go/facilitator/builder-codes/signer.go b/examples/go/facilitator/builder-codes/signer.go new file mode 100644 index 0000000000..a0c98a83cb --- /dev/null +++ b/examples/go/facilitator/builder-codes/signer.go @@ -0,0 +1,300 @@ +package main + +import ( + "bytes" + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "strings" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + evmmech "github.com/x402-foundation/x402/go/mechanisms/evm" +) + +const DefaultEvmRPC = "https://sepolia.base.org" + +// facilitatorEvmSigner implements evmmech.FacilitatorEvmSigner. +type facilitatorEvmSigner struct { + privateKey *ecdsa.PrivateKey + address common.Address + client *ethclient.Client + chainID *big.Int +} + +func newFacilitatorEvmSigner(privateKeyHex, rpcURL string) (*facilitatorEvmSigner, error) { + privateKeyHex = strings.TrimPrefix(privateKeyHex, "0x") + + privateKey, err := crypto.HexToECDSA(privateKeyHex) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %w", err) + } + + address := crypto.PubkeyToAddress(privateKey.PublicKey) + + client, err := ethclient.Dial(rpcURL) + if err != nil { + return nil, fmt.Errorf("failed to connect to RPC: %w", err) + } + + chainID, err := client.ChainID(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to get chain ID: %w", err) + } + + return &facilitatorEvmSigner{ + privateKey: privateKey, + address: address, + client: client, + chainID: chainID, + }, nil +} + +func (s *facilitatorEvmSigner) GetAddresses() []string { + return []string{s.address.Hex()} +} + +func (s *facilitatorEvmSigner) GetChainID(ctx context.Context) (*big.Int, error) { + return s.chainID, nil +} + +func (s *facilitatorEvmSigner) VerifyTypedData( + ctx context.Context, + address string, + domain evmmech.TypedDataDomain, + typesMap map[string][]evmmech.TypedDataField, + primaryType string, + message map[string]interface{}, + signature []byte, +) (bool, error) { + chainId := getBigIntFromInterface(domain.ChainID) + typedData := apitypes.TypedData{ + Types: make(apitypes.Types), + PrimaryType: primaryType, + Domain: apitypes.TypedDataDomain{ + Name: getStringFromInterface(domain.Name), + Version: getStringFromInterface(domain.Version), + ChainId: (*math.HexOrDecimal256)(chainId), + VerifyingContract: getStringFromInterface(domain.VerifyingContract), + }, + Message: message, + } + + for typeName, fields := range typesMap { + typedFields := make([]apitypes.Type, len(fields)) + for i, field := range fields { + typedFields[i] = apitypes.Type{Name: field.Name, Type: field.Type} + } + typedData.Types[typeName] = typedFields + } + + if _, exists := typedData.Types["EIP712Domain"]; !exists { + typedData.Types["EIP712Domain"] = []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + } + } + + dataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return false, fmt.Errorf("failed to hash struct: %w", err) + } + + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return false, fmt.Errorf("failed to hash domain: %w", err) + } + + rawData := []byte{0x19, 0x01} + rawData = append(rawData, domainSeparator...) + rawData = append(rawData, dataHash...) + digest := crypto.Keccak256(rawData) + + if len(signature) != 65 { + return false, fmt.Errorf("invalid signature length: %d", len(signature)) + } + + v := signature[64] + if v >= 27 { + v -= 27 + } + sigCopy := make([]byte, 65) + copy(sigCopy, signature) + sigCopy[64] = v + + pubKey, err := crypto.SigToPub(digest, sigCopy) + if err != nil { + return false, fmt.Errorf("failed to recover public key: %w", err) + } + + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + expectedAddr := common.HexToAddress(address) + return bytes.Equal(recoveredAddr.Bytes(), expectedAddr.Bytes()), nil +} + +func (s *facilitatorEvmSigner) ReadContract( + ctx context.Context, + contractAddress string, + abiJSON []byte, + method string, + args ...interface{}, +) (interface{}, error) { + contractABI, err := abi.JSON(strings.NewReader(string(abiJSON))) + if err != nil { + return nil, fmt.Errorf("failed to parse ABI: %w", err) + } + + methodObj, exists := contractABI.Methods[method] + if !exists { + return nil, fmt.Errorf("method %s not found in ABI", method) + } + + data, err := contractABI.Pack(method, args...) + if err != nil { + return nil, fmt.Errorf("failed to pack method call: %w", err) + } + + to := common.HexToAddress(contractAddress) + result, err := s.client.CallContract(ctx, ethereum.CallMsg{To: &to, Data: data}, nil) + if err != nil { + return nil, fmt.Errorf("failed to call contract: %w", err) + } + + if len(methodObj.Outputs) == 0 { + return nil, nil + } + + output, err := methodObj.Outputs.Unpack(result) + if err != nil { + return nil, fmt.Errorf("failed to unpack result: %w", err) + } + if len(output) > 0 { + return output[0], nil + } + return nil, nil +} + +func (s *facilitatorEvmSigner) WriteContract( + ctx context.Context, + contractAddress string, + abiJSON []byte, + method string, + args ...interface{}, +) (string, error) { + contractABI, err := abi.JSON(strings.NewReader(string(abiJSON))) + if err != nil { + return "", fmt.Errorf("failed to parse ABI: %w", err) + } + + data, err := contractABI.Pack(method, args...) + if err != nil { + return "", fmt.Errorf("failed to pack method call: %w", err) + } + + return s.SendTransaction(ctx, contractAddress, data) +} + +func (s *facilitatorEvmSigner) SendTransaction(ctx context.Context, to string, data []byte) (string, error) { + nonce, err := s.client.PendingNonceAt(ctx, s.address) + if err != nil { + return "", fmt.Errorf("failed to get nonce: %w", err) + } + + gasPrice, err := s.client.SuggestGasPrice(ctx) + if err != nil { + return "", fmt.Errorf("failed to get gas price: %w", err) + } + + toAddr := common.HexToAddress(to) + tx := types.NewTransaction(nonce, toAddr, big.NewInt(0), 300000, gasPrice, data) + + signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(s.chainID), s.privateKey) + if err != nil { + return "", fmt.Errorf("failed to sign transaction: %w", err) + } + + if err := s.client.SendTransaction(ctx, signedTx); err != nil { + return "", fmt.Errorf("failed to send transaction: %w", err) + } + + return signedTx.Hash().Hex(), nil +} + +func (s *facilitatorEvmSigner) WaitForTransactionReceipt(ctx context.Context, txHash string) (*evmmech.TransactionReceipt, error) { + hash := common.HexToHash(txHash) + for i := 0; i < 30; i++ { + receipt, err := s.client.TransactionReceipt(ctx, hash) + if err == nil && receipt != nil { + return &evmmech.TransactionReceipt{ + Status: uint64(receipt.Status), + BlockNumber: receipt.BlockNumber.Uint64(), + TxHash: receipt.TxHash.Hex(), + }, nil + } + time.Sleep(1 * time.Second) + } + return nil, fmt.Errorf("transaction receipt not found after 30 seconds") +} + +func (s *facilitatorEvmSigner) GetBalance(ctx context.Context, address, tokenAddress string) (*big.Int, error) { + if tokenAddress == "" || tokenAddress == "0x0000000000000000000000000000000000000000" { + balance, err := s.client.BalanceAt(ctx, common.HexToAddress(address), nil) + if err != nil { + return nil, fmt.Errorf("failed to get balance: %w", err) + } + return balance, nil + } + + const erc20ABI = `[{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"}]` + result, err := s.ReadContract(ctx, tokenAddress, []byte(erc20ABI), "balanceOf", common.HexToAddress(address)) + if err != nil { + return nil, err + } + if balance, ok := result.(*big.Int); ok { + return balance, nil + } + return nil, fmt.Errorf("unexpected balance type: %T", result) +} + +func (s *facilitatorEvmSigner) GetCode(ctx context.Context, address string) ([]byte, error) { + code, err := s.client.CodeAt(ctx, common.HexToAddress(address), nil) + if err != nil { + return nil, fmt.Errorf("failed to get code: %w", err) + } + return code, nil +} + +func getStringFromInterface(v interface{}) string { + switch val := v.(type) { + case string: + return val + case *string: + if val != nil { + return *val + } + } + return "" +} + +func getBigIntFromInterface(v interface{}) *big.Int { + switch val := v.(type) { + case *big.Int: + return val + case int64: + return big.NewInt(val) + case string: + n, _ := new(big.Int).SetString(val, 10) + return n + } + return big.NewInt(0) +} diff --git a/examples/go/servers/builder-codes/go.mod b/examples/go/servers/builder-codes/go.mod new file mode 100644 index 0000000000..9bdac02922 --- /dev/null +++ b/examples/go/servers/builder-codes/go.mod @@ -0,0 +1,66 @@ +module github.com/x402-foundation/x402/examples/go/servers/builder-codes + +go 1.25.4 + +replace github.com/x402-foundation/x402/go => ../../../../go + +require ( + github.com/gin-gonic/gin v1.12.0 + github.com/joho/godotenv v1.5.1 + github.com/x402-foundation/x402/go v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/go-ethereum v1.16.7 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect + golang.org/x/arch v0.22.0 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect +) diff --git a/examples/go/servers/builder-codes/go.sum b/examples/go/servers/builder-codes/go.sum new file mode 100644 index 0000000000..a4f2fe3dc8 --- /dev/null +++ b/examples/go/servers/builder-codes/go.sum @@ -0,0 +1,266 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= +github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= +github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= +github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= +github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= +golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/go/servers/builder-codes/main.go b/examples/go/servers/builder-codes/main.go new file mode 100644 index 0000000000..aadbc5c9cd --- /dev/null +++ b/examples/go/servers/builder-codes/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "fmt" + "net/http" + "os" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/joho/godotenv" + x402 "github.com/x402-foundation/x402/go" + "github.com/x402-foundation/x402/go/extensions/buildercode" + x402http "github.com/x402-foundation/x402/go/http" + ginmw "github.com/x402-foundation/x402/go/http/gin" + evm "github.com/x402-foundation/x402/go/mechanisms/evm/exact/server" +) + +const port = "4021" + +func main() { + _ = godotenv.Load() + + facURL := os.Getenv("FACILITATOR_URL") + payee := os.Getenv("EVM_ADDRESS_PAYEE") + appCode := os.Getenv("BUILDER_CODE_A") + if facURL == "" || payee == "" || appCode == "" { + fmt.Println("FACILITATOR_URL, EVM_ADDRESS_PAYEE, BUILDER_CODE_A required") + os.Exit(1) + } + + var services []string + if s := os.Getenv("BUILDER_CODE_S"); s != "" { + services = strings.Split(s, ",") + } + + bcData, err := buildercode.DeclareBuilderCodeExtension(appCode, services...) + if err != nil { + fmt.Printf("invalid builder codes: %v\n", err) + os.Exit(1) + } + + evmNet := x402.Network("eip155:84532") + facClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{URL: facURL}) + + paymentOption := x402http.PaymentOption{ + Scheme: "exact", + PayTo: payee, + Network: evmNet, + Price: map[string]interface{}{ + "amount": "1000", + "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + }, + } + + routeCfg := x402http.RouteConfig{ + Accepts: x402http.PaymentOptions{paymentOption}, + Extensions: map[string]interface{}{ + buildercode.Key: bcData, + }, + } + + routes := x402http.RoutesConfig{ + "GET /premium-data": routeCfg, + } + + r := gin.Default() + r.Use(ginmw.X402Payment(ginmw.Config{ + Routes: routes, + Facilitator: facClient, + Schemes: []ginmw.SchemeConfig{{Network: evmNet, Server: evm.NewExactEvmScheme()}}, + SyncFacilitatorOnStart: true, + Timeout: 30 * time.Second, + })) + + r.GET("/premium-data", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "data": "premium market data", + "timestamp": time.Now().Format(time.RFC3339), + }) + }) + + fmt.Printf("server on :%s (a=%s s=%v)\n", port, appCode, services) + _ = r.Run(":" + port) +} diff --git a/go/extensions/buildercode/buildercode_test.go b/go/extensions/buildercode/buildercode_test.go new file mode 100644 index 0000000000..0e37bfb382 --- /dev/null +++ b/go/extensions/buildercode/buildercode_test.go @@ -0,0 +1,137 @@ +package buildercode + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestValidateCode(t *testing.T) { + good := []string{"a", "bc_app", "abc123", "bc_my_facilitator_xx"} + for _, c := range good { + if err := validateCode(c); err != nil { + t.Errorf("expected %q valid, got %v", c, err) + } + } + bad := []string{"", "BC_APP", "bc-app", "bc app", "bc_" + string(make([]byte, 32))} + for _, c := range bad { + if err := validateCode(c); err == nil { + t.Errorf("expected %q invalid", c) + } + } +} + +func TestEncodeBuilderCodeSuffixLayout(t *testing.T) { + suffix, err := EncodeBuilderCodeSuffix(ExtensionData{A: "bc_app", W: "bc_fac"}) + if err != nil { + t.Fatal(err) + } + // Trailing 16 bytes must be the marker. + if !bytes.Equal(suffix[len(suffix)-16:], Marker) { + t.Errorf("trailing 16 bytes != marker; got %x", suffix[len(suffix)-16:]) + } + // Schema byte directly precedes marker. + if suffix[len(suffix)-17] != SchemaID { + t.Errorf("schema byte = 0x%x, want 0x%x", suffix[len(suffix)-17], SchemaID) + } + // Two big-endian length bytes precede schema; must equal cbor length. + declaredLen := int(suffix[len(suffix)-19])<<8 | int(suffix[len(suffix)-18]) + cborLen := len(suffix) - 19 + if declaredLen != cborLen { + t.Errorf("declared cbor length = %d, want %d", declaredLen, cborLen) + } +} + +func TestEncodeBuilderCodeSuffixFixture(t *testing.T) { + // Hand-computed CBOR for {"a":"bc_app","w":"bc_fac","s":["bc_morpho"]}: + // a3 map(3) + // 61 61 text(1) "a" + // 66 62 63 5f 61 70 70 text(6) "bc_app" + // 61 77 text(1) "w" + // 66 62 63 5f 66 61 63 text(6) "bc_fac" + // 61 73 text(1) "s" + // 81 69 62 63 5f 6d 6f 72 70 68 6f array(1) [text(9) "bc_morpho"] + wantCbor, _ := hex.DecodeString("a36161666263" + "5f617070" + "6177666263" + "5f666163" + "617381696263" + "5f6d6f7270686f") + suffix, err := EncodeBuilderCodeSuffix(ExtensionData{A: "bc_app", W: "bc_fac", S: []string{"bc_morpho"}}) + if err != nil { + t.Fatal(err) + } + gotCbor := suffix[:len(suffix)-19] + if !bytes.Equal(gotCbor, wantCbor) { + t.Errorf("cbor bytes\n got: %x\nwant: %x", gotCbor, wantCbor) + } +} + +func TestEncodeBuilderCodeSuffixSkipsEmpty(t *testing.T) { + // Only "w" set — should produce a single-entry map. + suffix, err := EncodeBuilderCodeSuffix(ExtensionData{W: "bc_fac"}) + if err != nil { + t.Fatal(err) + } + cbor := suffix[:len(suffix)-19] + // map(1) = 0xa1 + if cbor[0] != 0xa1 { + t.Errorf("cbor map header = 0x%x, want 0xa1", cbor[0]) + } +} + +func TestDeclareBuilderCodeExtension(t *testing.T) { + d, err := DeclareBuilderCodeExtension("bc_app", "bc_morpho", "bc_aerodrome") + if err != nil { + t.Fatal(err) + } + if d.A != "bc_app" || len(d.S) != 2 || d.S[0] != "bc_morpho" { + t.Errorf("unexpected ExtensionData: %+v", d) + } + if _, err := DeclareBuilderCodeExtension("BadCode"); err == nil { + t.Error("expected validation error for BadCode") + } + if _, err := DeclareBuilderCodeExtension("bc_app", "bad-code"); err == nil { + t.Error("expected validation error for bad service code") + } +} + +func TestFacilitatorBuildCalldataSuffix(t *testing.T) { + ext, err := NewFacilitatorExtension(FacilitatorConfig{BuilderCode: "bc_fac"}) + if err != nil { + t.Fatal(err) + } + + // Map shape (post-JSON-unmarshal). + suffixA, err := ext.BuildCalldataSuffix(map[string]interface{}{ + Key: map[string]interface{}{ + "a": "bc_app", + "s": []interface{}{"bc_morpho"}, + }, + }) + if err != nil { + t.Fatal(err) + } + + // Struct shape. + suffixB, err := ext.BuildCalldataSuffix(map[string]interface{}{ + Key: ExtensionData{A: "bc_app", S: []string{"bc_morpho"}}, + }) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(suffixA, suffixB) { + t.Errorf("map and struct shapes produced different suffixes:\n map: %x\n struct: %x", suffixA, suffixB) + } + + // Nil extensions should still produce a suffix containing only "w". + suffixC, err := ext.BuildCalldataSuffix(nil) + if err != nil { + t.Fatal(err) + } + if suffixC[0] != 0xa1 { + t.Errorf("nil-ext cbor map header = 0x%x, want 0xa1 (1-entry map)", suffixC[0]) + } +} + +func TestNewFacilitatorExtensionRejectsBadCode(t *testing.T) { + if _, err := NewFacilitatorExtension(FacilitatorConfig{BuilderCode: "BadCode"}); err == nil { + t.Error("expected validation error") + } +} diff --git a/go/extensions/buildercode/cbor.go b/go/extensions/buildercode/cbor.go new file mode 100644 index 0000000000..fa1a628675 --- /dev/null +++ b/go/extensions/buildercode/cbor.go @@ -0,0 +1,123 @@ +package buildercode + +import "fmt" + +// EncodeBuilderCodeSuffix builds a complete ERC-8021 Schema 2 calldata suffix. +// +// Layout: [cbor_data][suffix_data_length (2 bytes, big-endian)][schema_id (1 byte)][marker (16 bytes)] +// suffix_data_length covers cbor_data only. +func EncodeBuilderCodeSuffix(data ExtensionData) ([]byte, error) { + cborBytes, err := encodeCborMap(data) + if err != nil { + return nil, err + } + cborLen := len(cborBytes) + if cborLen > 0xffff { + return nil, fmt.Errorf("cbor payload too large: %d", cborLen) + } + + out := make([]byte, 0, cborLen+2+1+len(Marker)) + out = append(out, cborBytes...) + out = append(out, byte((cborLen>>8)&0xff), byte(cborLen&0xff)) + out = append(out, SchemaID) + out = append(out, Marker...) + return out, nil +} + +// encodeCborMap emits a minimal CBOR map (major type 5) with single-letter keys +// "a", "w", "s" in that order. Empty fields are skipped. +func encodeCborMap(data ExtensionData) ([]byte, error) { + type entry struct { + key string + // Exactly one of strVal / arrVal is set. + strVal string + arrVal []string + isArr bool + } + var entries []entry + if data.A != "" { + entries = append(entries, entry{key: "a", strVal: data.A}) + } + if data.W != "" { + entries = append(entries, entry{key: "w", strVal: data.W}) + } + if len(data.S) > 0 { + entries = append(entries, entry{key: "s", arrVal: data.S, isArr: true}) + } + + header, err := encodeCborMajorType(5, len(entries)) + if err != nil { + return nil, err + } + + out := append([]byte(nil), header...) + for _, e := range entries { + k, err := encodeCborString(e.key) + if err != nil { + return nil, err + } + out = append(out, k...) + + if e.isArr { + arr, err := encodeCborArray(e.arrVal) + if err != nil { + return nil, err + } + out = append(out, arr...) + } else { + v, err := encodeCborString(e.strVal) + if err != nil { + return nil, err + } + out = append(out, v...) + } + } + return out, nil +} + +// encodeCborString emits a CBOR text string (major type 3). +func encodeCborString(value string) ([]byte, error) { + header, err := encodeCborMajorType(3, len(value)) + if err != nil { + return nil, err + } + out := make([]byte, 0, len(header)+len(value)) + out = append(out, header...) + out = append(out, []byte(value)...) + return out, nil +} + +// encodeCborArray emits a CBOR array (major type 4) of text strings. +func encodeCborArray(values []string) ([]byte, error) { + header, err := encodeCborMajorType(4, len(values)) + if err != nil { + return nil, err + } + out := append([]byte(nil), header...) + for _, v := range values { + s, err := encodeCborString(v) + if err != nil { + return nil, err + } + out = append(out, s...) + } + return out, nil +} + +// encodeCborMajorType emits a CBOR initial byte plus optional length argument. +func encodeCborMajorType(majorType int, value int) ([]byte, error) { + if value < 0 { + return nil, fmt.Errorf("negative cbor length: %d", value) + } + mt := byte(majorType << 5) + switch { + case value <= 23: + return []byte{mt | byte(value)}, nil + case value <= 0xff: + return []byte{mt | 24, byte(value)}, nil + case value <= 0xffff: + return []byte{mt | 25, byte((value >> 8) & 0xff), byte(value & 0xff)}, nil + default: + return nil, fmt.Errorf("cbor value too large: %d", value) + } +} diff --git a/go/extensions/buildercode/facilitator.go b/go/extensions/buildercode/facilitator.go new file mode 100644 index 0000000000..21b96fa1a8 --- /dev/null +++ b/go/extensions/buildercode/facilitator.go @@ -0,0 +1,77 @@ +package buildercode + +// FacilitatorExtension is registered with x402Facilitator to enable builder-code +// attribution at settlement time. The EIP-3009 settlement mechanism looks it up +// via FacilitatorContext and calls BuildCalldataSuffix to obtain the suffix bytes +// to append to the transferWithAuthorization calldata. +type FacilitatorExtension struct { + config FacilitatorConfig +} + +// NewFacilitatorExtension validates cfg.BuilderCode and constructs a +// FacilitatorExtension ready to be registered with x402Facilitator. +func NewFacilitatorExtension(cfg FacilitatorConfig) (*FacilitatorExtension, error) { + if err := validateCode(cfg.BuilderCode); err != nil { + return nil, err + } + return &FacilitatorExtension{config: cfg}, nil +} + +// Key satisfies x402.FacilitatorExtension. +func (e *FacilitatorExtension) Key() string { + return Key +} + +// BuildCalldataSuffix reads the payment payload's builder-code extension data +// (fields "a" and "s"), sets "w" to the facilitator's configured code, and +// returns the encoded ERC-8021 Schema 2 suffix. +// +// Accepts payloadExtensions either as ExtensionData or as a map[string]interface{} +// (the shape produced by JSON unmarshaling into PaymentPayload.Extensions). +func (e *FacilitatorExtension) BuildCalldataSuffix(payloadExtensions map[string]interface{}) ([]byte, error) { + data := ExtensionData{W: e.config.BuilderCode} + + if payloadExtensions != nil { + if raw, ok := payloadExtensions[Key]; ok { + extractInto(raw, &data) + } + } + + return EncodeBuilderCodeSuffix(data) +} + +// extractInto copies "a" and "s" from raw into data. raw may be ExtensionData, +// *ExtensionData, or a map[string]interface{}. +func extractInto(raw interface{}, data *ExtensionData) { + switch v := raw.(type) { + case ExtensionData: + data.A = v.A + if len(v.S) > 0 { + data.S = append([]string(nil), v.S...) + } + case *ExtensionData: + if v != nil { + data.A = v.A + if len(v.S) > 0 { + data.S = append([]string(nil), v.S...) + } + } + case map[string]interface{}: + if a, ok := v["a"].(string); ok { + data.A = a + } + if s, ok := v["s"].([]interface{}); ok { + out := make([]string, 0, len(s)) + for _, item := range s { + if str, ok := item.(string); ok { + out = append(out, str) + } + } + if len(out) > 0 { + data.S = out + } + } else if s, ok := v["s"].([]string); ok && len(s) > 0 { + data.S = append([]string(nil), s...) + } + } +} diff --git a/go/extensions/buildercode/resource_server.go b/go/extensions/buildercode/resource_server.go new file mode 100644 index 0000000000..a60341525f --- /dev/null +++ b/go/extensions/buildercode/resource_server.go @@ -0,0 +1,39 @@ +package buildercode + +// DeclareBuilderCodeExtension validates the supplied codes and returns an +// ExtensionData value suitable for inclusion in PaymentRequired.Extensions +// under the "builder-code" key. +// +// appCode is written to the "a" field. Optional serviceCodes populate the "s" +// array — use these to attribute related on-chain protocols the app depends on +// (e.g., "bc_morpho", "bc_aerodrome"). +func DeclareBuilderCodeExtension(appCode string, serviceCodes ...string) (ExtensionData, error) { + if err := validateCode(appCode); err != nil { + return ExtensionData{}, err + } + for _, c := range serviceCodes { + if err := validateCode(c); err != nil { + return ExtensionData{}, err + } + } + data := ExtensionData{A: appCode} + if len(serviceCodes) > 0 { + data.S = append([]string(nil), serviceCodes...) + } + return data, nil +} + +// resourceServerExtension is a no-op ResourceServerExtension that simply +// advertises support for the "builder-code" key. It satisfies +// types.ResourceServerExtension (Key + EnrichDeclaration). +type resourceServerExtension struct{} + +func (e *resourceServerExtension) Key() string { return Key } + +func (e *resourceServerExtension) EnrichDeclaration(declaration interface{}, _ interface{}) interface{} { + return declaration +} + +// ResourceServerExtension declares builder-code support on a resource server. +// Register via httpServer.RegisterExtension(buildercode.ResourceServerExtension). +var ResourceServerExtension = &resourceServerExtension{} diff --git a/go/extensions/buildercode/types.go b/go/extensions/buildercode/types.go new file mode 100644 index 0000000000..e045858cc2 --- /dev/null +++ b/go/extensions/buildercode/types.go @@ -0,0 +1,65 @@ +// Package buildercode implements the ERC-8021 Schema 2 Builder Code attribution +// extension for x402 payments. +// +// Two parties contribute their builder codes, which are CBOR-encoded and appended +// as a suffix to the EIP-3009 settlement transaction calldata: +// - Service (resource server): declares its app code in the 402 response +// extensions under field "a", and optionally related on-chain services as "s". +// - Facilitator: at settlement, reads the payload extensions, adds its own code +// as field "w", and the EIP-3009 mechanism appends the encoded suffix to the +// transferWithAuthorization calldata. +package buildercode + +import ( + "fmt" + "regexp" + + x402 "github.com/x402-foundation/x402/go" +) + +// Key is the extension identifier used in PaymentRequired/PaymentPayload extensions +// and the FacilitatorContext. +const Key = "builder-code" + +// SchemaID is the ERC-8021 Schema 2 identifier byte. +const SchemaID byte = 0x02 + +// Marker is the 16-byte ERC-8021 marker appended at the end of every suffix. +var Marker = []byte{ + 0x80, 0x21, 0x80, 0x21, 0x80, 0x21, 0x80, 0x21, + 0x80, 0x21, 0x80, 0x21, 0x80, 0x21, 0x80, 0x21, +} + +// builderCodePattern restricts builder codes to 1-32 lowercase alphanumeric +// characters or underscores. +var builderCodePattern = regexp.MustCompile(`^[a-z0-9_]{1,32}$`) + +// BuilderCode is the FacilitatorExtension identifier for registration with x402Facilitator. +var BuilderCode = x402.NewFacilitatorExtension(Key) + +// ExtensionData is the builder-code payload as it appears in +// PaymentRequired.Extensions and PaymentPayload.Extensions. +// +// Maps to ERC-8021 Schema 2 fields: +// - A: app code (the x402 service that exposed the endpoint) +// - W: wallet code (the facilitator that settled the payment on-chain) +// - S: service codes (related on-chain services the app depends on) +type ExtensionData struct { + A string `json:"a,omitempty"` + W string `json:"w,omitempty"` + S []string `json:"s,omitempty"` +} + +// FacilitatorConfig configures the facilitator-side builder-code extension. +type FacilitatorConfig struct { + // BuilderCode is the facilitator's own code, written to the "w" field at settlement. + BuilderCode string +} + +// validateCode returns an error if code does not match the builder-code pattern. +func validateCode(code string) error { + if !builderCodePattern.MatchString(code) { + return fmt.Errorf("invalid builder code %q: must be 1-32 characters, lowercase alphanumeric and underscores only", code) + } + return nil +} diff --git a/go/mechanisms/evm/exact/facilitator/eip3009.go b/go/mechanisms/evm/exact/facilitator/eip3009.go index 1195b1abd6..0946314a31 100644 --- a/go/mechanisms/evm/exact/facilitator/eip3009.go +++ b/go/mechanisms/evm/exact/facilitator/eip3009.go @@ -9,6 +9,7 @@ import ( "time" x402 "github.com/x402-foundation/x402/go" + "github.com/x402-foundation/x402/go/extensions/buildercode" "github.com/x402-foundation/x402/go/mechanisms/evm" "github.com/x402-foundation/x402/go/types" ) @@ -135,11 +136,31 @@ func (f *ExactEvmScheme) verifyEIP3009( }, nil } +// buildBuilderCodeSuffix returns the ERC-8021 calldata suffix to append to the +// transferWithAuthorization call when a builder-code facilitator extension is +// registered. Returns nil when the extension is absent or suffix construction +// fails (a logging hook would go here in production). +func buildBuilderCodeSuffix(fctx *x402.FacilitatorContext, payloadExtensions map[string]interface{}) []byte { + if fctx == nil { + return nil + } + ext, ok := fctx.GetExtension(buildercode.Key).(*buildercode.FacilitatorExtension) + if !ok || ext == nil { + return nil + } + suffix, err := ext.BuildCalldataSuffix(payloadExtensions) + if err != nil { + return nil + } + return suffix +} + // settleEIP3009 settles an EIP-3009 payment on-chain. func (f *ExactEvmScheme) settleEIP3009( ctx context.Context, payload types.PaymentPayload, requirements types.PaymentRequirements, + fctx *x402.FacilitatorContext, ) (*x402.SettleResponse, error) { network := x402.Network(payload.Accepted.Network) @@ -191,7 +212,9 @@ func (f *ExactEvmScheme) settleEIP3009( return nil, x402.NewSettleError(ErrInvalidPayload, verifyResp.Payer, network, "", err.Error()) } - txHash, err := ExecuteTransferWithAuthorization(ctx, f.signer, tokenAddress, parsedAuthorization, sigData) + suffix := buildBuilderCodeSuffix(fctx, payload.Extensions) + + txHash, err := ExecuteTransferWithAuthorizationWithSuffix(ctx, f.signer, tokenAddress, parsedAuthorization, sigData, suffix) if err != nil { return nil, x402.NewSettleError(parseEIP3009TransferError(err), verifyResp.Payer, network, "", err.Error()) } diff --git a/go/mechanisms/evm/exact/facilitator/eip3009_helpers.go b/go/mechanisms/evm/exact/facilitator/eip3009_helpers.go index 3528703ce0..f2b076ed93 100644 --- a/go/mechanisms/evm/exact/facilitator/eip3009_helpers.go +++ b/go/mechanisms/evm/exact/facilitator/eip3009_helpers.go @@ -345,6 +345,56 @@ func ExecuteTransferWithAuthorization( ) } +// ExecuteTransferWithAuthorizationWithSuffix is identical to +// ExecuteTransferWithAuthorization but appends an arbitrary byte suffix to the +// transferWithAuthorization calldata. Used by ERC-8021 builder-code attribution. +// +// When suffix is empty, this is equivalent to ExecuteTransferWithAuthorization. +func ExecuteTransferWithAuthorizationWithSuffix( + ctx context.Context, + signer evm.FacilitatorEvmSigner, + tokenAddress string, + parsed *ParsedEIP3009Authorization, + sigData *evm.ERC6492SignatureData, + suffix []byte, +) (string, error) { + if len(suffix) == 0 { + return ExecuteTransferWithAuthorization(ctx, signer, tokenAddress, parsed, sigData) + } + if sigData == nil { + return "", fmt.Errorf("missing signature data") + } + + var ( + calldata []byte + err error + ) + if len(sigData.InnerSignature) == 65 { + v, r, s := splitSignatureParts(sigData.InnerSignature) + calldata, err = packCallData( + evm.TransferWithAuthorizationVRSABI, + evm.FunctionTransferWithAuthorization, + parsed.From, + parsed.To, + parsed.Value, + parsed.ValidAfter, + parsed.ValidBefore, + parsed.Nonce, + v, + r, + s, + ) + } else { + calldata, err = buildTransferWithAuthorizationBytesCalldata(parsed, sigData.InnerSignature) + } + if err != nil { + return "", err + } + + calldata = append(calldata, suffix...) + return signer.SendTransaction(ctx, tokenAddress, calldata) +} + // DeploySmartWallet sends the ERC-6492 factory deployment transaction when enabled. func DeploySmartWallet( ctx context.Context, diff --git a/go/mechanisms/evm/exact/facilitator/scheme.go b/go/mechanisms/evm/exact/facilitator/scheme.go index f0c9fec723..d48a95afdd 100644 --- a/go/mechanisms/evm/exact/facilitator/scheme.go +++ b/go/mechanisms/evm/exact/facilitator/scheme.go @@ -108,5 +108,5 @@ func (f *ExactEvmScheme) Settle( }) } - return f.settleEIP3009(ctx, payload, requirements) + return f.settleEIP3009(ctx, payload, requirements, fctx) } diff --git a/go/mechanisms/evm/exact/v1/facilitator/scheme.go b/go/mechanisms/evm/exact/v1/facilitator/scheme.go index d10c70fa4c..06703f5844 100644 --- a/go/mechanisms/evm/exact/v1/facilitator/scheme.go +++ b/go/mechanisms/evm/exact/v1/facilitator/scheme.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" x402 "github.com/x402-foundation/x402/go" + "github.com/x402-foundation/x402/go/extensions/buildercode" "github.com/x402-foundation/x402/go/mechanisms/evm" exactfacilitator "github.com/x402-foundation/x402/go/mechanisms/evm/exact/facilitator" evmv1 "github.com/x402-foundation/x402/go/mechanisms/evm/v1" @@ -305,7 +306,18 @@ func (f *ExactEvmSchemeV1) Settle( return nil, x402.NewSettleError(ErrInvalidPayload, verifyResp.Payer, network, "", err.Error()) } - txHash, err := exactfacilitator.ExecuteTransferWithAuthorization(ctx, f.signer, tokenAddress, parsedAuthorization, sigData) + // V1 payloads carry no Extensions field, so the facilitator can only contribute + // its own "w" code; "a"/"s" remain empty. + var suffix []byte + if fctx != nil { + if ext, ok := fctx.GetExtension(buildercode.Key).(*buildercode.FacilitatorExtension); ok && ext != nil { + if s, err := ext.BuildCalldataSuffix(nil); err == nil { + suffix = s + } + } + } + + txHash, err := exactfacilitator.ExecuteTransferWithAuthorizationWithSuffix(ctx, f.signer, tokenAddress, parsedAuthorization, sigData, suffix) if err != nil { return nil, x402.NewSettleError(ErrTransactionFailed, verifyResp.Payer, network, "", err.Error()) } diff --git a/typescript/packages/extensions/package.json b/typescript/packages/extensions/package.json index 634def85a1..b7c3847128 100644 --- a/typescript/packages/extensions/package.json +++ b/typescript/packages/extensions/package.json @@ -103,6 +103,16 @@ "types": "./dist/cjs/payment-identifier/index.d.ts", "default": "./dist/cjs/payment-identifier/index.js" } + }, + "./builder-code": { + "import": { + "types": "./dist/esm/builder-code/index.d.mts", + "default": "./dist/esm/builder-code/index.mjs" + }, + "require": { + "types": "./dist/cjs/builder-code/index.d.ts", + "default": "./dist/cjs/builder-code/index.js" + } } }, "files": [ diff --git a/typescript/packages/extensions/src/builder-code/cbor.ts b/typescript/packages/extensions/src/builder-code/cbor.ts new file mode 100644 index 0000000000..d45ba272e1 --- /dev/null +++ b/typescript/packages/extensions/src/builder-code/cbor.ts @@ -0,0 +1,176 @@ +/** + * ERC-8021 Schema 2 CBOR encoding for builder code suffixes. + * + * Schema 2 suffix format: + * [cbor_data (variable)] [suffix_data_length (2 bytes)] [schema_id = 0x02 (1 byte)] [ERC-8021 marker (16 bytes)] + * + * CBOR payload uses single-letter keys: + * "a" — app builder code (string) + * "w" — wallet/facilitator builder code (string) + * "s" — service codes (string array) + */ + +import { type Hex } from "viem"; +import { ERC_8021_MARKER, SCHEMA_2_ID, type BuilderCodeExtensionData } from "./types"; + +/** + * Encodes a CBOR map from builder code extension data. + * + * Produces a minimal CBOR map with: + * - "a" key (major type 3, length 1) → string value (app/service code) + * - "w" key (major type 3, length 1) → string value (facilitator code) + * - "s" key (major type 3, length 1) → array of strings (related services) + * + * Uses hand-rolled CBOR to avoid adding a dependency. + */ +function encodeCborMap(data: BuilderCodeExtensionData): Uint8Array { + const entries: Uint8Array[] = []; + let mapSize = 0; + + if (data.a) { + mapSize++; + entries.push(encodeCborString("a")); + entries.push(encodeCborString(data.a)); + } + + if (data.w) { + mapSize++; + entries.push(encodeCborString("w")); + entries.push(encodeCborString(data.w)); + } + + if (data.s && data.s.length > 0) { + mapSize++; + entries.push(encodeCborString("s")); + entries.push(encodeCborArray(data.s)); + } + + // CBOR map header + const header = encodeCborMajorType(5, mapSize); // major type 5 = map + + const totalLength = header.length + entries.reduce((sum, e) => sum + e.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + + result.set(header, offset); + offset += header.length; + + for (const entry of entries) { + result.set(entry, offset); + offset += entry.length; + } + + return result; +} + +/** + * Encodes a CBOR text string (major type 3). + */ +function encodeCborString(value: string): Uint8Array { + const encoded = new TextEncoder().encode(value); + const header = encodeCborMajorType(3, encoded.length); // major type 3 = text string + const result = new Uint8Array(header.length + encoded.length); + result.set(header, 0); + result.set(encoded, header.length); + return result; +} + +/** + * Encodes a CBOR array of strings (major type 4). + */ +function encodeCborArray(values: string[]): Uint8Array { + const header = encodeCborMajorType(4, values.length); // major type 4 = array + const encodedValues = values.map(encodeCborString); + + const totalLength = header.length + encodedValues.reduce((sum, e) => sum + e.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + + result.set(header, offset); + offset += header.length; + + for (const encoded of encodedValues) { + result.set(encoded, offset); + offset += encoded.length; + } + + return result; +} + +/** + * Encodes a CBOR major type with an argument value. + * + * CBOR encoding rules: + * - 0-23: single byte (major type << 5 | value) + * - 24-255: two bytes (major type << 5 | 24, value) + * - 256-65535: three bytes (major type << 5 | 25, value high, value low) + */ +function encodeCborMajorType(majorType: number, value: number): Uint8Array { + const mt = majorType << 5; + + if (value <= 23) { + return new Uint8Array([mt | value]); + } + if (value <= 0xff) { + return new Uint8Array([mt | 24, value]); + } + if (value <= 0xffff) { + return new Uint8Array([mt | 25, (value >> 8) & 0xff, value & 0xff]); + } + + throw new Error(`CBOR value too large: ${value}`); +} + +/** + * Builds a complete ERC-8021 Schema 2 data suffix from builder code data. + * + * Format: [cbor_data][suffix_data_length (2 bytes)][schema_id (1 byte)][marker (16 bytes)] + * + * The suffix_data_length covers the cbor_data only (not itself, schema_id, or marker). + * + * @param data - Builder code extension data with "a" and/or "s" fields + * @returns Hex-encoded suffix bytes (without 0x prefix) ready to append to calldata + */ +export function encodeBuilderCodeSuffix(data: BuilderCodeExtensionData): Hex { + const cborBytes = encodeCborMap(data); + const cborLength = cborBytes.length; + + // suffix_data_length is 2 bytes, big-endian + const lengthHigh = (cborLength >> 8) & 0xff; + const lengthLow = cborLength & 0xff; + + // Build the full suffix: [cbor][length 2B][schema_id 1B][marker 16B] + const suffixBytes = new Uint8Array(cborLength + 2 + 1 + 16); + let offset = 0; + + // CBOR data + suffixBytes.set(cborBytes, offset); + offset += cborLength; + + // Suffix data length (2 bytes, big-endian) + suffixBytes[offset++] = lengthHigh; + suffixBytes[offset++] = lengthLow; + + // Schema ID + suffixBytes[offset++] = SCHEMA_2_ID; + + // ERC-8021 marker (16 bytes) + const markerBytes = hexToBytes(ERC_8021_MARKER); + suffixBytes.set(markerBytes, offset); + + return `0x${bytesToHex(suffixBytes)}`; +} + +function hexToBytes(hex: string): Uint8Array { + const bytes = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16); + } + return bytes; +} + +function bytesToHex(bytes: Uint8Array): string { + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +} diff --git a/typescript/packages/extensions/src/builder-code/facilitator.ts b/typescript/packages/extensions/src/builder-code/facilitator.ts new file mode 100644 index 0000000000..79b8300b7f --- /dev/null +++ b/typescript/packages/extensions/src/builder-code/facilitator.ts @@ -0,0 +1,75 @@ +/** + * Facilitator-side extension for the Builder Code Extension. + * + * At settlement time, the facilitator: + * 1. Reads builder code data from the payment payload extensions + * 2. Adds its own builder code as the "w" (wallet) field + * 3. Encodes the combined data as an ERC-8021 Schema 2 CBOR suffix + * 4. The suffix is appended to the settlement transaction calldata + */ + +import type { FacilitatorExtension } from "@x402/core/types"; +import type { Hex } from "viem"; +import { encodeBuilderCodeSuffix } from "./cbor"; +import { + BUILDER_CODE, + BUILDER_CODE_PATTERN, + type BuilderCodeExtensionData, + type BuilderCodeFacilitatorConfig, +} from "./types"; + +/** + * Facilitator extension that manages builder code attribution at settlement time. + * + * Register this with the x402Facilitator to enable builder code support. + * The extension reads builder code data from payment payloads and provides + * the encoded ERC-8021 suffix for the settlement mechanism to append. + * + * @example + * ```typescript + * import { BuilderCodeFacilitatorExtension } from '@x402/extensions/builder-code'; + * + * const facilitator = new x402Facilitator(); + * facilitator.registerExtension(new BuilderCodeFacilitatorExtension({ + * builderCode: "bc_my_facilitator", + * })); + * ``` + */ +export class BuilderCodeFacilitatorExtension implements FacilitatorExtension { + readonly key = BUILDER_CODE; + private readonly config: BuilderCodeFacilitatorConfig; + + constructor(config: BuilderCodeFacilitatorConfig) { + if (!BUILDER_CODE_PATTERN.test(config.builderCode)) { + throw new Error( + `Invalid builder code: "${config.builderCode}". ` + + `Must be 1-32 characters, lowercase alphanumeric and underscores only.`, + ); + } + this.config = config; + } + + /** + * Builds the ERC-8021 Schema 2 calldata suffix from payment payload extensions. + * + * Reads "a" (app/service code) and "s" (related service codes) from the + * payment payload, adds the facilitator's own code as "w" (wallet), and + * encodes as Schema 2 CBOR. + * + * @param payloadExtensions - The extensions from the payment payload + * @returns Hex-encoded suffix to append to settlement calldata, or undefined if no builder codes + */ + buildCalldataSuffix(payloadExtensions?: Record): Hex | undefined { + const extData = payloadExtensions?.[BUILDER_CODE] as + | BuilderCodeExtensionData + | undefined; + + const suffixData: BuilderCodeExtensionData = { + a: extData?.a, + w: this.config.builderCode, + s: extData?.s ? [...extData.s] : undefined, + }; + + return encodeBuilderCodeSuffix(suffixData); + } +} diff --git a/typescript/packages/extensions/src/builder-code/index.ts b/typescript/packages/extensions/src/builder-code/index.ts new file mode 100644 index 0000000000..b10f0ecea6 --- /dev/null +++ b/typescript/packages/extensions/src/builder-code/index.ts @@ -0,0 +1,67 @@ +/** + * Builder Code Extension for x402 (ERC-8021) + * + * Enables attribution tracking for x402 payments by appending ERC-8021 + * Schema 2 builder codes to settlement transaction calldata. + * + * Two parties attach their builder code: + * - Service (server): Declares as "a" (app) in 402 response via declareBuilderCodeExtension() + * - Facilitator: Adds as "w" (wallet) at settlement via BuilderCodeFacilitatorExtension + * + * The service can optionally include related on-chain services in the "s" array + * (e.g., Morpho, Aerodrome) to attribute protocols it depends on. + * + * ## Usage + * + * ### For Services (Resource Servers) + * + * ```typescript + * import { declareBuilderCodeExtension, BUILDER_CODE } from '@x402/extensions/builder-code'; + * + * // In paywall config extensions + * extensions: { + * [BUILDER_CODE]: declareBuilderCodeExtension("bc_my_service"), + * } + * + * // With related on-chain services + * extensions: { + * [BUILDER_CODE]: declareBuilderCodeExtension("bc_my_service", ["bc_morpho", "bc_aerodrome"]), + * } + * ``` + * + * ### For Facilitators + * + * ```typescript + * import { BuilderCodeFacilitatorExtension } from '@x402/extensions/builder-code'; + * + * const facilitator = new x402Facilitator(); + * facilitator.registerExtension(new BuilderCodeFacilitatorExtension({ + * builderCode: "bc_my_facilitator", + * })); + * ``` + */ + +// Types +export type { + BuilderCodeExtensionData, + BuilderCodeFacilitatorConfig, +} from "./types"; + +export { + BUILDER_CODE, + BUILDER_CODE_PATTERN, + ERC_8021_MARKER, + SCHEMA_2_ID, +} from "./types"; + +// CBOR encoding +export { encodeBuilderCodeSuffix } from "./cbor"; + +// Resource Server +export { + declareBuilderCodeExtension, + builderCodeResourceServerExtension, +} from "./resourceServer"; + +// Facilitator +export { BuilderCodeFacilitatorExtension } from "./facilitator"; diff --git a/typescript/packages/extensions/src/builder-code/resourceServer.ts b/typescript/packages/extensions/src/builder-code/resourceServer.ts new file mode 100644 index 0000000000..891ff97f43 --- /dev/null +++ b/typescript/packages/extensions/src/builder-code/resourceServer.ts @@ -0,0 +1,85 @@ +/** + * Resource Server utilities for the Builder Code Extension. + * + * Services use this to declare their builder code in the 402 response. + * The service's code goes in the "a" (app) field since the service is + * the application exposing the x402 endpoint. Optionally, the service + * can include related on-chain services in the "s" array. + */ + +import type { ResourceServerExtension } from "@x402/core/types"; +import { + BUILDER_CODE, + BUILDER_CODE_PATTERN, + type BuilderCodeExtensionData, +} from "./types"; + +/** + * Declares the builder-code extension for inclusion in PaymentRequired.extensions. + * + * The service's builder code is placed in the "a" (app) field — the service + * is the application that exposed the x402 endpoint. Related on-chain services + * the app depends on (e.g., Morpho, Aerodrome) can be listed in the "s" array. + * + * @param appCode - The service's builder code (e.g., "bc_weather_svc") + * @param serviceCodes - Optional array of related service builder codes + * @returns A BuilderCodeExtensionData object for PaymentRequired.extensions + * + * @example + * ```typescript + * import { declareBuilderCodeExtension, BUILDER_CODE } from '@x402/extensions/builder-code'; + * + * // Simple: just the service's own code + * extensions: { + * [BUILDER_CODE]: declareBuilderCodeExtension("bc_weather_svc"), + * } + * + * // With related services + * extensions: { + * [BUILDER_CODE]: declareBuilderCodeExtension("bc_lending_app", ["bc_morpho", "bc_aerodrome"]), + * } + * ``` + */ +export function declareBuilderCodeExtension( + appCode: string, + serviceCodes?: string[], +): BuilderCodeExtensionData { + if (!BUILDER_CODE_PATTERN.test(appCode)) { + throw new Error( + `Invalid builder code: "${appCode}". ` + + `Must be 1-32 characters, lowercase alphanumeric and underscores only.`, + ); + } + + if (serviceCodes) { + for (const code of serviceCodes) { + if (!BUILDER_CODE_PATTERN.test(code)) { + throw new Error( + `Invalid service builder code: "${code}". ` + + `Must be 1-32 characters, lowercase alphanumeric and underscores only.`, + ); + } + } + } + + const data: BuilderCodeExtensionData = { + a: appCode, + }; + + if (serviceCodes && serviceCodes.length > 0) { + data.s = serviceCodes; + } + + return data; +} + +/** + * ResourceServerExtension implementation for builder-code. + * + * Register this with the resource server to advertise builder code support. + * The actual builder code value is set via declareBuilderCodeExtension() + * in the paywall configuration. + */ +export const builderCodeResourceServerExtension: ResourceServerExtension = { + key: BUILDER_CODE, +}; diff --git a/typescript/packages/extensions/src/builder-code/types.ts b/typescript/packages/extensions/src/builder-code/types.ts new file mode 100644 index 0000000000..457c315e0f --- /dev/null +++ b/typescript/packages/extensions/src/builder-code/types.ts @@ -0,0 +1,68 @@ +/** + * Type definitions for the Builder Code Extension (ERC-8021) + * + * Enables attribution tracking for x402 payments by appending + * ERC-8021 Schema 2 builder codes to settlement transaction calldata. + */ + +/** + * Extension identifier constant + */ +export const BUILDER_CODE = "builder-code"; + +/** + * ERC-8021 marker bytes (16 bytes) appended at the end of every suffix + */ +export const ERC_8021_MARKER = "80218021802180218021802180218021"; + +/** + * Schema 2 identifier byte + */ +export const SCHEMA_2_ID = 0x02; + +/** + * Pattern for valid builder codes (lowercase alphanumeric + underscore, 1-32 chars) + */ +export const BUILDER_CODE_PATTERN = /^[a-z0-9_]{1,32}$/; + +/** + * Builder code extension data as it appears in PaymentRequired/PaymentPayload extensions. + * + * Maps to ERC-8021 Schema 2 fields: + * - a: app code (the x402 service that exposed the endpoint) + * - w: wallet code (the facilitator that settled the payment on-chain) + * - s: service codes array (related on-chain services the app depends on) + */ +export interface BuilderCodeExtensionData { + /** + * App builder code — the x402 service that exposed the paid endpoint. + * Maps to the "a" field in ERC-8021 Schema 2. + * Set by the service in the 402 response. + */ + a?: string; + + /** + * Wallet builder code — the facilitator that settled the payment on-chain. + * Maps to the "w" field in ERC-8021 Schema 2. + * Set by the facilitator at settlement time. + */ + w?: string; + + /** + * Service builder codes — related on-chain services the app depends on. + * Maps to the "s" field in ERC-8021 Schema 2. + * Optionally set by the service to attribute protocols it interacts with + * (e.g., Morpho for lending, Aerodrome for swaps). + */ + s?: string[]; +} + +/** + * Configuration for the builder code facilitator extension. + */ +export interface BuilderCodeFacilitatorConfig { + /** + * The facilitator's own builder code, set as the "w" field at settlement. + */ + builderCode: string; +} diff --git a/typescript/packages/extensions/src/index.ts b/typescript/packages/extensions/src/index.ts index a8e18bfd60..1663543ff8 100644 --- a/typescript/packages/extensions/src/index.ts +++ b/typescript/packages/extensions/src/index.ts @@ -20,3 +20,6 @@ export * from "./eip2612-gas-sponsoring"; // ERC-20 Approval Gas Sponsoring extension export * from "./erc20-approval-gas-sponsoring"; + +// Builder Code extension (ERC-8021) +export * from "./builder-code"; diff --git a/typescript/packages/extensions/tsup.config.ts b/typescript/packages/extensions/tsup.config.ts index bd85a73925..fe76c754d1 100644 --- a/typescript/packages/extensions/tsup.config.ts +++ b/typescript/packages/extensions/tsup.config.ts @@ -7,6 +7,7 @@ const baseConfig = { "sign-in-with-x/index": "src/sign-in-with-x/index.ts", "offer-receipt/index": "src/offer-receipt/index.ts", "payment-identifier/index": "src/payment-identifier/index.ts", + "builder-code/index": "src/builder-code/index.ts", }, dts: { resolve: true, diff --git a/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009-utils.ts b/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009-utils.ts index 73a7506845..cc4b70527a 100644 --- a/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009-utils.ts +++ b/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009-utils.ts @@ -218,9 +218,15 @@ export function parseEip3009TransferError(error: unknown): string { /** * Executes transferWithAuthorization onchain. * + * When a calldataSuffix is provided (e.g., ERC-8021 builder code attribution), + * the function manually encodes the calldata, appends the suffix, and uses + * sendTransaction instead of writeContract. The EVM ignores trailing calldata + * bytes, so the transfer executes normally while indexers can read the suffix. + * * @param signer - EVM signer for contract writes * @param erc20Address - ERC-20 token contract address * @param payload - EIP-3009 transfer authorization payload + * @param calldataSuffix - Optional hex bytes to append after the ABI-encoded calldata * * @returns Transaction hash */ @@ -228,6 +234,7 @@ export async function executeTransferWithAuthorization( signer: FacilitatorEvmSigner, erc20Address: `0x${string}`, payload: ExactEIP3009Payload, + calldataSuffix?: Hex, ): Promise { const { signature } = parseErc6492Signature(payload.signature!); const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length; @@ -243,10 +250,36 @@ export async function executeTransferWithAuthorization( auth.nonce, ] as const; - if (isECDSA) { - const parsedSig = parseSignature(signature); + // If no suffix, use the standard writeContract path (unchanged behavior) + if (!calldataSuffix) { + if (isECDSA) { + const parsedSig = parseSignature(signature); + return signer.writeContract({ + address: erc20Address, + abi: eip3009ABI, + functionName: "transferWithAuthorization", + args: [ + ...baseArgs, + (parsedSig.v as number | undefined) || parsedSig.yParity, + parsedSig.r, + parsedSig.s, + ], + }); + } + return signer.writeContract({ address: erc20Address, + abi: eip3009ABI, + functionName: "transferWithAuthorization", + args: [...baseArgs, signature], + }); + } + + // With suffix: encode calldata manually, append suffix, use sendTransaction + let calldata: Hex; + if (isECDSA) { + const parsedSig = parseSignature(signature); + calldata = encodeFunctionData({ abi: eip3009ABI, functionName: "transferWithAuthorization", args: [ @@ -256,12 +289,20 @@ export async function executeTransferWithAuthorization( parsedSig.s, ], }); + } else { + calldata = encodeFunctionData({ + abi: eip3009ABI, + functionName: "transferWithAuthorization", + args: [...baseArgs, signature], + }); } - return signer.writeContract({ - address: erc20Address, - abi: eip3009ABI, - functionName: "transferWithAuthorization", - args: [...baseArgs, signature], + // Append the suffix (strip 0x prefix from suffix before concatenating) + const suffixHex = calldataSuffix.startsWith("0x") ? calldataSuffix.slice(2) : calldataSuffix; + const calldataWithSuffix = `${calldata}${suffixHex}` as Hex; + + return signer.sendTransaction({ + to: erc20Address, + data: calldataWithSuffix, }); } diff --git a/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009.ts b/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009.ts index 5dab3b5526..15e766a6c9 100644 --- a/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009.ts +++ b/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009.ts @@ -1,6 +1,7 @@ import { PaymentPayload, PaymentRequirements, + FacilitatorContext, SettleResponse, VerifyResponse, } from "@x402/core/types"; @@ -236,11 +237,16 @@ export async function verifyEIP3009( /** * Settles an EIP-3009 payment by executing transferWithAuthorization. * + * If a BuilderCodeFacilitatorExtension is registered, the facilitator will + * append an ERC-8021 Schema 2 suffix to the settlement transaction calldata + * containing builder codes from the agent, service, and facilitator. + * * @param signer - The facilitator signer for contract writes * @param payload - The payment payload to settle * @param requirements - The payment requirements * @param eip3009Payload - The EIP-3009 specific payload * @param config - Facilitator configuration + * @param context - Optional facilitator context for extension capabilities * @returns Promise resolving to settlement response */ export async function settleEIP3009( @@ -249,6 +255,7 @@ export async function settleEIP3009( requirements: PaymentRequirements, eip3009Payload: ExactEIP3009Payload, config: EIP3009FacilitatorConfig, + context?: FacilitatorContext, ): Promise { const payer = eip3009Payload.authorization.from; @@ -294,10 +301,22 @@ export async function settleEIP3009( } } + // Build ERC-8021 calldata suffix if builder code extension is registered + let calldataSuffix: Hex | undefined; + if (context) { + const builderCodeExt = context.getExtension("builder-code"); + if (builderCodeExt && "buildCalldataSuffix" in builderCodeExt) { + calldataSuffix = (builderCodeExt as { buildCalldataSuffix: (ext?: Record) => Hex | undefined }).buildCalldataSuffix( + payload.extensions as Record | undefined, + ); + } + } + const tx = await executeTransferWithAuthorization( signer, getAddress(requirements.asset), eip3009Payload, + calldataSuffix, ); // Wait for transaction confirmation diff --git a/typescript/packages/mechanisms/evm/src/exact/facilitator/scheme.ts b/typescript/packages/mechanisms/evm/src/exact/facilitator/scheme.ts index d5bcd5afc6..8904333beb 100644 --- a/typescript/packages/mechanisms/evm/src/exact/facilitator/scheme.ts +++ b/typescript/packages/mechanisms/evm/src/exact/facilitator/scheme.ts @@ -121,6 +121,6 @@ export class ExactEvmScheme implements SchemeNetworkFacilitator { } const eip3009Payload: ExactEIP3009Payload = rawPayload; - return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config); + return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config, context); } }