diff --git a/bun.lock b/bun.lock index 5bdf1d59..37da64f6 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,11 @@ "@hono/zod-validator": "^0.7.5", "@pinax/graph-networks-registry": "^0.7.1", "@web3icons/core": "^4.0.18", + "@x402/core": "^2.11.0", + "@x402/evm": "^2.11.0", + "@x402/extensions": "^2.11.0", + "@x402/hono": "^2.11.0", + "@x402/svm": "^2.11.0", "commander": "^14.0.0", "dotenv": "^17.2.1", "hono": "^4.8.12", @@ -26,6 +31,8 @@ }, }, "packages": { + "@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], + "@biomejs/biome": ["@biomejs/biome@2.3.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.5", "@biomejs/cli-darwin-x64": "2.3.5", "@biomejs/cli-linux-arm64": "2.3.5", "@biomejs/cli-linux-arm64-musl": "2.3.5", "@biomejs/cli-linux-x64": "2.3.5", "@biomejs/cli-linux-x64-musl": "2.3.5", "@biomejs/cli-win32-arm64": "2.3.5", "@biomejs/cli-win32-x64": "2.3.5" }, "bin": { "biome": "bin/biome" } }, "sha512-HvLhNlIlBIbAV77VysRIBEwp55oM/QAjQEin74QQX9Xb259/XP/D5AGGnZMOyF1el4zcvlNYYR3AyTMUV3ILhg=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fLdTur8cJU33HxHUUsii3GLx/TR0BsfQx8FkeqIiW33cGMtUD56fAtrh+2Fx1uhiCsVZlFh6iLKUU3pniZREQw=="], @@ -54,8 +61,112 @@ "@hono/zod-validator": ["@hono/zod-validator@0.7.5", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-n4l4hutkfYU07PzRUHBOVzUEn38VSfrh+UVE5d0w4lyfWDOEhzxIupqo5iakRiJL44c3vTuFJBvcmUl8b9agIA=="], + "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], + + "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@pinax/graph-networks-registry": ["@pinax/graph-networks-registry@0.7.1", "", {}, "sha512-Gn2kXRiEd5COAaMY/aDCRO0V+zfb1uQKCu5HFPoWka+EsZW27AlTINA7JctYYYEMuCbjMia5FBOzskjgEvj6LA=="], + "@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], + + "@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="], + + "@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], + + "@signinwithethereum/siwe": ["@signinwithethereum/siwe@4.2.0", "", { "dependencies": { "@signinwithethereum/siwe-parser": "^4.2.0" }, "peerDependencies": { "ethers": "^5.7.0 || ^6.13.0", "viem": "^2.7.0" }, "optionalPeers": ["ethers", "viem"] }, "sha512-6W+oyKgMFUZRJI4o9P9mTMzrokkXfu3tq1/CfOJj9QrMqyPqujJqv1xnxUAqDQwWVyCA8TMg0Fxk/+gXrDg2Nw=="], + + "@signinwithethereum/siwe-parser": ["@signinwithethereum/siwe-parser@4.2.0", "", { "dependencies": { "@noble/hashes": "^1.7.0", "apg-js": "^4.4.0" } }, "sha512-e3edh8XpZrEjbzVYc0BZ4ySFOa8RKTZOQTafSf1E6ejCB5XcBH93jEpYmjzry1EEocgMNq4KlB3YunsFNCXakQ=="], + + "@solana-program/compute-budget": ["@solana-program/compute-budget@0.11.0", "", { "peerDependencies": { "@solana/kit": "^5.0" } }, "sha512-7f1ePqB/eURkTwTOO9TNIdUXZcyrZoX3Uy2hNo7cXMfNhPFWp9AVgIyRNBc2jf15sdUa9gNpW+PfP2iV8AYAaw=="], + + "@solana-program/token": ["@solana-program/token@0.9.0", "", { "peerDependencies": { "@solana/kit": "^5.0" } }, "sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA=="], + + "@solana-program/token-2022": ["@solana-program/token-2022@0.6.1", "", { "peerDependencies": { "@solana/kit": "^5.0", "@solana/sysvars": "^5.0" } }, "sha512-Ex02cruDMGfBMvZZCrggVR45vdQQSI/unHVpt/7HPt/IwFYB4eTlXtO8otYZyqV/ce5GqZ8S6uwyRf0zy6fdbA=="], + + "@solana/accounts": ["@solana/accounts@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/rpc-spec": "6.8.0", "@solana/rpc-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-rXjFYVopaEw1H2PTBQbRjKr+0i4EFuBEhRT5E0dI4cMaabSb4KKypC2gaf47+6cjU3hMlM1AcsyIs72/MqAVBw=="], + + "@solana/addresses": ["@solana/addresses@6.8.0", "", { "dependencies": { "@solana/assertions": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/nominal-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-xVlA0DNX1LVfTueVsbhxDDoqr1VxeXvgJEh2GcIN/vcJPhY3GE3AYtjTbJJmTDgPrzOccI0t6ElVb1gelJH/PQ=="], + + "@solana/assertions": ["@solana/assertions@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-OU6prCq39fSvGL8xY1C/9vhghasvAkMiRlituzJxzJpZRfpVRrwhzLd6P5NPAPoQ28qKcenA50kFdw9+ZyneJQ=="], + + "@solana/codecs": ["@solana/codecs@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/options": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-qCSAaw1qszeQflavkIM7c21qJ3BHReP/qgDelZbhsEXpZc852CCZM00FOIWuxePr6X+JjSNqJquxwdDSoZe7Bw=="], + + "@solana/codecs-core": ["@solana/codecs-core@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-udFO8TrvzgROonwX3rY3E2SG675RehILNb4ZYcKlf1mL7vkDJ9bEJnBxi87AEwl8RWZFTl+MhT0MmrJnbpvdug=="], + + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-lHr0F+nNwgm9c+tWQX398yzYh1qDi7QSCJpY9MQ2azW4FfY2IyPSo7bqzTaWNnJh9pmJx3ZI6jHfXBnLD5k/SQ=="], + + "@solana/codecs-numbers": ["@solana/codecs-numbers@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-ebf4f1D19EAe0uhdUYOCEYnn5+EellsBxbJ42tM2yYEoIBVz5FoBBC0gSsq+UTNbQHFa7XagyBT3LewxXttiTQ=="], + + "@solana/codecs-strings": ["@solana/codecs-strings@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.0.0" }, "optionalPeers": ["fastestsmallesttextencoderdecoder", "typescript"] }, "sha512-Rpk5NVhbKYcPnE7wz3IpTp0GVNVs0IYKdmyzByiimgPTiII8eb8ay4wQiYHGHrpYh62hD14Qy3GiGDFgipRKqA=="], + + "@solana/errors": ["@solana/errors@6.8.0", "", { "dependencies": { "chalk": "5.6.2", "commander": "14.0.3" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"], "bin": { "errors": "bin/cli.mjs" } }, "sha512-HRTrLgTn0c99GKz4v4IKgz2+6soaRY1mh2tLW4sk1Fe4Zzv85Q6ZLK1mXrVGL73z1apyHDrr9/Sd/9ZhUsUvpA=="], + + "@solana/fast-stable-stringify": ["@solana/fast-stable-stringify@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-lZa3Qnsn+9ew6rHTXkPc+uqSa3i+AWqSBhV6oYxxBc+smvuxovItU4TPIs30cTfA7lAP+j+oYAQtUDu2dLy0hA=="], + + "@solana/functional": ["@solana/functional@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-oMSAD/8w9ujx7OplvwRWwHHFnaaxi/Xrji1XH3xAB+gzxupUpBbOmgxQ+e84x+9VN8QWk5aU3L7gmCqdTAR6OA=="], + + "@solana/instruction-plans": ["@solana/instruction-plans@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/instructions": "6.8.0", "@solana/keys": "6.8.0", "@solana/promises": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-osAsY8ozqohrcTcHlG1EmO3i9flc0eESMIy9akTHyVvqk915gZgkaTmt4IjcYSwBGt7i+Rh8TmLj27RrTpCKvg=="], + + "@solana/instructions": ["@solana/instructions@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-dTtykhS9IeN3npCfnd7wSS6KmKAh54+g90JRtLYy5/31L2Zvunf3AJz2QUk58vgsAGZ5fuoiMyhCxRJm4rHUBQ=="], + + "@solana/keys": ["@solana/keys@6.8.0", "", { "dependencies": { "@solana/assertions": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/nominal-types": "6.8.0", "@solana/promises": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-Wo8CnbrVfCP1Jbsb3ElMej/3dmMrl4ArPhI1mDcqIIz/O4j4HmxZYbn2BCWtnV9V/LPM638EMO2r1x6GzDNrPA=="], + + "@solana/kit": ["@solana/kit@6.8.0", "", { "dependencies": { "@solana/accounts": "6.8.0", "@solana/addresses": "6.8.0", "@solana/codecs": "6.8.0", "@solana/errors": "6.8.0", "@solana/functional": "6.8.0", "@solana/instruction-plans": "6.8.0", "@solana/instructions": "6.8.0", "@solana/keys": "6.8.0", "@solana/offchain-messages": "6.8.0", "@solana/plugin-core": "6.8.0", "@solana/plugin-interfaces": "6.8.0", "@solana/program-client-core": "6.8.0", "@solana/programs": "6.8.0", "@solana/rpc": "6.8.0", "@solana/rpc-api": "6.8.0", "@solana/rpc-parsed-types": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "@solana/rpc-subscriptions": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/signers": "6.8.0", "@solana/subscribable": "6.8.0", "@solana/sysvars": "6.8.0", "@solana/transaction-confirmation": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-+McC1aCgcUBdM7Cd7U6k2ZHJ9OKCy5mzpb0XWrhkrgsFxT0QoRr0AcWJc85o6tIDfG6Jz7vVhbS3l8ugYz2Vzw=="], + + "@solana/nominal-types": ["@solana/nominal-types@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-mLmHr92pM4mEfe49GUmZ5Ry0RMqtMuFQqZYnxQqhDKMcl+Wtt820ezxYgwPhqcMxRzfqaQSO3ZxpSB0RlLBa/Q=="], + + "@solana/offchain-messages": ["@solana/offchain-messages@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/keys": "6.8.0", "@solana/nominal-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-HoniTs2uoCHGicD0dTTJ3YBhLZC9URxdXXUf0CHalLFwAidF9iNuB8dsuKk16Euu68L4/ERKKGfyC0QobBvahw=="], + + "@solana/options": ["@solana/options@6.8.0", "", { "dependencies": { "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-T5441HHeucFaLtaMAJQJl79T7mX007oAFPunpPebBphRvCXGv+qQwQvqa4HkYct6Jf2O0aKLBL9GSe/kfdCk9A=="], + + "@solana/plugin-core": ["@solana/plugin-core@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-kdqFIhQvJP2BDUsMOIbor35esj8u78SO33Xv0Wmo+uTRg6yKONKVK53ghw235pWrinOT4f0VnVe6MN6ciYiQVA=="], + + "@solana/plugin-interfaces": ["@solana/plugin-interfaces@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/instruction-plans": "6.8.0", "@solana/keys": "6.8.0", "@solana/rpc-spec": "6.8.0", "@solana/rpc-subscriptions-spec": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/signers": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-4olaMKGUVA7wG6BBWM5A31bQsUWBlfcL1pjhq6ZTqVEJ7vshHXGwHVlWYXYyYn9ixozGDpGSl553yaRY9jQwWw=="], + + "@solana/program-client-core": ["@solana/program-client-core@6.8.0", "", { "dependencies": { "@solana/accounts": "6.8.0", "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/errors": "6.8.0", "@solana/instruction-plans": "6.8.0", "@solana/instructions": "6.8.0", "@solana/plugin-interfaces": "6.8.0", "@solana/rpc-api": "6.8.0", "@solana/signers": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-eOZtEnwl+vdiy9x/rFF89NDtnvt+Q3H04A/0u4GoHnt+fFkQG3JS+ChWG9c77izmpmRuz5C1GptOPDGNDnIUgQ=="], + + "@solana/programs": ["@solana/programs@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-8hSKGfPTLX9Sm7KGV/UtiGCeSzptT/9vcjbodE+ZGHKFefo5vES4UAW+qD01LjL7IumGtMJvnfhCWt81qT/jbQ=="], + + "@solana/promises": ["@solana/promises@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-kIypZG83ZbADbrAq9/LS7LuWlVxlgJSzIpic75+9IuAfC3k5/KSus8LrvggBkCzfAyIslrUh70iz4JcnzUZrOw=="], + + "@solana/rpc": ["@solana/rpc@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/fast-stable-stringify": "6.8.0", "@solana/functional": "6.8.0", "@solana/rpc-api": "6.8.0", "@solana/rpc-spec": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "@solana/rpc-transformers": "6.8.0", "@solana/rpc-transport-http": "6.8.0", "@solana/rpc-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-+jW4n9TDmBttY3bO3PdUo54GAnwFrd7UJsyfXoMgl/lWGQq5uddYDgnzQLtHOBP5zKslkR8h0RKkic0GZhMZrQ=="], + + "@solana/rpc-api": ["@solana/rpc-api@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/keys": "6.8.0", "@solana/rpc-parsed-types": "6.8.0", "@solana/rpc-spec": "6.8.0", "@solana/rpc-transformers": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-v8ZKWgPtKbF6HeJcfC4ciwI8mwDCizBtRLYYjjHOu+9S9IJYyefQzsQxL5P8OjJPpI4gFauT6gsjQLo76BoojA=="], + + "@solana/rpc-parsed-types": ["@solana/rpc-parsed-types@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-jYddZviBSUYbuUKqvNthet7KbJVI7me6xfRH2znv1SjIpmvhSPJcGN5QrlHVOasHdzEWSpvZa5VYDfnqH3aYvA=="], + + "@solana/rpc-spec": ["@solana/rpc-spec@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/rpc-spec-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-kE5uOspxCVFJKNUu73hlebGiAFosjfYXbbTXAbGKfksPzy84u1oJFC2IVIobLRnqUCw1x7oJcvfnX00Zs0Itpg=="], + + "@solana/rpc-spec-types": ["@solana/rpc-spec-types@6.8.0", "", { "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-ebCWgiQbIgFOehU7PdRFmYCzda3Azc/qa2Y3P8gexSHSsDAO27VwS4E05XSY+a7cIL5MYmvUa1vpDynl1Rkakw=="], + + "@solana/rpc-subscriptions": ["@solana/rpc-subscriptions@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/fast-stable-stringify": "6.8.0", "@solana/functional": "6.8.0", "@solana/promises": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "@solana/rpc-subscriptions-api": "6.8.0", "@solana/rpc-subscriptions-channel-websocket": "6.8.0", "@solana/rpc-subscriptions-spec": "6.8.0", "@solana/rpc-transformers": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/subscribable": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-9CotreNZmKAP2z07FY1I7TPPvylKLFF5p4mujB5ZFMHQPp5JVQFVCmMIhSj5voZHAeYx7jdwJ2Kf0RDeClqJzA=="], + + "@solana/rpc-subscriptions-api": ["@solana/rpc-subscriptions-api@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/keys": "6.8.0", "@solana/rpc-subscriptions-spec": "6.8.0", "@solana/rpc-transformers": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-cPJOsydyoqkztW3msEH09wPDYqxJcMvO6DBlvrboq6wGu1UjeP66w2eApzQ8POoQHxhyw+CfEXl1Gbu6kKwuMQ=="], + + "@solana/rpc-subscriptions-channel-websocket": ["@solana/rpc-subscriptions-channel-websocket@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/functional": "6.8.0", "@solana/rpc-subscriptions-spec": "6.8.0", "@solana/subscribable": "6.8.0", "ws": "^8.19.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-c3PpkorYwhAz1iuUfM5sLpZQi8xtZFGbaPbaPRELVeDjFSRzoa12KFnuQs4i9fbVbLy5Cnt1t23tf0bL2snZCQ=="], + + "@solana/rpc-subscriptions-spec": ["@solana/rpc-subscriptions-spec@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/promises": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "@solana/subscribable": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-+t4L5q9qE6IVfunW3n1amA/3EswJr64pVqRF7234vCUuVUz4PgYfbqtEBV3KkA1o0NwEHHM3pXuofT63nBb8Bg=="], + + "@solana/rpc-transformers": ["@solana/rpc-transformers@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/functional": "6.8.0", "@solana/nominal-types": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "@solana/rpc-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-GzcFkllym7eXbw7grdE41MCb15CjkibrXtr7EFsf4d6LD9DRvzFj2ZRYywS2FB2ibVP0LUXXGk3vmtkZJjfajA=="], + + "@solana/rpc-transport-http": ["@solana/rpc-transport-http@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0", "@solana/rpc-spec": "6.8.0", "@solana/rpc-spec-types": "6.8.0", "undici-types": "^8.0.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-jw/L0q2motGcx7yo6KvkKJd2HGVg9gvViXatFloLl1XmHbkwE7+97YYmG17WRuM5xauzI/UGYOXNW7cEB+Uaxw=="], + + "@solana/rpc-types": ["@solana/rpc-types@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/nominal-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-vACMV9VR2JsZGDcgaMOFN/dwLK57CsE+erassxxtF12sSPXJooz+Vu1vyY2Yp2EkCc7mDf7BNkTKvSXajbt+Qw=="], + + "@solana/signers": ["@solana/signers@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/errors": "6.8.0", "@solana/instructions": "6.8.0", "@solana/keys": "6.8.0", "@solana/nominal-types": "6.8.0", "@solana/offchain-messages": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-7E1cAXBLOcz9kmHhzWdu5m3UJlJzxfwOl8irOMLJI6NnKB2EmU0B0h4I+Mlfs9w8Bfj0WQpUei21ammbNBq39g=="], + + "@solana/subscribable": ["@solana/subscribable@6.8.0", "", { "dependencies": { "@solana/errors": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-yj41Q97MiWrOmLj1iRFobvTdtU6H5wz5BlH5FHJg9lyapy1YQyaYF37MZx4LiUj4Ww0V3ReluIZTWWDBOJ53Jg=="], + + "@solana/sysvars": ["@solana/sysvars@5.5.1", "", { "dependencies": { "@solana/accounts": "5.5.1", "@solana/codecs": "5.5.1", "@solana/errors": "5.5.1", "@solana/rpc-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA=="], + + "@solana/transaction-confirmation": ["@solana/transaction-confirmation@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/keys": "6.8.0", "@solana/promises": "6.8.0", "@solana/rpc": "6.8.0", "@solana/rpc-subscriptions": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/transaction-messages": "6.8.0", "@solana/transactions": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-R6rj8y/+kZqYJr8FR/fWxgi3Pw3eCiacUyjCPTVtdVe6i+hIiBApTGLzXrSRJmAMdpZrjYBZU1cG8C6oAb+B2A=="], + + "@solana/transaction-messages": ["@solana/transaction-messages@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/errors": "6.8.0", "@solana/functional": "6.8.0", "@solana/instructions": "6.8.0", "@solana/nominal-types": "6.8.0", "@solana/rpc-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-jsJu9mAcN1x7onKOeC4WEvYP04UVcnkOYu/9bMe+S9jqjL+3DMy9kFZpV5FBl+TPuTNJrtOqc6Gc28hUWyyp1A=="], + + "@solana/transactions": ["@solana/transactions@6.8.0", "", { "dependencies": { "@solana/addresses": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/codecs-strings": "6.8.0", "@solana/errors": "6.8.0", "@solana/functional": "6.8.0", "@solana/instructions": "6.8.0", "@solana/keys": "6.8.0", "@solana/nominal-types": "6.8.0", "@solana/rpc-types": "6.8.0", "@solana/transaction-messages": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-Q46m+o3C1yL2EIZBAP5B8ou2VZwHN9wTi+muIS6/giCKO3jwUtnTEbWcZEDMj2vxUb7P2WfwTluZb/VAWxlx7Q=="], + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.8", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-80ap74p5oy/SU4al5HkPwO5+NbN2wH/FBr6kwaE5ROq7AvcDFaxzUfTazewroNaCotbvdGcvzXb9oEoOIyfC/Q=="], @@ -74,32 +185,138 @@ "@web3icons/core": ["@web3icons/core@4.0.33", "", { "dependencies": { "@web3icons/common": "0.11.28" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-aR8cSGfE9agMHmQWlYJ9nQpkoPAB1SdaskNosZMTR+xSqnRt4xi+5W03TOYMzJlirfvK7/w48axapi49ci5GmA=="], + "@x402/core": ["@x402/core@2.11.0", "", { "dependencies": { "zod": "^3.24.2" } }, "sha512-aqTfZc/BULrlWnd3I0lsqRQaH4gjJd8CsPcL16XqK2Lx5c6QDm+zCljgUVS1yj9BGJoZeQWTzI5hE+SVFkqMTw=="], + + "@x402/evm": ["@x402/evm@2.11.0", "", { "dependencies": { "@x402/core": "~2.11.0", "viem": "^2.39.3", "zod": "^3.24.2" } }, "sha512-F8uU1txDZA+wc/sEnmaHAyYvoTi/w39r7K3a44MmQHSxECDTEuB3A0FwbxOxUPLN1eyCxTAFKEiqlGe3bwybKA=="], + + "@x402/extensions": ["@x402/extensions@2.11.0", "", { "dependencies": { "@noble/curves": "^1.9.0", "@scure/base": "^1.2.6", "@signinwithethereum/siwe": "^4.1.0", "@x402/core": "~2.11.0", "ajv": "^8.17.1", "jose": "^5.9.6", "tweetnacl": "^1.0.3", "viem": "^2.43.5", "zod": "^3.24.2" } }, "sha512-S0SOoUsx4WtWqiWZuqslSzhopW/JcrEbnEC5l2sIQH/TABWzR0DgJLy36C43tD6TOJc0tEPN9bHuD+V8DYWomA=="], + + "@x402/hono": ["@x402/hono@2.11.0", "", { "dependencies": { "@x402/core": "~2.11.0", "@x402/extensions": "~2.11.0", "zod": "^3.24.2" }, "peerDependencies": { "@x402/paywall": "^2.11.0", "hono": "^4.0.0" }, "optionalPeers": ["@x402/paywall"] }, "sha512-myPsDNMeCLqQgk0O1Rbdx2kdpVNRumnnwiV3fTgSbiQzxiTWDc5IU/7fEWPSsXNbLtt9kB6eXWT9VxEkcJJolg=="], + + "@x402/svm": ["@x402/svm@2.11.0", "", { "dependencies": { "@solana-program/compute-budget": "^0.11.0", "@solana-program/token": "^0.9.0", "@solana-program/token-2022": "^0.6.1", "@x402/core": "~2.11.0" }, "peerDependencies": { "@solana/kit": ">=5.1.0" } }, "sha512-MmhLlEeb3FtgTxO4n3RkZszKEBBnYLfNnX8P3TvqVYP1u9gY2SgbYW4K3TsPAv00edCxoCOqYixI2JurYjW4Sw=="], + + "abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], + + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "apg-js": ["apg-js@4.4.0", "", {}, "sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q=="], + "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], "csstype": ["csstype@3.2.2", "", {}, "sha512-D80T+tiqkd/8B0xNlbstWDG4x6aqVfO52+OlSUNIdkTvmNw0uQpJLeos2J/2XvpyidAFuTPmpad+tUxLndwj6g=="], "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="], + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "hono": ["hono@4.10.6", "", {}, "sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g=="], "hono-openapi": ["hono-openapi@1.1.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.1.2", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.8", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-AC3HNhZYPHhnZdSy2Je7GDoTTNxPos6rKRQKVDBbSilY3cWJPqsxRnN6zA4pU7tfxmQEMTqkiLXbw6sAaemB8Q=="], + "isows": ["isows@1.0.7", "", { "peerDependencies": { "ws": "*" } }, "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg=="], + + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + "ox": ["ox@0.14.20", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rby38C3nDn8eQkf29Zgw4hkCZJ64Qqi0zRPWL8ENUQ7JVuoITqrVtwWQgM/He19SCMUEc7hS/Sjw0jIOSLJhOw=="], + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "tslog": ["tslog@4.10.2", "", {}, "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g=="], + "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "viem": ["viem@2.48.8", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.20", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-Xj3Nrt66SKtn06kczU91ELn9Difr84ZM5A62BTlaisT5lpgt058i2mBkfMZCXHGb1ocOLjzC2ztPhD0Lvky7uQ=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], "zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], + + "@solana/errors/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + + "@solana/kit/@solana/sysvars": ["@solana/sysvars@6.8.0", "", { "dependencies": { "@solana/accounts": "6.8.0", "@solana/codecs-core": "6.8.0", "@solana/codecs-data-structures": "6.8.0", "@solana/codecs-numbers": "6.8.0", "@solana/errors": "6.8.0", "@solana/rpc-types": "6.8.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-pwfMpMNL6MSmm07eHQYdTdRdzmPOd+EuVCCaNLSYdWGpYcocVJiaLiNWRV3cXA5wPj/ZFkoUGtc1bo0v7H50lw=="], + + "@solana/rpc-subscriptions-channel-websocket/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + + "@solana/rpc-transport-http/undici-types": ["undici-types@8.2.0", "", {}, "sha512-uciYZ5yCmf+QJb18kJw10HjquzM7K0z992vWcI+84KeBpTfXT4hfgfGJ5DQbf/mCBPACofkrjvqgcjZfuujjFA=="], + + "@solana/sysvars/@solana/accounts": ["@solana/accounts@5.5.1", "", { "dependencies": { "@solana/addresses": "5.5.1", "@solana/codecs-core": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/errors": "5.5.1", "@solana/rpc-spec": "5.5.1", "@solana/rpc-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ=="], + + "@solana/sysvars/@solana/codecs": ["@solana/codecs@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-data-structures": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/options": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g=="], + + "@solana/sysvars/@solana/errors": ["@solana/errors@5.5.1", "", { "dependencies": { "chalk": "5.6.2", "commander": "14.0.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "errors": "bin/cli.mjs" } }, "sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg=="], + + "@solana/sysvars/@solana/rpc-types": ["@solana/rpc-types@5.5.1", "", { "dependencies": { "@solana/addresses": "5.5.1", "@solana/codecs-core": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/errors": "5.5.1", "@solana/nominal-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA=="], + + "@x402/core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@x402/evm/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@x402/extensions/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@x402/hono/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + + "viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + + "@solana/sysvars/@solana/accounts/@solana/addresses": ["@solana/addresses@5.5.1", "", { "dependencies": { "@solana/assertions": "5.5.1", "@solana/codecs-core": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/errors": "5.5.1", "@solana/nominal-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA=="], + + "@solana/sysvars/@solana/accounts/@solana/codecs-core": ["@solana/codecs-core@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw=="], + + "@solana/sysvars/@solana/accounts/@solana/codecs-strings": ["@solana/codecs-strings@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": "^5.0.0" }, "optionalPeers": ["fastestsmallesttextencoderdecoder", "typescript"] }, "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A=="], + + "@solana/sysvars/@solana/accounts/@solana/rpc-spec": ["@solana/rpc-spec@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1", "@solana/rpc-spec-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": "^5.0.0" }, "optionalPeers": ["fastestsmallesttextencoderdecoder", "typescript"] }, "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A=="], + + "@solana/sysvars/@solana/codecs/@solana/options": ["@solana/options@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-data-structures": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A=="], + + "@solana/sysvars/@solana/rpc-types/@solana/addresses": ["@solana/addresses@5.5.1", "", { "dependencies": { "@solana/assertions": "5.5.1", "@solana/codecs-core": "5.5.1", "@solana/codecs-strings": "5.5.1", "@solana/errors": "5.5.1", "@solana/nominal-types": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA=="], + + "@solana/sysvars/@solana/rpc-types/@solana/codecs-core": ["@solana/codecs-core@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw=="], + + "@solana/sysvars/@solana/rpc-types/@solana/codecs-numbers": ["@solana/codecs-numbers@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw=="], + + "@solana/sysvars/@solana/rpc-types/@solana/codecs-strings": ["@solana/codecs-strings@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/codecs-numbers": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": "^5.0.0" }, "optionalPeers": ["fastestsmallesttextencoderdecoder", "typescript"] }, "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A=="], + + "@solana/sysvars/@solana/rpc-types/@solana/nominal-types": ["@solana/nominal-types@5.5.1", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ=="], + + "@solana/sysvars/@solana/accounts/@solana/addresses/@solana/assertions": ["@solana/assertions@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q=="], + + "@solana/sysvars/@solana/accounts/@solana/addresses/@solana/nominal-types": ["@solana/nominal-types@5.5.1", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ=="], + + "@solana/sysvars/@solana/accounts/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@5.5.1", "", { "dependencies": { "@solana/codecs-core": "5.5.1", "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw=="], + + "@solana/sysvars/@solana/accounts/@solana/rpc-spec/@solana/rpc-spec-types": ["@solana/rpc-spec-types@5.5.1", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw=="], + + "@solana/sysvars/@solana/rpc-types/@solana/addresses/@solana/assertions": ["@solana/assertions@5.5.1", "", { "dependencies": { "@solana/errors": "5.5.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q=="], } } diff --git a/package.json b/package.json index 2cf8be12..693c5384 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,11 @@ "@hono/zod-validator": "^0.7.5", "@pinax/graph-networks-registry": "^0.7.1", "@web3icons/core": "^4.0.18", + "@x402/core": "^2.11.0", + "@x402/evm": "^2.11.0", + "@x402/extensions": "^2.11.0", + "@x402/hono": "^2.11.0", + "@x402/svm": "^2.11.0", "commander": "^14.0.0", "dotenv": "^17.2.1", "hono": "^4.8.12", diff --git a/src/config.ts b/src/config.ts index 839d1477..a6186835 100644 --- a/src/config.ts +++ b/src/config.ts @@ -40,6 +40,16 @@ export const DEFAULT_CACHE_SERVER_MAX_AGE = 600; // s-maxage for shared/proxy ca export const DEFAULT_CACHE_MAX_AGE = 60; // max-age for browser caches export const DEFAULT_CACHE_STALE_WHILE_REVALIDATE = 30; // RFC 5861 stale-while-revalidate window export const DEFAULT_PLANS = ''; +export const DEFAULT_X402_ENABLED = false; +export const DEFAULT_X402_FACILITATOR_URL = 'https://api.cdp.coinbase.com/platform/v2/x402'; +export const DEFAULT_X402_EVM_NETWORK = 'eip155:8453'; +export const DEFAULT_X402_SVM_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'; +export const DEFAULT_X402_EVM_PAY_TO = '0x49D581486438aAD93f4114084Ac5B09A8b7C9685'; +export const DEFAULT_X402_SVM_PAY_TO = 'EpRR35QnB5PfTczy3j5rp9bCw4NKzHkd8S1ubdza4my9'; +export const DEFAULT_X402_PASS_PRICE = '$0.10'; +export const DEFAULT_X402_PASS_DURATION_SECONDS = 3600; +export const DEFAULT_X402_PLAN = 'pro'; +export const DEFAULT_X402_SYNC_FACILITATOR_ON_START = true; // GitHub metadata const GIT_COMMIT = (process.env.GIT_COMMIT ?? (await $`git rev-parse HEAD`.text())).replace(/\n/, '').slice(0, 7); @@ -255,6 +265,58 @@ const opts = program .env('PLANS') .default(DEFAULT_PLANS) ) + .addOption( + new Option('--x402-enabled ', 'Enable x402 access pass payment middleware') + .choices(['true', 'false']) + .env('X402_ENABLED') + .default(DEFAULT_X402_ENABLED) + ) + .addOption( + new Option('--x402-facilitator-url ', 'x402 facilitator HTTP URL') + .env('X402_FACILITATOR_URL') + .default(DEFAULT_X402_FACILITATOR_URL) + ) + .addOption( + new Option('--x402-evm-network ', 'CAIP-2 EVM network accepted for x402 payments') + .env('X402_EVM_NETWORK') + .default(DEFAULT_X402_EVM_NETWORK) + ) + .addOption( + new Option('--x402-svm-network ', 'CAIP-2 SVM network accepted for x402 payments') + .env('X402_SVM_NETWORK') + .default(DEFAULT_X402_SVM_NETWORK) + ) + .addOption( + new Option('--x402-evm-pay-to ', 'EVM payment recipient for x402 access passes') + .env('X402_EVM_PAY_TO') + .default(DEFAULT_X402_EVM_PAY_TO) + ) + .addOption( + new Option('--x402-svm-pay-to ', 'SVM payment recipient for x402 access passes') + .env('X402_SVM_PAY_TO') + .default(DEFAULT_X402_SVM_PAY_TO) + ) + .addOption( + new Option('--x402-pass-price ', 'USD-denominated x402 access pass price') + .env('X402_PASS_PRICE') + .default(DEFAULT_X402_PASS_PRICE) + ) + .addOption( + new Option('--x402-pass-duration-seconds ', 'x402 access pass duration in seconds') + .env('X402_PASS_DURATION_SECONDS') + .default(DEFAULT_X402_PASS_DURATION_SECONDS) + ) + .addOption( + new Option('--x402-plan ', 'Plan applied to requests with valid x402 payment access') + .env('X402_PLAN') + .default(DEFAULT_X402_PLAN) + ) + .addOption( + new Option('--x402-sync-facilitator-on-start ', 'Sync x402 facilitator support before first request') + .choices(['true', 'false']) + .env('X402_SYNC_FACILITATOR_ON_START') + .default(DEFAULT_X402_SYNC_FACILITATOR_ON_START) + ) .allowUnknownOption() .allowExcessArguments() .parse() @@ -294,6 +356,16 @@ const config = z cacheMaxAge: z.coerce.number().nonnegative('Cache max-age must be non-negative'), cacheStaleWhileRevalidate: z.coerce.number().nonnegative('Cache stale-while-revalidate must be non-negative'), plans: z.string().transform(parsePlans), + x402Enabled: z.coerce.string().transform((val) => val.toLowerCase() === 'true'), + x402FacilitatorUrl: z.string().url({ message: 'Invalid x402 facilitator URL' }), + x402EvmNetwork: z.string().min(1, 'x402 EVM network cannot be empty'), + x402SvmNetwork: z.string().min(1, 'x402 SVM network cannot be empty'), + x402EvmPayTo: z.string().min(1, 'x402 EVM pay-to address cannot be empty'), + x402SvmPayTo: z.string().min(1, 'x402 SVM pay-to address cannot be empty'), + x402PassPrice: z.string().regex(/^\$\d+(\.\d+)?$/, 'x402 pass price must be a USD amount like $0.10'), + x402PassDurationSeconds: z.coerce.number().positive('x402 pass duration must be positive'), + x402Plan: z.string().min(1, 'x402 plan cannot be empty'), + x402SyncFacilitatorOnStart: z.coerce.string().transform((val) => val.toLowerCase() === 'true'), }) .transform((data) => { // Use YAML config as the authoritative source — spread all database maps dynamically diff --git a/src/middleware/cacheControl.spec.ts b/src/middleware/cacheControl.spec.ts new file mode 100644 index 00000000..82724746 --- /dev/null +++ b/src/middleware/cacheControl.spec.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { config } from '../config.js'; +import { cacheControl } from './cacheControl.js'; + +describe('cacheControl', () => { + it('sets public cache headers on successful cacheable responses', async () => { + const previousDisable = config.cacheDisable; + config.cacheDisable = false; + + const app = new Hono(); + app.use('*', cacheControl()); + app.get('/', (ctx) => ctx.text('ok')); + + const response = await app.request('/'); + + expect(response.headers.get('Cache-Control')).toContain('public'); + + config.cacheDisable = previousDisable; + }); + + it('does not set public cache headers for x402 paid requests', async () => { + const previousDisable = config.cacheDisable; + config.cacheDisable = false; + + const app = new Hono(); + app.use('*', cacheControl()); + app.get('/', (ctx) => ctx.text('ok')); + + const response = await app.request('/', { + headers: { + 'PAYMENT-SIGNATURE': 'signature', + }, + }); + + expect(response.headers.get('Cache-Control')).toBeNull(); + + config.cacheDisable = previousDisable; + }); +}); diff --git a/src/middleware/cacheControl.ts b/src/middleware/cacheControl.ts index d9f40dc0..cb715068 100644 --- a/src/middleware/cacheControl.ts +++ b/src/middleware/cacheControl.ts @@ -1,5 +1,6 @@ import type { Context, Next } from 'hono'; import { config } from '../config.js'; +import { hasPaymentHeader, LEGACY_PAYMENT_RESPONSE_HEADER, PAYMENT_RESPONSE_HEADER } from '../x402/accessPass.js'; /** * Hono middleware that adds HTTP Cache-Control headers to cacheable responses. @@ -10,6 +11,7 @@ import { config } from '../config.js'; * Behaviour: * - When `CACHE_DISABLE=true`, no cache headers are emitted. * - When the request includes `Cache-Control: no-cache`, no cache headers are emitted. + * - When the request/response carries x402 payment state, no shared cache headers are emitted. * * Note: ETag/If-None-Match is intentionally omitted — response bodies include dynamic * metadata (request_time, duration_ms, statistics) that change on every request, making @@ -35,6 +37,15 @@ export function cacheControl() { // Skip cache headers if client requests no-cache if (ctx.req.header('Cache-Control') === 'no-cache') return; + // Skip cache headers for paid responses; access is tied to payment receipt state. + if ( + hasPaymentHeader(ctx.req.raw.headers) || + ctx.res.headers.has(PAYMENT_RESPONSE_HEADER) || + ctx.res.headers.has(LEGACY_PAYMENT_RESPONSE_HEADER) + ) { + return; + } + // Only cache successful responses if (ctx.res.status !== 200) return; diff --git a/src/routes/index.ts b/src/routes/index.ts index ab9f2dac..f93e4d5b 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,6 +1,7 @@ import { Hono } from 'hono'; import { cacheControl } from '../middleware/cacheControl.js'; import { normalizeProtocolQuery } from '../middleware/normalizeProtocolQuery.js'; +import { x402PaymentMiddleware, x402PlanHeaderMiddleware } from '../x402/middleware.js'; // Balances import evmBalances from './balances/evm.js'; // import tvmBalancesNative from './balances/tvm_native.js'; @@ -79,6 +80,11 @@ const router = new Hono(); // Normalize request query parameters before validation/route handling. router.use('/v1/*', normalizeProtocolQuery); +// --- x402 payment middleware --- +// Protects paid API routes when enabled; free monitoring/discovery endpoints remain outside x402. +router.use('/v1/*', x402PlanHeaderMiddleware); +router.use('/v1/*', x402PaymentMiddleware); + // --- HTTP Cache-Control middleware --- // Default: all /v1/* routes get a minimal 1s cache (no SWR). // Specific routes below override with longer env-configured TTLs. diff --git a/src/x402/accessPass.spec.ts b/src/x402/accessPass.spec.ts new file mode 100644 index 00000000..408689f9 --- /dev/null +++ b/src/x402/accessPass.spec.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from 'bun:test'; +import { encodePaymentSignatureHeader } from '@x402/core/http'; +import type { Network, PaymentPayload, SettleResponse } from '@x402/core/types'; +import { PAYMENT_IDENTIFIER } from '@x402/extensions/payment-identifier'; +import { + cacheSettledAccessPass, + getPaymentHeader, + getPaymentIdentifierFromHeader, + X402AccessPassStore, + x402AccessPassStore, +} from './accessPass.js'; + +const paymentPayload: PaymentPayload = { + x402Version: 2, + accepted: { + scheme: 'exact', + network: 'eip155:8453' as Network, + asset: 'USDC', + amount: '100000', + payTo: '0x49D581486438aAD93f4114084Ac5B09A8b7C9685', + maxTimeoutSeconds: 120, + extra: {}, + }, + payload: {}, + extensions: { + [PAYMENT_IDENTIFIER]: { + info: { + required: true, + id: 'pay_1234567890abcdef', + }, + }, + }, +}; + +const settleResponse: SettleResponse = { + success: true, + transaction: '0xsettled', + network: 'eip155:8453' as Network, + amount: '100000', + payer: '0xpayer', +}; + +describe('X402AccessPassStore', () => { + it('returns a valid pass and prunes expired passes', () => { + const store = new X402AccessPassStore(); + + store.set({ + id: 'pay_1234567890abcdef', + transaction: '0xsettled', + network: 'eip155:8453', + createdAt: 1_000, + expiresAt: 2_000, + }); + + expect(store.get('pay_1234567890abcdef', 1_500)?.transaction).toBe('0xsettled'); + expect(store.get('pay_1234567890abcdef', 2_000)).toBeUndefined(); + expect(store.size(2_000)).toBe(0); + }); +}); + +describe('x402 access pass helpers', () => { + it('extracts the payment identifier from a payment signature header', () => { + const header = encodePaymentSignatureHeader(paymentPayload); + + expect(getPaymentIdentifierFromHeader(header)).toBe('pay_1234567890abcdef'); + }); + + it('returns undefined for malformed payment headers', () => { + expect(getPaymentIdentifierFromHeader('not-base64-json')).toBeUndefined(); + }); + + it('reads both v2 and legacy payment headers', () => { + const headers = new Headers({ 'X-PAYMENT': encodePaymentSignatureHeader(paymentPayload) }); + + expect(getPaymentHeader(headers)).toBeDefined(); + }); + + it('caches settled payments for the configured duration', () => { + x402AccessPassStore.clear(); + + const pass = cacheSettledAccessPass(paymentPayload, settleResponse, 3600, 1_000); + + expect(pass).toMatchObject({ + id: 'pay_1234567890abcdef', + transaction: '0xsettled', + expiresAt: 3_601_000, + }); + expect(x402AccessPassStore.get('pay_1234567890abcdef', 3_600_999)).toBeDefined(); + x402AccessPassStore.clear(); + }); +}); diff --git a/src/x402/accessPass.ts b/src/x402/accessPass.ts new file mode 100644 index 00000000..16f5ab08 --- /dev/null +++ b/src/x402/accessPass.ts @@ -0,0 +1,108 @@ +import { decodePaymentSignatureHeader } from '@x402/core/http'; +import type { PaymentPayload, SettleResponse } from '@x402/core/types'; +import { extractPaymentIdentifier } from '@x402/extensions/payment-identifier'; + +export const PAYMENT_SIGNATURE_HEADER = 'PAYMENT-SIGNATURE'; +export const LEGACY_PAYMENT_HEADER = 'X-PAYMENT'; +export const PAYMENT_RESPONSE_HEADER = 'PAYMENT-RESPONSE'; +export const LEGACY_PAYMENT_RESPONSE_HEADER = 'X-PAYMENT-RESPONSE'; + +export type X402AccessPass = { + id: string; + payer?: string; + transaction: string; + network: string; + amount?: string; + expiresAt: number; + createdAt: number; +}; + +export class X402AccessPassStore { + private passes = new Map(); + + get(id: string, nowMs = Date.now()) { + const pass = this.passes.get(id); + + if (!pass) return undefined; + + if (pass.expiresAt <= nowMs) { + this.passes.delete(id); + return undefined; + } + + return pass; + } + + set(pass: X402AccessPass) { + this.passes.set(pass.id, pass); + } + + size(nowMs = Date.now()) { + this.prune(nowMs); + return this.passes.size; + } + + clear() { + this.passes.clear(); + } + + prune(nowMs = Date.now()) { + for (const [id, pass] of this.passes.entries()) { + if (pass.expiresAt <= nowMs) this.passes.delete(id); + } + } +} + +export const x402AccessPassStore = new X402AccessPassStore(); + +export function getPaymentHeader(headers: Headers | { get(name: string): string | null | undefined }) { + return headers.get(PAYMENT_SIGNATURE_HEADER) ?? headers.get(LEGACY_PAYMENT_HEADER) ?? undefined; +} + +export function hasPaymentHeader(headers: Headers | { get(name: string): string | null | undefined }) { + return getPaymentHeader(headers) !== undefined; +} + +export function getPaymentIdentifierFromPayload(paymentPayload: PaymentPayload) { + return extractPaymentIdentifier(paymentPayload, true) ?? undefined; +} + +export function getPaymentPayloadFromHeader(paymentHeader: string) { + try { + return decodePaymentSignatureHeader(paymentHeader); + } catch { + return undefined; + } +} + +export function getPaymentIdentifierFromHeader(paymentHeader: string) { + const paymentPayload = getPaymentPayloadFromHeader(paymentHeader); + if (!paymentPayload) return undefined; + + return getPaymentIdentifierFromPayload(paymentPayload); +} + +export function cacheSettledAccessPass( + paymentPayload: PaymentPayload, + settleResult: Readonly, + durationSeconds: number, + nowMs = Date.now() +) { + if (!settleResult.success) return undefined; + + const id = getPaymentIdentifierFromPayload(paymentPayload); + if (!id) return undefined; + + const pass: X402AccessPass = { + id, + payer: settleResult.payer, + transaction: settleResult.transaction, + network: settleResult.network, + amount: settleResult.amount, + createdAt: nowMs, + expiresAt: nowMs + durationSeconds * 1000, + }; + + x402AccessPassStore.set(pass); + return pass; +} diff --git a/src/x402/middleware.spec.ts b/src/x402/middleware.spec.ts new file mode 100644 index 00000000..5b036d5e --- /dev/null +++ b/src/x402/middleware.spec.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { config } from '../config.js'; +import { x402PlanHeaderMiddleware } from './middleware.js'; + +describe('x402PlanHeaderMiddleware', () => { + it('applies the configured paid plan when an x402 payment header is present', async () => { + const previousEnabled = config.x402Enabled; + const previousPlan = config.x402Plan; + config.x402Enabled = true; + config.x402Plan = 'pro'; + + const app = new Hono(); + app.use('*', x402PlanHeaderMiddleware); + app.get('/v1/evm/tokens', (ctx) => ctx.text(ctx.req.header('X-Plan') ?? 'missing')); + + const response = await app.request('/v1/evm/tokens', { + headers: { + 'PAYMENT-SIGNATURE': 'signature', + }, + }); + + expect(await response.text()).toBe('pro'); + + config.x402Enabled = previousEnabled; + config.x402Plan = previousPlan; + }); + + it('leaves the plan untouched when x402 is disabled', async () => { + const previousEnabled = config.x402Enabled; + config.x402Enabled = false; + + const app = new Hono(); + app.use('*', x402PlanHeaderMiddleware); + app.get('/v1/evm/tokens', (ctx) => ctx.text(ctx.req.header('X-Plan') ?? 'missing')); + + const response = await app.request('/v1/evm/tokens', { + headers: { + 'PAYMENT-SIGNATURE': 'signature', + }, + }); + + expect(await response.text()).toBe('missing'); + + config.x402Enabled = previousEnabled; + }); +}); diff --git a/src/x402/middleware.ts b/src/x402/middleware.ts new file mode 100644 index 00000000..28337aae --- /dev/null +++ b/src/x402/middleware.ts @@ -0,0 +1,67 @@ +import { HTTPFacilitatorClient, x402HTTPResourceServer, x402ResourceServer } from '@x402/core/server'; +import type { Network, PaymentPayload } from '@x402/core/types'; +import { ExactEvmScheme } from '@x402/evm/exact/server'; +import { bazaarResourceServerExtension } from '@x402/extensions/bazaar'; +import { paymentIdentifierResourceServerExtension } from '@x402/extensions/payment-identifier'; +import { paymentMiddlewareFromHTTPServer } from '@x402/hono'; +import { ExactSvmScheme } from '@x402/svm/exact/server'; +import type { MiddlewareHandler } from 'hono'; +import { config } from '../config.js'; +import { + cacheSettledAccessPass, + getPaymentHeader, + getPaymentIdentifierFromHeader, + x402AccessPassStore, +} from './accessPass.js'; +import { createX402RouteConfig, isX402ProtectedRoute } from './routes.js'; + +const passthroughMiddleware: MiddlewareHandler = async (_ctx, next) => { + await next(); +}; + +export const x402PlanHeaderMiddleware: MiddlewareHandler = async (ctx, next) => { + if ( + config.x402Enabled && + isX402ProtectedRoute(ctx.req.method, ctx.req.path) && + getPaymentHeader(ctx.req.raw.headers) + ) { + ctx.req.raw.headers.set('X-Plan', config.x402Plan); + } + + await next(); +}; + +export function createX402PaymentMiddleware(): MiddlewareHandler { + if (!config.x402Enabled) return passthroughMiddleware; + + const facilitatorClient = new HTTPFacilitatorClient({ + url: config.x402FacilitatorUrl, + }); + const routeConfig = createX402RouteConfig(config); + const resourceServer = new x402ResourceServer(facilitatorClient) + .register(config.x402EvmNetwork as Network, new ExactEvmScheme()) + .register(config.x402SvmNetwork as Network, new ExactSvmScheme()) + .registerExtension(paymentIdentifierResourceServerExtension) + .registerExtension(bazaarResourceServerExtension) + .onAfterSettle(async (settleContext) => { + cacheSettledAccessPass( + settleContext.paymentPayload as PaymentPayload, + settleContext.result, + config.x402PassDurationSeconds + ); + }); + + const httpServer = new x402HTTPResourceServer(resourceServer, routeConfig).onProtectedRequest(async (request) => { + if (!request.paymentHeader) return; + + const paymentId = getPaymentIdentifierFromHeader(request.paymentHeader); + if (paymentId && x402AccessPassStore.get(paymentId)) return { grantAccess: true }; + + // Cache misses fall through to the facilitator-backed verification flow. + // This lets restarted or separate containers rebuild local state when the facilitator accepts the receipt. + }); + + return paymentMiddlewareFromHTTPServer(httpServer, undefined, undefined, config.x402SyncFacilitatorOnStart); +} + +export const x402PaymentMiddleware = createX402PaymentMiddleware(); diff --git a/src/x402/routes.spec.ts b/src/x402/routes.spec.ts new file mode 100644 index 00000000..ec45a7f6 --- /dev/null +++ b/src/x402/routes.spec.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from 'bun:test'; +import { config } from '../config.js'; +import { createX402RouteConfig, X402_FREE_GET_ROUTES, X402_PROTECTED_GET_ROUTES } from './routes.js'; + +describe('x402 route config', () => { + it('prices every protected route as a one-hour access pass on EVM and SVM', () => { + const routeConfig = createX402RouteConfig(config) as Record; + + expect(Object.keys(routeConfig)).toHaveLength(X402_PROTECTED_GET_ROUTES.length); + expect(routeConfig['GET /v1/evm/tokens']?.accepts).toEqual([ + expect.objectContaining({ + scheme: 'exact', + price: '$0.10', + network: 'eip155:8453', + payTo: '0x49D581486438aAD93f4114084Ac5B09A8b7C9685', + }), + expect.objectContaining({ + scheme: 'exact', + price: '$0.10', + network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + payTo: 'EpRR35QnB5PfTczy3j5rp9bCw4NKzHkd8S1ubdza4my9', + }), + ]); + expect(routeConfig['GET /v1/svm/dexes']).toBeUndefined(); + expect(routeConfig['GET /v1/polymarket/markets']).toBeUndefined(); + }); + + it('tracks concrete free routes separately from protected routes', () => { + for (const route of X402_FREE_GET_ROUTES) { + expect(X402_PROTECTED_GET_ROUTES).not.toContain(route as (typeof X402_PROTECTED_GET_ROUTES)[number]); + } + }); +}); diff --git a/src/x402/routes.ts b/src/x402/routes.ts new file mode 100644 index 00000000..baed2c28 --- /dev/null +++ b/src/x402/routes.ts @@ -0,0 +1,129 @@ +import type { RouteConfig, RoutesConfig } from '@x402/core/server'; +import type { Network } from '@x402/core/types'; +import { declareDiscoveryExtension } from '@x402/extensions/bazaar'; +import { declarePaymentIdentifierExtension, PAYMENT_IDENTIFIER } from '@x402/extensions/payment-identifier'; +import type { config } from '../config.js'; + +export const X402_ACCESS_PASS_RESOURCE_PATH = '/v1/x402/access-pass/pro-1h'; + +export const X402_PROTECTED_GET_ROUTES = [ + '/v1/svm/transfers', + '/v1/svm/balances', + '/v1/svm/holders', + '/v1/svm/owner', + '/v1/svm/tokens', + '/v1/svm/transfers/native', + '/v1/svm/balances/native', + '/v1/svm/holders/native', + '/v1/svm/tokens/native', + '/v1/svm/swaps', + '/v1/svm/pools', + '/v1/svm/pools/ohlc', + '/v1/evm/transfers', + '/v1/evm/balances', + '/v1/evm/holders', + '/v1/evm/tokens', + '/v1/evm/balances/historical', + '/v1/evm/transfers/native', + '/v1/evm/balances/native', + '/v1/evm/holders/native', + '/v1/evm/tokens/native', + '/v1/evm/balances/historical/native', + '/v1/evm/swaps', + '/v1/evm/pools', + '/v1/evm/pools/ohlc', + '/v1/evm/nft/collections', + '/v1/evm/nft/holders', + '/v1/evm/nft/items', + '/v1/evm/nft/ownerships', + '/v1/evm/nft/sales', + '/v1/evm/nft/transfers', + '/v1/tvm/transfers', + '/v1/tvm/tokens', + '/v1/tvm/transfers/native', + '/v1/tvm/tokens/native', + '/v1/tvm/swaps', + '/v1/tvm/pools', + '/v1/tvm/pools/ohlc', + '/v1/polymarket/markets/ohlc', + '/v1/polymarket/markets/oi', + '/v1/polymarket/markets/activity', + '/v1/polymarket/markets/positions', + '/v1/polymarket/platform', + '/v1/polymarket/users', + '/v1/polymarket/users/positions', +] as const; + +export const X402_FREE_GET_ROUTES = [ + '/v1', + '/v1/health', + '/v1/version', + '/v1/networks', + '/v1/evm/dexes', + '/v1/svm/dexes', + '/v1/tvm/dexes', + '/v1/polymarket/markets', +] as const; + +type X402Config = Pick< + typeof config, + | 'apiUrl' + | 'x402EvmNetwork' + | 'x402SvmNetwork' + | 'x402EvmPayTo' + | 'x402SvmPayTo' + | 'x402PassDurationSeconds' + | 'x402PassPrice' +>; + +export function createX402RouteConfig(cfg: X402Config): RoutesConfig { + return Object.fromEntries( + X402_PROTECTED_GET_ROUTES.map((route) => [`GET ${route}`, createAccessPassRouteConfig(cfg)]) + ); +} + +export function isX402ProtectedRoute(method: string, path: string) { + return method.toUpperCase() === 'GET' && X402_PROTECTED_GET_ROUTES.includes(path as X402ProtectedGetRoute); +} + +type X402ProtectedGetRoute = (typeof X402_PROTECTED_GET_ROUTES)[number]; + +function createAccessPassRouteConfig(cfg: X402Config): RouteConfig { + const resource = new URL(X402_ACCESS_PASS_RESOURCE_PATH, cfg.apiUrl).toString(); + + return { + resource, + accepts: [ + { + scheme: 'exact', + price: cfg.x402PassPrice, + network: cfg.x402EvmNetwork as Network, + payTo: cfg.x402EvmPayTo, + }, + { + scheme: 'exact', + price: cfg.x402PassPrice, + network: cfg.x402SvmNetwork as Network, + payTo: cfg.x402SvmPayTo, + }, + ], + description: `One-hour Token API Pro access pass. Reuse a settled payment-identifier for ${cfg.x402PassDurationSeconds} seconds of paid API access.`, + mimeType: 'application/json', + extensions: { + [PAYMENT_IDENTIFIER]: declarePaymentIdentifierExtension(true), + ...declareDiscoveryExtension({ + input: { + query: { + network: 'mainnet', + limit: 10, + }, + }, + output: { + example: { + data: [], + }, + }, + }), + }, + }; +}