diff --git a/Cargo.lock b/Cargo.lock index 4e272b8..57d4f53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -25,7 +25,7 @@ version = "2.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a2c365c0245cbb8959de725fc2b44c754b673fdf34c9a7f9d4a25c35a7bf1" dependencies = [ - "ahash", + "ahash 0.8.12", "solana-epoch-schedule", "solana-hash", "solana-pubkey", @@ -33,6 +33,17 @@ dependencies = [ "solana-svm-feature-set", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -82,20 +93,46 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4973038846323e4e69a433916522195dce2947770076c03078fc21c80ea0f1c4" dependencies = [ - "alloy-consensus", - "alloy-contract", + "alloy-consensus 1.7.3", + "alloy-contract 1.7.3", + "alloy-core", + "alloy-eips 1.7.3", + "alloy-genesis 1.7.3", + "alloy-network 1.7.3", + "alloy-provider 1.7.3", + "alloy-rpc-client 1.7.3", + "alloy-rpc-types 1.7.3", + "alloy-serde 1.7.3", + "alloy-signer 1.7.3", + "alloy-signer-local 1.7.3", + "alloy-transport 1.7.3", + "alloy-transport-http 1.7.3", + "alloy-trie", +] + +[[package]] +name = "alloy" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1172e33a862030da77768c11cf17a1f801590de94cf518392b65207f15810c8" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-contract 2.1.0", "alloy-core", - "alloy-eips", - "alloy-genesis", - "alloy-network", - "alloy-provider", - "alloy-rpc-client", - "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-local", - "alloy-transport", - "alloy-transport-http", + "alloy-eips 2.1.0", + "alloy-genesis 2.1.0", + "alloy-network 2.1.0", + "alloy-provider 2.1.0", + "alloy-pubsub", + "alloy-rpc-client 2.1.0", + "alloy-rpc-types 2.1.0", + "alloy-serde 2.1.0", + "alloy-signer 2.1.0", + "alloy-signer-local 2.1.0", + "alloy-transport 2.1.0", + "alloy-transport-http 2.1.0", + "alloy-transport-ipc", + "alloy-transport-ws", "alloy-trie", ] @@ -116,12 +153,39 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" dependencies = [ - "alloy-eips", + "alloy-eips 1.7.3", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 1.7.3", + "alloy-trie", + "alloy-tx-macros 1.7.3", + "auto_impl", + "borsh 1.6.1", + "c-kzg", + "derive_more", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1 0.30.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b44937ce84d2cbf1ee4010667bd9214bb7db134a91dd9ffa6b1b8b1d6b030449" +dependencies = [ + "alloy-eips 2.1.0", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.1.0", "alloy-trie", - "alloy-tx-macros", + "alloy-tx-macros 2.1.0", "auto_impl", "borsh 1.6.1", "c-kzg", @@ -130,7 +194,7 @@ dependencies = [ "k256", "once_cell", "rand 0.8.5", - "secp256k1", + "secp256k1 0.30.0", "serde", "serde_json", "serde_with", @@ -143,11 +207,25 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.7.3", + "alloy-eips 1.7.3", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 1.7.3", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb0bdd61ddff726026676450e7e6a52c68aa39d98f2d60d05ad7caaea5b8100" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.1.0", "serde", ] @@ -157,27 +235,51 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.7.3", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network 1.7.3", + "alloy-network-primitives 1.7.3", + "alloy-primitives", + "alloy-provider 1.7.3", + "alloy-rpc-types-eth 1.7.3", + "alloy-sol-types", + "alloy-transport 1.7.3", + "futures", + "futures-util", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-contract" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1475c7edc6780b1759ccdb7960e28d29856ecbed47cfcf9e25be6a5f48b0cfb" +dependencies = [ + "alloy-consensus 2.1.0", "alloy-dyn-abi", "alloy-json-abi", - "alloy-network", - "alloy-network-primitives", + "alloy-network 2.1.0", + "alloy-network-primitives 2.1.0", "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-eth", + "alloy-provider 2.1.0", + "alloy-pubsub", + "alloy-rpc-types-eth 2.1.0", "alloy-sol-types", - "alloy-transport", + "alloy-transport 2.1.0", "futures", "futures-util", "serde_json", "thiserror 2.0.18", + "tracing", ] [[package]] name = "alloy-core" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e8604b0c092fabc80d075ede181c9b9e596249c70b99253082d7e689836529" +checksum = "62ddde5968de6044d67af107ad835bc0069a7ca245870b94c5958a7d8712b184" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -188,9 +290,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" +checksum = "a475bb02d9cef2dbb99065c1664ab3fe1f9352e21d6d5ed3f02cdbfc06ed1abc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -200,7 +302,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow 0.7.15", + "winnow 1.0.0", ] [[package]] @@ -237,6 +339,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "borsh 1.6.1", + "k256", "serde", "thiserror 2.0.18", ] @@ -253,6 +356,20 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-eip7928" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17201e6cbb4efbb1e510b0746839cd743a5cec3fcfb4673ea2342bd657341c93" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh 1.6.1", + "once_cell", + "serde", + "thiserror 2.0.18", +] + [[package]] name = "alloy-eips" version = "1.7.3" @@ -262,10 +379,34 @@ dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", - "alloy-eip7928", + "alloy-eip7928 0.3.3", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 1.7.3", + "auto_impl", + "borsh 1.6.1", + "c-kzg", + "derive_more", + "either", + "serde", + "serde_with", + "sha2 0.10.9", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eips" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "154b0f566ebbfc256b63c8d643c6a299f40a6fcd50a9d756c1d191487dd06483" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-eip7928 0.4.4", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.1.0", "auto_impl", "borsh 1.6.1", "c-kzg", @@ -274,7 +415,24 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", +] + +[[package]] +name = "alloy-evm" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ceeea6dcbbcd4e546b27700763a6f6c3b3fee30054209884f521078b6fda4f" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", + "alloy-hardforks", + "alloy-primitives", + "alloy-sol-types", + "auto_impl", + "derive_more", + "revm 38.0.0", "thiserror 2.0.18", + "tracing", ] [[package]] @@ -283,20 +441,48 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" dependencies = [ - "alloy-eips", + "alloy-eips 1.7.3", + "alloy-primitives", + "alloy-serde 1.7.3", + "alloy-trie", + "borsh 1.6.1", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-genesis" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a930a68a6f0ac19231bc2f5f0bf13fad3a26fa7df2525354890c72366c2998" +dependencies = [ + "alloy-eips 2.1.0", "alloy-primitives", - "alloy-serde", + "alloy-serde 2.1.0", "alloy-trie", "borsh 1.6.1", "serde", "serde_with", ] +[[package]] +name = "alloy-hardforks" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "auto_impl", + "dyn-clone", +] + [[package]] name = "alloy-json-abi" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" +checksum = "7c36c9d7f9021601b04bfef14a4b64849f6d73116a4e91e071d7fbfe10247901" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -319,22 +505,63 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-json-rpc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e92108767c8c95b5e521570e039e374e65198c4179a98d200412c083d6c26d5" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "http 1.4.0", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + [[package]] name = "alloy-network" version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", + "alloy-consensus 1.7.3", + "alloy-consensus-any 1.7.3", + "alloy-eips 1.7.3", + "alloy-json-rpc 1.7.3", + "alloy-network-primitives 1.7.3", + "alloy-primitives", + "alloy-rpc-types-any 1.7.3", + "alloy-rpc-types-eth 1.7.3", + "alloy-serde 1.7.3", + "alloy-signer 1.7.3", + "alloy-sol-types", + "async-trait", + "auto_impl", + "derive_more", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-network" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4546c9fae2861d4159ee3b25c44ab3edb90f21b849e86cf2086cacb1d56d8ac" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-consensus-any 2.1.0", + "alloy-eips 2.1.0", + "alloy-json-rpc 2.1.0", + "alloy-network-primitives 2.1.0", "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", + "alloy-rpc-types-any 2.1.0", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", + "alloy-signer 2.1.0", "alloy-sol-types", "async-trait", "auto_impl", @@ -351,18 +578,31 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.7.3", + "alloy-eips 1.7.3", + "alloy-primitives", + "alloy-serde 1.7.3", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2c325d5934445209c8d22fc67d27e2cf9fb79054b8154d9e522d6a4ee3a4f7" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", "alloy-primitives", - "alloy-serde", + "alloy-serde 2.1.0", "serde", ] [[package]] name = "alloy-primitives" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" dependencies = [ "alloy-rlp", "bytes", @@ -370,7 +610,8 @@ dependencies = [ "const-hex", "derive_more", "foldhash 0.2.0", - "hashbrown 0.16.1", + "getrandom 0.4.2", + "hashbrown 0.17.1", "indexmap 2.13.0", "itoa", "k256", @@ -381,8 +622,9 @@ dependencies = [ "rapidhash", "ruint", "rustc-hash", + "secp256k1 0.31.1", "serde", - "sha3", + "sha3 0.11.0", ] [[package]] @@ -392,18 +634,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", + "alloy-consensus 1.7.3", + "alloy-eips 1.7.3", + "alloy-json-rpc 1.7.3", + "alloy-network 1.7.3", + "alloy-network-primitives 1.7.3", + "alloy-primitives", + "alloy-rpc-client 1.7.3", + "alloy-rpc-types-eth 1.7.3", + "alloy-signer 1.7.3", + "alloy-sol-types", + "alloy-transport 1.7.3", + "alloy-transport-http 1.7.3", + "async-stream", + "async-trait", + "auto_impl", + "dashmap 6.1.0", + "either", + "futures", + "futures-utils-wasm", + "lru", + "parking_lot 0.12.5", + "pin-project", + "reqwest 0.12.28", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-provider" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4536d25780fcf51a338c26153c526c99649be1273600684ef10b397a207d4a" +dependencies = [ + "alloy-chains", + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", + "alloy-json-rpc 2.1.0", + "alloy-network 2.1.0", + "alloy-network-primitives 2.1.0", "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-signer", + "alloy-pubsub", + "alloy-rpc-client 2.1.0", + "alloy-rpc-types-anvil", + "alloy-rpc-types-debug", + "alloy-rpc-types-eth 2.1.0", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-signer 2.1.0", "alloy-sol-types", - "alloy-transport", - "alloy-transport-http", + "alloy-transport 2.1.0", + "alloy-transport-http 2.1.0", + "alloy-transport-ipc", + "alloy-transport-ws", "async-stream", "async-trait", "auto_impl", @@ -414,7 +702,7 @@ dependencies = [ "lru", "parking_lot 0.12.5", "pin-project", - "reqwest", + "reqwest 0.13.4", "serde", "serde_json", "thiserror 2.0.18", @@ -424,11 +712,33 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "alloy-pubsub" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9393d7f1fbe02ec0cff205943cf1b899200312bed0d150b2b99dc6c729203414" +dependencies = [ + "alloy-json-rpc 2.1.0", + "alloy-primitives", + "alloy-transport 2.1.0", + "auto_impl", + "bimap", + "futures", + "parking_lot 0.12.5", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "wasmtimer", +] + [[package]] name = "alloy-rlp" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -437,9 +747,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" dependencies = [ "proc-macro2", "quote", @@ -452,13 +762,13 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 1.7.3", "alloy-primitives", - "alloy-transport", - "alloy-transport-http", + "alloy-transport 1.7.3", + "alloy-transport-http 1.7.3", "futures", "pin-project", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "tokio", @@ -470,58 +780,216 @@ dependencies = [ ] [[package]] -name = "alloy-rpc-types" -version = "1.7.3" +name = "alloy-rpc-client" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" +checksum = "7f8d765656f02f993fa0565abeb3554ccd6a0d72ea44ce0558b983136639bd6b" dependencies = [ + "alloy-json-rpc 2.1.0", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-pubsub", + "alloy-transport 2.1.0", + "alloy-transport-http 2.1.0", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest 0.13.4", "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", + "wasmtimer", ] [[package]] -name = "alloy-rpc-types-any" +name = "alloy-rpc-types" version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-primitives", + "alloy-rpc-types-eth 1.7.3", + "alloy-serde 1.7.3", + "serde", ] [[package]] -name = "alloy-rpc-types-eth" -version = "1.7.3" +name = "alloy-rpc-types" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" +checksum = "2a05ef5b11fad72068e6f011c21445bc5b596ac850644c4a14c30d845ce56de8" dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-network-primitives", "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-sol-types", - "itertools 0.14.0", + "alloy-rpc-types-anvil", + "alloy-rpc-types-debug", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth 2.1.0", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde 2.1.0", "serde", - "serde_json", - "serde_with", - "thiserror 2.0.18", ] [[package]] -name = "alloy-serde" -version = "1.7.3" +name = "alloy-rpc-types-anvil" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" +checksum = "7c1fa8062c379d4fb51d28361cd02cd6a18a49fa79831c16831449b65408aa6c" dependencies = [ "alloy-primitives", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", "serde", - "serde_json", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" +dependencies = [ + "alloy-consensus-any 1.7.3", + "alloy-rpc-types-eth 1.7.3", + "alloy-serde 1.7.3", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06bfe79149dd53de5b196aa3c96db0500b7cf0ed72dbd1a31bac7660bc7cc65" +dependencies = [ + "alloy-consensus-any 2.1.0", + "alloy-network-primitives 2.1.0", + "alloy-primitives", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ff0a296b58194c7464aa56f2bdad065c5a24b43a6de5d62b8a985ae3969ecd0" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851167851209fac75c31833b3a4ec5d9c3efa2b14563605dc6a1d58f576e390c" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 2.1.0", + "derive_more", + "rand 0.8.5", + "serde", + "strum", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" +dependencies = [ + "alloy-consensus 1.7.3", + "alloy-consensus-any 1.7.3", + "alloy-eips 1.7.3", + "alloy-network-primitives 1.7.3", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 1.7.3", + "alloy-sol-types", + "itertools 0.14.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41d1a11f58b456c199e04591befc64cb236fa2574a7275a07b98b173727bd39" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-consensus-any 2.1.0", + "alloy-eips 2.1.0", + "alloy-network-primitives 2.1.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 2.1.0", + "alloy-sol-types", + "itertools 0.14.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f813a34cce29d4a340633310e5da5ac182450887333be38490c51f7776543ac2" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6149b3ef1ec411016127baeb3ff2f479290721da9f2d936b882df50d83c3133" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", + "serde", +] + +[[package]] +name = "alloy-serde" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf028ac8bcb161ad7c104243e710cece8e1a794f65ee6c35c4c4d693c8e80ee" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", ] [[package]] @@ -539,16 +1007,47 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "alloy-signer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5f392f2f56b2417ea6b74e1e116c6f35df5ab5fce4dc422e277d72ee18ff7b" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "either", + "elliptic-curve", + "k256", + "thiserror 2.0.18", +] + [[package]] name = "alloy-signer-local" version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" dependencies = [ - "alloy-consensus", - "alloy-network", + "alloy-consensus 1.7.3", + "alloy-network 1.7.3", + "alloy-primitives", + "alloy-signer 1.7.3", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-signer-local" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78de3d4ed62e2c90e16be166d0ddfe41944bb5979752703199073acf976083f2" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-network 2.1.0", "alloy-primitives", - "alloy-signer", + "alloy-signer 2.1.0", "async-trait", "k256", "rand 0.8.5", @@ -557,9 +1056,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +checksum = "840128ed2b2971d6d4668a553fe403a82683d3acc646c73e75887e7157408033" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -571,9 +1070,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +checksum = "63ec265e5d65d725175f6ca7711c970824c90ef9c0d1f1973711d4150ee612dd" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -583,16 +1082,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "sha3", + "sha3 0.11.0", "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +checksum = "89bf01077f18650876cfa682eb1f949967b5cde03f1a51c955c469d2c9b4aa67" dependencies = [ "alloy-json-abi", "const-hex", @@ -608,19 +1107,19 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" +checksum = "857b470ecdd2ed38beaf82ad1a38c516a8ff75266750f38b9eeed001d575241b" dependencies = [ "serde", - "winnow 0.7.15", + "winnow 1.0.0", ] [[package]] name = "alloy-sol-types" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +checksum = "384cf252de0db2dec52821eac037a7f57e2aa33fe5b900ce6fe39973402341f1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -634,7 +1133,30 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 1.7.3", + "auto_impl", + "base64 0.22.1", + "derive_more", + "futures", + "futures-utils-wasm", + "parking_lot 0.12.5", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f42ee1ef30d4d3d8b4ea5794937fccfc69e2bb1cd5bcf42d62afc57b049081" +dependencies = [ + "alloy-json-rpc 2.1.0", "auto_impl", "base64 0.22.1", "derive_more", @@ -657,16 +1179,71 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" dependencies = [ - "alloy-json-rpc", - "alloy-transport", + "alloy-json-rpc 1.7.3", + "alloy-transport 1.7.3", "itertools 0.14.0", - "reqwest", + "reqwest 0.12.28", "serde_json", "tower", "tracing", "url", ] +[[package]] +name = "alloy-transport-http" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee68bc7d713e033eeaaecb8fd13d5d8a41af97226c4388930ad459285b8e9f70" +dependencies = [ + "alloy-json-rpc 2.1.0", + "alloy-transport 2.1.0", + "itertools 0.14.0", + "reqwest 0.13.4", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d110e862e4869de0b3a6b7f7382cf035ab5a7e0668eb56df32efd7c39eb1a5da" +dependencies = [ + "alloy-json-rpc 2.1.0", + "alloy-pubsub", + "alloy-transport 2.1.0", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc2011694071e6dcf8ad418479ee21c4ed0e9ca20906484ea9336b99f28a59f" +dependencies = [ + "alloy-pubsub", + "alloy-transport 2.1.0", + "futures", + "http 1.4.0", + "rustls 0.23.37", + "serde_json", + "tokio", + "tokio-tungstenite 0.28.0", + "tracing", + "url", + "ws_stream_wasm", +] + [[package]] name = "alloy-trie" version = "0.9.5" @@ -695,6 +1272,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "alloy-tx-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2cd27809c88c413e5542dbd5a8eff8c12f0086af920e881ae586d3a787d5e5" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -760,17 +1349,41 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + [[package]] name = "ark-bn254" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" dependencies = [ - "ark-ec", + "ark-ec 0.4.2", "ark-ff 0.4.2", "ark-std 0.4.0", ] +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-r1cs-std", + "ark-std 0.5.0", +] + [[package]] name = "ark-ec" version = "0.4.2" @@ -778,7 +1391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ "ark-ff 0.4.2", - "ark-poly", + "ark-poly 0.4.2", "ark-serialize 0.4.2", "ark-std 0.4.0", "derivative", @@ -788,6 +1401,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash 0.8.12", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -928,22 +1562,66 @@ dependencies = [ ] [[package]] -name = "ark-serialize" -version = "0.3.0" +name = "ark-poly" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] + "ahash 0.8.12", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-relations", + "ark-std 0.5.0", + "educe", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff 0.5.0", + "ark-std 0.5.0", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] name = "ark-serialize" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ - "ark-serialize-derive", + "ark-serialize-derive 0.4.2", "ark-std 0.4.0", "digest 0.10.7", "num-bigint 0.4.6", @@ -955,6 +1633,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ + "ark-serialize-derive 0.5.0", "ark-std 0.5.0", "arrayvec", "digest 0.10.7", @@ -972,6 +1651,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -1255,6 +1945,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1272,6 +1973,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num 0.4.3", +] + [[package]] name = "auto_impl" version = "1.3.0" @@ -1289,6 +2000,29 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -1332,6 +2066,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bincode" version = "1.3.3" @@ -1395,6 +2135,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -1410,7 +2151,7 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -1432,6 +2173,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block-padding" version = "0.3.3" @@ -1598,6 +2348,28 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.25.0" @@ -1707,6 +2479,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.44" @@ -1727,7 +2510,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "inout", ] @@ -1780,6 +2563,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" version = "1.0.5" @@ -1895,7 +2693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "proptest", "serde_core", ] @@ -1906,6 +2704,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "const_format" version = "0.2.35" @@ -1976,6 +2780,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -2085,6 +2898,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -2095,6 +2917,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -2115,7 +2946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", @@ -2136,6 +2967,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + [[package]] name = "darling" version = "0.21.3" @@ -2180,6 +3024,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", "syn 2.0.117", ] @@ -2274,7 +3119,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "zeroize", ] @@ -2319,6 +3164,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-where" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -2376,11 +3232,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", - "crypto-common", + "const-oid 0.9.6", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", +] + [[package]] name = "dirs" version = "6.0.0" @@ -2436,6 +3304,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "doctest-file" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" + [[package]] name = "document-features" version = "0.2.12" @@ -2481,6 +3355,20 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" @@ -2554,6 +3442,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.1" @@ -2822,6 +3719,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -3011,6 +3914,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -3052,11 +3956,33 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap 2.13.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -3064,7 +3990,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -3079,6 +4005,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", "foldhash 0.1.5", ] @@ -3091,6 +4018,15 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", "serde", "serde_core", ] @@ -3165,6 +4101,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -3232,6 +4177,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.8.1" @@ -3242,6 +4196,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http 1.4.0", "http-body", "httparse", @@ -3288,9 +4243,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2 0.6.3", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -3509,6 +4466,21 @@ dependencies = [ "web-sys", ] +[[package]] +name = "interprocess" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069323743400cb7ab06a8fe5c1ed911d36b6919ec531661d034c89083629595b" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.61.2", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -3650,7 +4622,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", +] + +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", ] [[package]] @@ -3820,9 +4802,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "macro-string" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" dependencies = [ "proc-macro2", "quote", @@ -3862,6 +4844,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3889,6 +4877,36 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mpp" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e72c53d545bed570868bcbb47a6aa8fecf2547fe7f678b154acf60ec1e3913" +dependencies = [ + "alloy 2.1.0", + "alloy-sol-type-parser", + "alloy-sol-types", + "anyhow", + "async-trait", + "base64 0.22.1", + "hex", + "hmac 0.13.0", + "http 1.4.0", + "rand 0.10.1", + "reqwest 0.13.4", + "reqwest-middleware 0.5.2", + "serde", + "serde_json", + "serde_json_canonicalizer", + "sha2 0.11.0", + "tempo-alloy", + "tempo-contracts", + "tempo-primitives", + "thiserror 2.0.18", + "time", + "uuid", +] + [[package]] name = "nix" version = "0.29.0" @@ -3931,6 +4949,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" +dependencies = [ + "serde", +] + [[package]] name = "nonzero_ext" version = "0.3.0" @@ -4239,6 +5266,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "serdect", + "sha2 0.10.9", +] + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -4370,6 +5410,59 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.2", +] + [[package]] name = "pin-project" version = "1.1.11" @@ -4513,6 +5606,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", + "serdect", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -4592,6 +5695,26 @@ dependencies = [ "unarray", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "qstring" version = "0.7.2" @@ -4648,6 +5771,7 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "fastbloom", "getrandom 0.3.4", @@ -4742,6 +5866,17 @@ dependencies = [ "serde", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -4800,6 +5935,12 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_hc" version = "0.2.0" @@ -4824,6 +5965,7 @@ version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ + "rand 0.9.2", "rustversion", ] @@ -4856,6 +5998,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.2.16" @@ -4934,6 +6082,15 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.12.28" @@ -4974,6 +6131,47 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.37", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "reqwest-middleware" version = "0.4.2" @@ -4983,12 +6181,27 @@ dependencies = [ "anyhow", "async-trait", "http 1.4.0", - "reqwest", + "reqwest 0.12.28", "serde", "thiserror 1.0.69", "tower-service", ] +[[package]] +name = "reqwest-middleware" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc3f1384cffa4f274dad2d4ddd73aed32fed8f786d96c6be8aa4e5fd3c3b58" +dependencies = [ + "anyhow", + "async-trait", + "http 1.4.0", + "reqwest 0.13.4", + "serde", + "thiserror 2.0.18", + "tower-service", +] + [[package]] name = "reqwest-retry" version = "0.7.0" @@ -5002,8 +6215,8 @@ dependencies = [ "http 1.4.0", "hyper", "parking_lot 0.11.2", - "reqwest", - "reqwest-middleware", + "reqwest 0.12.28", + "reqwest-middleware 0.4.2", "retry-policies", "thiserror 1.0.69", "tokio", @@ -5020,6 +6233,375 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "revm" +version = "38.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91202d39dbe8e8d10e9e8f2b76c30da68ecd1d25be69ba6d853ad0d03a3a398a" +dependencies = [ + "revm-bytecode 10.0.0", + "revm-context 16.0.1", + "revm-context-interface 17.0.1", + "revm-database 13.0.1", + "revm-database-interface 11.0.1", + "revm-handler 18.1.0", + "revm-inspector 19.0.0", + "revm-interpreter 35.0.1", + "revm-precompile 34.0.0", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm" +version = "40.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823da6e5509bb8e5dcd91295870e494917a030ad506fc83301f3f08ad8b15b17" +dependencies = [ + "revm-bytecode 11.0.1", + "revm-context 18.0.3", + "revm-context-interface 19.0.3", + "revm-database 15.0.2", + "revm-database-interface 12.1.1", + "revm-handler 20.0.3", + "revm-inspector 21.0.3", + "revm-interpreter 37.0.3", + "revm-precompile 36.0.3", + "revm-primitives 24.0.1", + "revm-state 12.0.1", +] + +[[package]] +name = "revm-bytecode" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbb3a3d735efa94c91f2ef6bf20a35f99a77bc78f3e25bd758336901bdf9661" +dependencies = [ + "bitvec", + "revm-primitives 23.0.0", +] + +[[package]] +name = "revm-bytecode" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b378c2653331fe60969d9745e802cd773d82a20d8aaced914dfcf26ab8f0d9" +dependencies = [ + "bitvec", + "phf", + "revm-primitives 24.0.1", + "serde", +] + +[[package]] +name = "revm-context" +version = "16.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f68d928d8b228e0faeb1c6ed75c4fde7d124f1ddf9119b67e7a0ad4041237d" +dependencies = [ + "bitvec", + "cfg-if", + "derive-where", + "revm-bytecode 10.0.0", + "revm-context-interface 17.0.1", + "revm-database-interface 11.0.1", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-context" +version = "18.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafa298114f3cab706945de14c04e73e6e6d7896302e4183dae273f968e52f80" +dependencies = [ + "bitvec", + "cfg-if", + "derive-where", + "revm-bytecode 11.0.1", + "revm-context-interface 19.0.3", + "revm-database-interface 12.1.1", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "17.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3758e6167c4ba7a59a689c519a047edaefcd4c37d74f279b93ed87bc8aece4" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface 11.0.1", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-context-interface" +version = "19.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c13f1dfc79425931fd184b6bd373dfac7baba50859b01107d5c0e20549cbb" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface 12.1.1", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", +] + +[[package]] +name = "revm-database" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c281a1f11d3bcb8c0bba1199ed6bcb001d1aeb3d4fb366819e14f88723989a4e" +dependencies = [ + "revm-bytecode 10.0.0", + "revm-database-interface 11.0.1", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-database" +version = "15.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69c3ce73454a09ef89a66177239d7c4f5f697227ae27254c99451866603b19d" +dependencies = [ + "alloy-eips 2.1.0", + "derive_more", + "revm-bytecode 11.0.1", + "revm-database-interface 12.1.1", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", +] + +[[package]] +name = "revm-database-interface" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89efb9832a4e3742bb4ded5f7fe5bf905e8860e69427d4dfec153484fc6d304" +dependencies = [ + "auto_impl", + "either", + "revm-primitives 23.0.0", + "revm-state 11.0.1", + "thiserror 2.0.18", +] + +[[package]] +name = "revm-database-interface" +version = "12.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a2656187f9f9c22ef9dd9300ed71aeaeca3506a6a0a229a07f264649b960d68" +dependencies = [ + "auto_impl", + "either", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "revm-handler" +version = "18.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783e903d6922b7f5f9a940d1bb229530502d2924b1aed9d5ca5a94ebf065d460" +dependencies = [ + "auto_impl", + "derive-where", + "revm-bytecode 10.0.0", + "revm-context 16.0.1", + "revm-context-interface 17.0.1", + "revm-database-interface 11.0.1", + "revm-interpreter 35.0.1", + "revm-precompile 34.0.0", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-handler" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce1d66037ca1394128313bb995fa9f50d834927a389386bb34f8f0ef914648f" +dependencies = [ + "auto_impl", + "derive-where", + "revm-bytecode 11.0.1", + "revm-context 18.0.3", + "revm-context-interface 19.0.3", + "revm-database-interface 12.1.1", + "revm-interpreter 37.0.3", + "revm-precompile 36.0.3", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", +] + +[[package]] +name = "revm-inspector" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8216ad58422090d0daa9eb430e0a081f7ad07e7fd30681dee71f8420c99624e0" +dependencies = [ + "auto_impl", + "either", + "revm-context 16.0.1", + "revm-database-interface 11.0.1", + "revm-handler 18.1.0", + "revm-interpreter 35.0.1", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-inspector" +version = "21.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe3635d3411e8318849546570ca0220e783443319e28f5397c9f80b05bf4344" +dependencies = [ + "auto_impl", + "either", + "revm-context 18.0.3", + "revm-database-interface 12.1.1", + "revm-handler 20.0.3", + "revm-interpreter 37.0.3", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "35.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ece9f41b69658c15d748288a4dbdfc06a63f3ce93d983af440de3f1631dce6a" +dependencies = [ + "revm-bytecode 10.0.0", + "revm-context-interface 17.0.1", + "revm-primitives 23.0.0", + "revm-state 11.0.1", +] + +[[package]] +name = "revm-interpreter" +version = "37.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae56c57ddca1f5c4abd443f826f1b3e49a86a528e7e1ea0fc207cdc4671a37e" +dependencies = [ + "revm-bytecode 11.0.1", + "revm-context-interface 19.0.3", + "revm-primitives 24.0.1", + "revm-state 12.0.1", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "34.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a346a8cc6c8c39bd65306641c692191299c0a7b63d38810e39e8fe9b92378660" +dependencies = [ + "ark-bls12-381", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "arrayref", + "aurora-engine-modexp", + "cfg-if", + "k256", + "p256", + "revm-context-interface 17.0.1", + "revm-primitives 23.0.0", + "ripemd", + "sha2 0.10.9", +] + +[[package]] +name = "revm-precompile" +version = "36.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "191db865091e07ecb80b12ce3048192c76071ca3d2b0a315b111b271cd4ced37" +dependencies = [ + "ark-bls12-381", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "arrayref", + "aurora-engine-modexp", + "blst", + "c-kzg", + "cfg-if", + "k256", + "p256", + "revm-context-interface 19.0.3", + "revm-primitives 24.0.1", + "ripemd", + "secp256k1 0.31.1", + "sha2 0.10.9", +] + +[[package]] +name = "revm-primitives" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c99bda77d9661521ba0b4bc04558c6692074f01e65dd420fa3b893033d9b8a2" +dependencies = [ + "alloy-primitives", + "num_enum", + "once_cell", +] + +[[package]] +name = "revm-primitives" +version = "24.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5102d804892908d4ebf68da29b8562895922dffa26c230ff2c4dadcf93916f" +dependencies = [ + "alloy-primitives", + "once_cell", + "serde", +] + +[[package]] +name = "revm-state" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32490ed687dba31c3c882beb8c20408bdd30ef96690d8f145b0ee9a87040bfe" +dependencies = [ + "alloy-eip7928 0.3.3", + "bitflags 2.11.0", + "revm-bytecode 10.0.0", + "revm-primitives 23.0.0", +] + +[[package]] +name = "revm-state" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eff6067185cf80932e06f6a9c8045b012ecb6f99a8d6edc618ec2792373e14" +dependencies = [ + "alloy-eip7928 0.4.4", + "bitflags 2.11.0", + "nonmax", + "revm-bytecode 11.0.1", + "revm-primitives 24.0.1", + "serde", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -5040,10 +6622,48 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rlp" version = "0.5.2" @@ -5088,11 +6708,31 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust_decimal" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" +dependencies = [ + "arrayvec", + "borsh 1.6.1", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", + "wasm-bindgen", +] + [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +dependencies = [ + "rand 0.8.5", +] [[package]] name = "rustc-hex" @@ -5158,6 +6798,7 @@ version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ + "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", @@ -5222,7 +6863,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -5231,9 +6872,10 @@ version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -5260,6 +6902,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "ryu-js" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" + [[package]] name = "same-file" version = "1.0.6" @@ -5315,9 +6963,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", - "untrusted", + "untrusted 0.9.0", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.7.3" @@ -5341,10 +6995,21 @@ checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "secp256k1-sys", + "secp256k1-sys 0.10.1", "serde", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.2", + "secp256k1-sys 0.11.0", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -5354,6 +7019,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + [[package]] name = "secret-service" version = "4.0.0" @@ -5433,6 +7107,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.228" @@ -5488,6 +7168,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap 2.13.0", "itoa", "memchr", "serde", @@ -5495,6 +7176,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_json_canonicalizer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe52319a927259afbfa5180c5157cd8167edfd3e8c254f9558c7fef44c5649f2" +dependencies = [ + "ryu-js", + "serde", + "serde_json", +] + [[package]] name = "serde_repr" version = "0.1.20" @@ -5575,7 +7267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -5587,7 +7279,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.9.0", "opaque-debug", ] @@ -5599,10 +7291,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "sha3" version = "0.10.8" @@ -5610,7 +7313,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.7", - "keccak", + "keccak 0.1.6", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", ] [[package]] @@ -5686,6 +7399,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "0.3.11" @@ -5846,8 +7565,8 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" dependencies = [ - "ark-bn254", - "ark-ec", + "ark-bn254 0.4.0", + "ark-ec 0.4.2", "ark-ff 0.4.2", "ark-serialize 0.4.2", "bytemuck", @@ -6151,7 +7870,7 @@ version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93b93971e289d6425f88e6e3cb6668c4b05df78b3c518c249be55ced8efd6b6d" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "solana-epoch-schedule", "solana-hash", @@ -6292,7 +8011,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" dependencies = [ - "sha3", + "sha3 0.10.8", "solana-define-syscall", "solana-hash", "solana-sanitize", @@ -6425,7 +8144,7 @@ dependencies = [ "crossbeam-channel", "gethostname", "log", - "reqwest", + "reqwest 0.12.28", "solana-cluster-type", "solana-sha256-hasher", "solana-time-utils", @@ -6530,7 +8249,7 @@ version = "2.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37192c0be5c222ca49dbc5667288c5a8bb14837051dd98e541ee4dad160a5da9" dependencies = [ - "ahash", + "ahash 0.8.12", "bincode", "bv", "bytes", @@ -6785,8 +8504,8 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-stream", - "tokio-tungstenite", - "tungstenite", + "tokio-tungstenite 0.20.1", + "tungstenite 0.20.1", "url", ] @@ -6913,8 +8632,8 @@ dependencies = [ "futures", "indicatif", "log", - "reqwest", - "reqwest-middleware", + "reqwest 0.12.28", + "reqwest-middleware 0.4.2", "semver 1.0.27", "serde", "serde_derive", @@ -6948,8 +8667,8 @@ checksum = "2dbc138685c79d88a766a8fd825057a74ea7a21e1dd7f8de275ada899540fff7" dependencies = [ "anyhow", "jsonrpc-core", - "reqwest", - "reqwest-middleware", + "reqwest 0.12.28", + "reqwest-middleware 0.4.2", "serde", "serde_derive", "serde_json", @@ -7114,7 +8833,7 @@ dependencies = [ "libsecp256k1", "serde", "serde_derive", - "sha3", + "sha3 0.10.8", "solana-feature-set", "solana-instruction", "solana-precompile-error", @@ -7754,6 +9473,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -7778,9 +9503,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +checksum = "ec005042c7d952febc1a3ef5b0f6674e9054aa836877a31c90b20e25b3d31744" dependencies = [ "paste", "proc-macro2", @@ -7820,6 +9545,27 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -7839,6 +9585,94 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tempo-alloy" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626a6c06f10c984050d9b434e9254eaeeb5211df9f17e3fb22ac2cc7e6f3e1a1" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-contract 2.1.0", + "alloy-eips 2.1.0", + "alloy-json-rpc 2.1.0", + "alloy-network 2.1.0", + "alloy-primitives", + "alloy-provider 2.1.0", + "alloy-rpc-client 2.1.0", + "alloy-rpc-types-eth 2.1.0", + "alloy-serde 2.1.0", + "alloy-signer-local 2.1.0", + "alloy-sol-types", + "alloy-transport 2.1.0", + "async-trait", + "dashmap 6.1.0", + "derive_more", + "futures", + "http 1.4.0", + "reqwest 0.13.4", + "serde", + "serde_json", + "tempo-chainspec", + "tempo-contracts", + "tempo-primitives", + "tower", + "tracing", +] + +[[package]] +name = "tempo-chainspec" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b2528d06c6d0f5222589960ff6cde43c0d7028d78756574c7f256d0443cb1de" +dependencies = [ + "alloy-eips 2.1.0", + "alloy-evm", + "alloy-genesis 2.1.0", + "alloy-hardforks", + "alloy-primitives", + "once_cell", + "paste", + "serde", + "serde_json", + "tempo-primitives", +] + +[[package]] +name = "tempo-contracts" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fe2453626530970243c4161d31cc1431abe4551ad4ffdc8aa86b7c197b1c13" +dependencies = [ + "alloy-contract 2.1.0", + "alloy-primitives", + "alloy-sol-types", + "serde", +] + +[[package]] +name = "tempo-primitives" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632bc140391f38f10aa6c0aeb8adf36798db3c5f5f5241a4b950779d0b72ef2e" +dependencies = [ + "alloy-consensus 2.1.0", + "alloy-eips 2.1.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 2.1.0", + "aws-lc-rs", + "base64 0.22.1", + "derive_more", + "ed25519-consensus", + "once_cell", + "p256", + "revm 40.0.3", + "serde", + "serde_json", + "sha2 0.10.9", + "tempo-contracts", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -8039,10 +9873,26 @@ dependencies = [ "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", - "tungstenite", + "tungstenite 0.20.1", "webpki-roots 0.25.4", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.37", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tungstenite 0.28.0", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -8230,6 +10080,15 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" @@ -8275,11 +10134,30 @@ dependencies = [ "webpki-roots 0.24.0", ] +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.2", + "rustls 0.23.37", + "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -8295,7 +10173,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8340,6 +10218,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -8483,6 +10367,7 @@ dependencies = [ "cfg-if", "once_cell", "rustversion", + "serde", "wasm-bindgen-macro", "wasm-bindgen-shared", ] @@ -8640,6 +10525,15 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + [[package]] name = "webpki-roots" version = "1.0.6" @@ -8649,6 +10543,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" @@ -8721,6 +10621,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -9082,6 +10993,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "ws_stream_wasm" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper", + "thiserror 2.0.18", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wyz" version = "0.5.1" @@ -9091,6 +11021,60 @@ dependencies = [ "tap", ] +[[package]] +name = "x402-chain-eip155" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ae3d2a41660dd49fffc887623c69f19399a2340766da92653713f4ff953960" +dependencies = [ + "alloy-contract 2.1.0", + "alloy-primitives", + "alloy-provider 2.1.0", + "alloy-signer 2.1.0", + "alloy-signer-local 2.1.0", + "alloy-sol-types", + "async-trait", + "rand 0.10.1", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", + "tracing-core", + "url", + "x402-types", +] + +[[package]] +name = "x402-reqwest" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "019a114533f98f35b9bbb7f6a798ace3dc0239564cbe47c4d62516cef2cf1fcc" +dependencies = [ + "async-trait", + "http 1.4.0", + "reqwest 0.13.4", + "reqwest-middleware 0.5.2", + "serde_json", + "x402-types", +] + +[[package]] +name = "x402-types" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c0207f03ddd25e5eec4f75590deaf1b50bdeee96c7d958343eb7602a7a3a9cf" +dependencies = [ + "alloy-primitives", + "async-trait", + "base64 0.22.1", + "rust_decimal", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", + "tracing", +] + [[package]] name = "x509-parser" version = "0.14.0" @@ -9208,8 +11192,9 @@ dependencies = [ name = "zero-x-cli" version = "0.1.0" dependencies = [ - "alloy", + "alloy 1.7.3", "alloy-dyn-abi", + "alloy-signer-local 2.1.0", "assert_cmd", "base64 0.22.1", "bigdecimal", @@ -9225,10 +11210,12 @@ dependencies = [ "hex", "indicatif", "keyring", + "mpp", "openssl", "predicates", - "reqwest", - "reqwest-middleware", + "reqwest 0.12.28", + "reqwest 0.13.4", + "reqwest-middleware 0.4.2", "reqwest-retry", "serde", "serde_json", @@ -9240,8 +11227,11 @@ dependencies = [ "tokio", "toml 0.8.23", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.23", "uuid", + "x402-chain-eip155", + "x402-reqwest", + "x402-types", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 878b8a7..f3a30bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,18 @@ keyring = { version = "3", features = [ "crypto-rust", "async-io", ] } +x402-reqwest = { version = "2.0.1", default-features = false, features = ["json"] } +mpp = { version = "0.10.4", default-features = false, features = ["client", "tempo", "middleware", "reqwest-rustls-tls"] } +# `telemetry` pulls `tracing` (the v2_eip155_upto client references it +# unconditionally, so `client` alone won't compile). The CLI already uses tracing. +x402-chain-eip155 = { version = "2.0.1", features = ["client", "telemetry"] } +alloy-signer-local = "2" +x402-types = { version = "2.0.1", default-features = false } +# The payment crates (x402-reqwest, mpp) are built on reqwest 0.13, while the +# keyed API client (src/api) stays on 0.12. Alias the 0.13 line under a second +# name so the payment adapters speak the same reqwest types those crates expose +# without forcing a crate-wide HTTP upgrade. +reqwest_payments = { package = "reqwest", version = "0.13", default-features = false, features = ["rustls", "query"] } [dev-dependencies] assert_cmd = "2" diff --git a/README.md b/README.md index 22bd38b..120f350 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ Trade tokens across Solana and EVM chains from your terminal. Built for both hum ## Quick Start ```bash -# Build from source -cargo install --path . +# Install (macOS / Linux) — see Installation below for Windows / from source +curl -fsSL https://raw.githubusercontent.com/0xProject/0x-cli/main/scripts/install.sh | sh # Configure 0x config init @@ -27,6 +27,7 @@ cargo install --path . - **4 APIs**: EVM Swap (Allowance Holder), Gasless Swap, Solana Swap, Cross-Chain - **21 chains**: Ethereum, Base, Arbitrum, Optimism, Polygon, BSC, Avalanche, Linea, Scroll, Blast, Mantle, Berachain, Sonic, Unichain, World Chain, Abstract, Ink, Monad, HyperEVM, Solana, Tron - **Agent-first**: Auto-detect non-TTY for JSON output, structured error codes, stable exit codes, inline `RESPONSE:` schemas in every `--help` +- **Pay-per-request (no API key)**: `--pay x402-evm` or `--pay mpp` settles ~$0.01 USDC per request via the 0x agent gateway — for autonomous agents with a funded wallet - **Safe by default**: OS keyring for wallet secrets, transaction simulation before every execution, `--dry-run` mode, exact token approvals - **Rich UX**: Colored tables, progress spinners, interactive confirmation, shell completions @@ -46,10 +47,10 @@ Pin a specific version or change the install directory: ```bash # Install a specific version -curl -fsSL https://raw.githubusercontent.com/0xProject/0x-cli/main/scripts/install.sh | ZEROX_VERSION=v0.1.0 sh +curl -fsSL https://raw.githubusercontent.com/0xProject/0x-cli/main/scripts/install.sh | ZEROEX_VERSION=v0.1.0 sh # Install somewhere on your PATH -curl -fsSL https://raw.githubusercontent.com/0xProject/0x-cli/main/scripts/install.sh | ZEROX_BIN_DIR=/usr/local/bin sh +curl -fsSL https://raw.githubusercontent.com/0xProject/0x-cli/main/scripts/install.sh | ZEROEX_BIN_DIR=/usr/local/bin sh ``` On **Windows**, download the `.zip` for `x86_64-pc-windows-msvc` from the @@ -119,10 +120,10 @@ By default, `wallet.evm` and `wallet.solana` (when given key material rather tha | `0x config set wallet.evm --plaintext` | `~/.0x-config/config.toml` | | `0x config set wallet.solana /path/to/file.json` | `~/.0x-config/config.toml` (it's a path) | | `0x config set wallet.solana ` | OS keyring | -| `ZEROX_EVM_PRIVATE_KEY` / `ZEROX_SOLANA_KEYPAIR` env var | Read directly, never persisted | +| `ZEROEX_EVM_PRIVATE_KEY` / `ZEROEX_SOLANA_KEYPAIR` env var | Read directly, never persisted | | `0x config set wallet.tron ` | OS keyring | | `0x config set wallet.tron --plaintext` | `~/.0x-config/config.toml` | -| `ZEROX_TRON_PRIVATE_KEY` env var | Read directly, never persisted | +| `ZEROEX_TRON_PRIVATE_KEY` env var | Read directly, never persisted | `0x config show` reports keyring-stored wallets as ``. If the OS keyring is unavailable (e.g. headless Linux with no DBus), use `--plaintext` or the env vars. @@ -132,13 +133,13 @@ Environment variables always take precedence over config file values. | Variable | Description | |----------|-------------| -| `ZEROX_API_KEY` | 0x API key | -| `ZEROX_EVM_PRIVATE_KEY` | EVM private key (hex) | -| `ZEROX_SOLANA_KEYPAIR` | Solana keypair file path or base58 | -| `ZEROX_TRON_PRIVATE_KEY` | Tron private key (hex) | -| `ZEROX_DEFAULT_CHAIN` | Default chain name or ID | -| `ZEROX_RPC_URL` | Override RPC URL for any chain | -| `ZEROX_TELEMETRY` | Set falsy (`0`/`false`/`off`) to disable usage telemetry | +| `ZEROEX_API_KEY` | 0x API key | +| `ZEROEX_EVM_PRIVATE_KEY` | EVM private key (hex) | +| `ZEROEX_SOLANA_KEYPAIR` | Solana keypair file path or base58 | +| `ZEROEX_TRON_PRIVATE_KEY` | Tron private key (hex) | +| `ZEROEX_DEFAULT_CHAIN` | Default chain name or ID | +| `ZEROEX_RPC_URL` | Override RPC URL for any chain | +| `ZEROEX_TELEMETRY` | Set falsy (`0`/`false`/`off`) to disable usage telemetry | | `DO_NOT_TRACK` | Set to `1` to disable usage telemetry | | `NO_COLOR` | Disable colored output | @@ -277,6 +278,42 @@ Nothing to configure — the keypair lives in memory only. 0x status 0xdef456... --type cross-chain --chain base --poll --poll-interval 10 ``` +### Pay per request (x402 / MPP) + +Instead of a `0x-api-key`, `price` and `swap` can pay **~$0.01 USDC per request** +through the 0x agent gateway — useful for an autonomous agent with a funded wallet +and no key. Add `--pay` with one of two payment rails: + +| `--pay` | Rail | Wallet needs | +|---------|------|--------------| +| `x402-evm` | [x402](https://www.x402.org) over Base — signs an EIP-3009 USDC authorization (off-chain, no gas) | USDC on Base | +| `mpp` | [MPP](https://mpp.dev) over Tempo (chainId 4217) — broadcasts a USDC.e transfer | USDC.e **and** native gas on Tempo | + +```bash +# Pay for a price check with x402 (EVM wallet signs; no API key needed) +0x price --chain base --sell 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \ + --buy 0x4200000000000000000000000000000000000006 --amount 1000000 \ + --pay x402-evm --max-payment 0.05 + +# Pay for a swap quote via MPP/Tempo, then execute on-chain as usual +0x swap --chain base --sell 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \ + --buy 0x4200000000000000000000000000000000000006 --amount 1000000 \ + --pay mpp --yes +``` + +- **EVM AllowanceHolder only.** Rejected (free, `INPUT_INVALID`) with `--gasless`, + Solana/Tron, or `--buy-amount` (exact-out). +- **`--max-payment `** (default `0.05`) caps the spend. If the gateway asks for + more, the CLI refuses **before signing/broadcasting** — `PAYMENT_EXCEEDS_LIMIT` + (exit 41), nothing spent. +- **The payment is real, non-refundable money** charged per request — including when + a later on-chain swap reverts. Don't poll `price --pay` in a loop. The settlement + (tx hash, payer, amount) is reported under `metadata.payment` in JSON output. +- `swap --pay` pays only for the **quote**; the on-chain swap still uses your wallet + and RPC. See exit codes 40–44 and the `PAYMENT_*` rows under [Error Codes](#error-codes). +- For `--pay mpp`, point the Tempo RPC wherever you like with `--tempo-rpc ` or + `0x config set rpc.tempo ` (defaults to `https://rpc.tempo.xyz`). + ## AI Agent Integration The CLI is designed as a first-class tool for AI agents and scripts. @@ -350,7 +387,7 @@ On error: "message": "No API key configured", "category": "config", "retryable": false, - "suggestion": "Run '0x config set api_key ' or set ZEROX_API_KEY env var" + "suggestion": "Run '0x config set api_key ' or set ZEROEX_API_KEY env var" } } ``` @@ -458,7 +495,7 @@ Every interactive prompt has a flag equivalent: - **Redaction**: `0x config show` and `0x config get` never reveal secret material. Wallets stored in the keyring show as ``; plaintext wallets show as `***redacted***`; Solana file paths show verbatim because the path itself isn't sensitive. - **Transaction simulation**: EVM and Solana transactions are simulated via `eth_call` or `simulate_transaction` before submission. Tron cross-chain transactions are not pre-simulated. - **Approval strategy**: Default is `exact` (only approve the needed amount). Use `--approval unlimited` for max approval. -- **Environment variables**: Sensitive values like private keys can be set via env vars (`ZEROX_EVM_PRIVATE_KEY`, `ZEROX_SOLANA_KEYPAIR`) to avoid persisting them at all — read-once, never written to disk or keyring. +- **Environment variables**: Sensitive values like private keys can be set via env vars (`ZEROEX_EVM_PRIVATE_KEY`, `ZEROEX_SOLANA_KEYPAIR`) to avoid persisting them at all — read-once, never written to disk or keyring. ## Telemetry @@ -485,7 +522,7 @@ The CLI sends **anonymous, opt-out** usage statistics (via Amplitude) to help us ```bash 0x config set telemetry.enabled false # persistent -export ZEROX_TELEMETRY=0 # per-shell +export ZEROEX_TELEMETRY=0 # per-shell export DO_NOT_TRACK=1 # cross-tool standard ``` diff --git a/scripts/install.sh b/scripts/install.sh index c84961d..34ab0bf 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -7,13 +7,13 @@ # checksum, and installs it to a bin directory on your PATH. # # Knobs (all optional, set as environment variables): -# ZEROX_VERSION version to install, e.g. v0.1.0 (default: latest release) -# ZEROX_BIN_DIR install directory (default: ~/.local/bin) +# ZEROEX_VERSION version to install, e.g. v0.1.0 (default: latest release) +# ZEROEX_BIN_DIR install directory (default: ~/.local/bin) # # Examples: # curl -fsSL .../install.sh | sh -# curl -fsSL .../install.sh | ZEROX_VERSION=v0.1.0 sh -# curl -fsSL .../install.sh | ZEROX_BIN_DIR=/usr/local/bin sh +# curl -fsSL .../install.sh | ZEROEX_VERSION=v0.1.0 sh +# curl -fsSL .../install.sh | ZEROEX_BIN_DIR=/usr/local/bin sh set -eu @@ -82,7 +82,7 @@ esac target="${cpu}-${os_name}" # --- resolve version -------------------------------------------------------- -version="${ZEROX_VERSION:-}" +version="${ZEROEX_VERSION:-}" if [ -z "$version" ]; then info "${DIM}Resolving latest release...${RESET}" # The /releases/latest page 302-redirects to /releases/tag/; parse @@ -96,7 +96,7 @@ if [ -z "$version" ]; then | awk '/^[[:space:]]*Location:/ {print $2}' | tail -n1)" fi version="${loc##*/}" - [ -n "$version" ] || err "could not determine the latest version; set ZEROX_VERSION=vX.Y.Z" + [ -n "$version" ] || err "could not determine the latest version; set ZEROEX_VERSION=vX.Y.Z" fi case "$version" in v*) ;; *) version="v$version" ;; esac @@ -148,11 +148,11 @@ binpath="$tmp/${BIN}-${target}/${BIN}" chmod +x "$binpath" # --- choose install dir ----------------------------------------------------- -bin_dir="${ZEROX_BIN_DIR:-$HOME/.local/bin}" +bin_dir="${ZEROEX_BIN_DIR:-$HOME/.local/bin}" mkdir -p "$bin_dir" 2>/dev/null || err "cannot create install dir: $bin_dir" if [ ! -w "$bin_dir" ]; then err "install dir is not writable: $bin_dir -Re-run with a writable dir, e.g. ZEROX_BIN_DIR=\$HOME/.local/bin +Re-run with a writable dir, e.g. ZEROEX_BIN_DIR=\$HOME/.local/bin or with elevated permissions for a system path." fi diff --git a/skills/0x-trade/SKILL.md b/skills/0x-trade/SKILL.md index d598c6c..09f658d 100644 --- a/skills/0x-trade/SKILL.md +++ b/skills/0x-trade/SKILL.md @@ -101,11 +101,29 @@ On error, `status` is `"error"` and a structured `error` object replaces `data`: | 11 | Transaction reverted on-chain | Don't retry as-is; explain to the user. | | 12 | Pending / timed out — tx may still land | Poll with `0x status` (see references). | | 20 | User declined the confirmation prompt | Stop; don't re-run. | +| 40 | Agent-payment challenge invalid (`--pay`) — no payable scheme offered | Nothing spent; report it. | +| 41 | Payment exceeded `--max-payment` — refused before signing | **Nothing spent.** Only raise the cap if the amount is expected. | +| 42 | Payment signing failed | Verify the payment wallet; nothing spent. | +| 43 | Payment settlement failed | **Money may have been spent** with no usable result — don't blindly retry; check the wallet. | +| 44 | Payment wallet unfunded (USDC/USDC.e or native gas) | Fund the payment wallet. | | 25 | Preview emitted, confirmation required (`--yes` missing) | Show the quote to the user, or re-run with `--yes`. | | 30 | Dry-run completed | Report the simulated result. | The full error-code catalog (code → category → retryable → action) is in `references/errors.md`. +## Paying per request instead of an API key (`--pay`) + +`price` and `swap` accept `--pay ` to pay ~$0.01 in USDC per request through the 0x agent gateway instead of using an API key — useful for an autonomous agent with a funded wallet and no key. + +- **EVM AllowanceHolder only.** Rejected (`INPUT_INVALID`) with `--gasless`, Solana, or Tron. +- **`x402-evm`**: signs an EIP-3009 USDC authorization on Base; needs USDC in the EVM wallet. +- **`mpp`**: broadcasts a USDC.e transfer on Tempo (chainId 4217); needs USDC.e **and** native gas there. Override the RPC with `--tempo-rpc` / `ZEROEX_TEMPO_RPC_URL`. +- **`--max-payment `** (default `0.05`) caps the spend; the CLI refuses *before* signing/broadcasting if the gateway asks for more (`PAYMENT_EXCEEDS_LIMIT`, exit 41 — nothing spent). +- **Every paid request costs real, non-refundable money**, including when the on-chain swap later reverts. Don't poll `price --pay` in a loop. The settlement (tx hash, payer, amount) is in `metadata.payment`. +- `swap --pay` pays only for the *quote*; the on-chain swap still uses your wallet + RPC as usual. + +See the `PAYMENT_*` rows in `references/errors.md` (exit 40–44) for recovery — exit 41 means nothing was spent; exit 43 means money may have moved without a usable result. + ## Going deeper — read on demand Read the matching reference only when the task needs it: diff --git a/skills/0x-trade/references/config.md b/skills/0x-trade/references/config.md index af56aee..b115fe1 100644 --- a/skills/0x-trade/references/config.md +++ b/skills/0x-trade/references/config.md @@ -45,15 +45,15 @@ Non-interactive (agent-driven) setup: | Var | Overrides | |-----|-----------| -| `ZEROX_API_KEY` | `api_key` | -| `ZEROX_EVM_PRIVATE_KEY` | `wallet.evm` | -| `ZEROX_SOLANA_KEYPAIR` | `wallet.solana` (path or base58) | -| `ZEROX_TRON_PRIVATE_KEY` | `wallet.tron` | -| `ZEROX_DEFAULT_CHAIN` | `defaults.chain` | -| `ZEROX_RPC_URL` | RPC for the current command | -| `ZEROX_OUTPUT` | `-o/--output` format | -| `ZEROX_PROFILE` | Config profile to use (overrides active_profile) | -| `ZEROX_TELEMETRY` | Set falsy (`0`/`false`/`off`) to disable usage telemetry | +| `ZEROEX_API_KEY` | `api_key` | +| `ZEROEX_EVM_PRIVATE_KEY` | `wallet.evm` | +| `ZEROEX_SOLANA_KEYPAIR` | `wallet.solana` (path or base58) | +| `ZEROEX_TRON_PRIVATE_KEY` | `wallet.tron` | +| `ZEROEX_DEFAULT_CHAIN` | `defaults.chain` | +| `ZEROEX_RPC_URL` | RPC for the current command | +| `ZEROEX_OUTPUT` | `-o/--output` format | +| `ZEROEX_PROFILE` | Config profile to use (overrides active_profile) | +| `ZEROEX_TELEMETRY` | Set falsy (`0`/`false`/`off`) to disable usage telemetry | | `DO_NOT_TRACK` | Set to `1` to disable usage telemetry (cross-tool standard) | | `NO_COLOR` | Disables colored output | @@ -78,8 +78,8 @@ section. ``` When a profile is active, every API command prints `Profile '' → ` on -stderr. The banner is suppressed by --quiet. `ZEROX_PROFILE` selects a profile per-environment; `--api-key` / -`ZEROX_API_KEY` still beat the profile's key. +stderr. The banner is suppressed by --quiet. `ZEROEX_PROFILE` selects a profile per-environment; `--api-key` / +`ZEROEX_API_KEY` still beat the profile's key. ## Telemetry @@ -87,5 +87,5 @@ The CLI sends anonymous usage stats (which command ran, exit code, duration, cha - **Never sent:** token addresses, amounts, transaction/trade hashes, wallet addresses, API keys, RPC URLs, error messages, or IP. - **Identifier:** a random `telemetry.install_id` (a UUID, not a device/hardware fingerprint), shown in `config show`. -- **Opt out** any of three ways: `0x config set telemetry.enabled false`, `ZEROX_TELEMETRY=0`, or `DO_NOT_TRACK=1`. +- **Opt out** any of three ways: `0x config set telemetry.enabled false`, `ZEROEX_TELEMETRY=0`, or `DO_NOT_TRACK=1`. - Events spool to `~/.0x-config/telemetry-queue.jsonl` and flush in the background, so they never add latency to a command. diff --git a/skills/0x-trade/references/cross-chain.md b/skills/0x-trade/references/cross-chain.md index 2f1f0ff..ba24852 100644 --- a/skills/0x-trade/references/cross-chain.md +++ b/skills/0x-trade/references/cross-chain.md @@ -2,7 +2,7 @@ Swap a token on one chain for a token on another in a single command. Supports EVM↔EVM, EVM↔Solana, and EVM↔Tron. The CLI fetches multiple bridge quotes, executes the origin-chain transaction, and can track the bridge until funds land on the destination. -**Tron:** Tron addresses are base58check (`T…`). A Tron wallet (`wallet.tron` / `ZEROX_TRON_PRIVATE_KEY`) is required to bridge from or to Tron. Tron is bridging-only — it is not available in `swap`, `price`, or `gasless`. +**Tron:** Tron addresses are base58check (`T…`). A Tron wallet (`wallet.tron` / `ZEROEX_TRON_PRIVATE_KEY`) is required to bridge from or to Tron. Tron is bridging-only — it is not available in `swap`, `price`, or `gasless`. ## Quote + execute diff --git a/skills/0x-trade/references/errors.md b/skills/0x-trade/references/errors.md index 5926d53..1a21160 100644 --- a/skills/0x-trade/references/errors.md +++ b/skills/0x-trade/references/errors.md @@ -8,7 +8,7 @@ Every error carries: `code` (stable), `category`, `retryable`, `message`, and us |--------------|----------|-----:|:---------:|----------| | `CONFIG_NOT_FOUND` | config | 3 | no | Run `0x config init`. | | `CONFIG_INVALID` | config | 3 | no | Inspect `~/.0x-config/config.toml`; fix or re-init. | -| `API_KEY_MISSING` | config | 5 | no | `0x config set api_key ` or `ZEROX_API_KEY`. | +| `API_KEY_MISSING` | config | 5 | no | `0x config set api_key ` or `ZEROEX_API_KEY`. | | `WALLET_NOT_FOUND` | config | 3 | no | `0x config set wallet.evm ` / `wallet.solana `. | | `WALLET_INVALID` | config | 3 | no | The stored key/keypair doesn't parse — re-set it. | | `KEYRING_UNAVAILABLE` | config | 3 | no | No OS keyring (headless Linux) — re-set the secret with `--plaintext` or use env vars. | @@ -32,6 +32,11 @@ Every error carries: `code` (stable), `category`, `retryable`, `message`, and us | `BRIDGE_FAILED` | bridge | 1 | no | Check `data.failure_reason` via `0x status`. | | `BRIDGE_TIMEOUT` | bridge | 12 | yes | Bridge in flight — keep polling `0x status --type cross-chain`. | | `USER_CANCELLED` | input | 20 | no | The user said no. Stop. | +| `PAYMENT_CHALLENGE_INVALID` | payment | 40 | no | The agent gateway's 402 offered no scheme the CLI can pay (`--pay`). Nothing spent — report it. | +| `PAYMENT_EXCEEDS_LIMIT` | payment | 41 | no | Required payment exceeded `--max-payment`; the CLI refused to sign. **Nothing spent.** Raise the cap only if the amount is expected. | +| `PAYMENT_SIGNING_FAILED` | payment | 42 | no | Signing the x402 payment authorization failed — verify the configured payment wallet. Nothing spent. | +| `PAYMENT_SETTLEMENT_FAILED` | payment | 43 | no | Payment submitted but rejected/failed on settlement. **Money may have been spent** without a usable response — do not blindly retry; check the wallet. | +| `PAYMENT_WALLET_UNFUNDED` | payment | 44 | no | Payment wallet lacks USDC/USDC.e (or native gas for an MPP push). Fund it. | ## SIMULATION_FAILED (exit 10) — the special case diff --git a/skills/0x-trade/references/solana.md b/skills/0x-trade/references/solana.md index d479aad..e458318 100644 --- a/skills/0x-trade/references/solana.md +++ b/skills/0x-trade/references/solana.md @@ -24,7 +24,7 @@ Read-only — no keypair needed, no keychain prompt. --yes -o json-envelope ``` -Requires a Solana keypair: `0x config set wallet.solana ` or `ZEROX_SOLANA_KEYPAIR`. The transaction is simulated before submission; simulation failures exit 10 with the first simulation logs in `error.details.simulation_logs`. +Requires a Solana keypair: `0x config set wallet.solana ` or `ZEROEX_SOLANA_KEYPAIR`. The transaction is simulated before submission; simulation failures exit 10 with the first simulation logs in `error.details.simulation_logs`. ## Flag differences vs EVM (important) diff --git a/src/cli.rs b/src/cli.rs index 0b7607d..65ccbc6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,13 +10,13 @@ use clap_complete::Shell; Supports EVM swaps, gasless swaps, Solana swaps, and cross-chain\n\ swaps. Designed for both human traders and AI agents.", after_help = "ENVIRONMENT VARIABLES:\n\ - \x20 ZEROX_API_KEY 0x API key (overrides config)\n\ - \x20 ZEROX_EVM_PRIVATE_KEY EVM private key (overrides wallet config)\n\ - \x20 ZEROX_SOLANA_KEYPAIR Solana keypair path or base58 (overrides wallet config)\n\ - \x20 ZEROX_DEFAULT_CHAIN Default chain (overrides config)\n\ - \x20 ZEROX_RPC_URL RPC URL (overrides config)\n\ - \x20 ZEROX_PROFILE Config profile to use (overrides active_profile)\n\ - \x20 ZEROX_TELEMETRY Set falsy (0/false/off) to disable usage telemetry\n\ + \x20 ZEROEX_API_KEY 0x API key (overrides config)\n\ + \x20 ZEROEX_EVM_PRIVATE_KEY EVM private key (overrides wallet config)\n\ + \x20 ZEROEX_SOLANA_KEYPAIR Solana keypair path or base58 (overrides wallet config)\n\ + \x20 ZEROEX_DEFAULT_CHAIN Default chain (overrides config)\n\ + \x20 ZEROEX_RPC_URL RPC URL (overrides config)\n\ + \x20 ZEROEX_PROFILE Config profile to use (overrides active_profile)\n\ + \x20 ZEROEX_TELEMETRY Set falsy (0/false/off) to disable usage telemetry\n\ \x20 DO_NOT_TRACK Set to 1 to disable usage telemetry\n\ \x20 NO_COLOR Disable colored output\n\n\ CONFIG:\n\ @@ -45,7 +45,7 @@ pub struct Cli { pub command: Commands, /// Output format (auto-detects: human for TTY, json-envelope otherwise) - #[arg(short = 'o', long, global = true, value_enum, env = "ZEROX_OUTPUT")] + #[arg(short = 'o', long, global = true, value_enum, env = "ZEROEX_OUTPUT")] pub output: Option, /// Skip all confirmation prompts @@ -65,15 +65,15 @@ pub struct Cli { pub dry_run: bool, /// Override the configured API key - #[arg(long, global = true, env = "ZEROX_API_KEY", hide_env = true)] + #[arg(long, global = true, env = "ZEROEX_API_KEY", hide_env = true)] pub api_key: Option, /// Override the RPC URL for this command - #[arg(long, global = true, env = "ZEROX_RPC_URL", hide_env = true)] + #[arg(long, global = true, env = "ZEROEX_RPC_URL", hide_env = true)] pub rpc_url: Option, /// Use a named config profile for this command (see '0x config set') - #[arg(long, global = true, env = "ZEROX_PROFILE", hide_env = true)] + #[arg(long, global = true, env = "ZEROEX_PROFILE", hide_env = true)] pub profile: Option, /// Wallet path or name to use @@ -330,6 +330,28 @@ pub enum SkillAction { }, } +/// Agent-payment rail for `--pay`: pay per request through the 0x agent +/// gateway instead of using an API key. +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum PaymentArg { + /// x402 over Base/EVM (USDC, EIP-3009 signature). + #[value(name = "x402-evm")] + X402Evm, + /// MPP over Tempo mainnet (USDC.e, on-chain push). + #[value(name = "mpp")] + Mpp, +} + +impl PaymentArg { + /// Map the CLI flag to the payment-module method. + pub fn method(self) -> crate::payment::PaymentMethod { + match self { + Self::X402Evm => crate::payment::PaymentMethod::X402Evm, + Self::Mpp => crate::payment::PaymentMethod::MppTempo, + } + } +} + #[derive(Debug, Clone, Copy, ValueEnum)] pub enum SkillTopic { /// Gasless swaps and trade-hash polling @@ -509,7 +531,7 @@ impl OutputFormat { #[command(group(clap::ArgGroup::new("price_amount").required(true).args(["amount", "buy_amount"])))] pub struct PriceArgs { /// Chain ID or name (e.g. base, 8453, ethereum, solana) - #[arg(short = 'c', long, env = "ZEROX_DEFAULT_CHAIN")] + #[arg(short = 'c', long, env = "ZEROEX_DEFAULT_CHAIN")] pub chain: String, /// Token to sell (contract address, e.g. 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) @@ -536,6 +558,21 @@ pub struct PriceArgs { /// Use gasless pricing #[arg(long)] pub gasless: bool, + + /// Pay per request through the 0x agent gateway instead of an API key. + /// EVM AllowanceHolder only (rejected with --gasless / Solana / Tron). + #[arg(long, value_enum)] + pub pay: Option, + + /// Maximum payment per request in USD; the CLI refuses to sign/broadcast + /// above this (only used with --pay). + #[arg(long, default_value = "0.05")] + pub max_payment: String, + + /// Tempo RPC URL for --pay mpp. Falls back to `rpc.tempo` in config + /// (`0x config set rpc.tempo `), then https://rpc.tempo.xyz. + #[arg(long)] + pub tempo_rpc: Option, } impl PriceArgs { @@ -555,7 +592,7 @@ impl PriceArgs { #[command(group(clap::ArgGroup::new("swap_amount").required(true).args(["amount", "buy_amount"])))] pub struct SwapArgs { /// Chain ID or name (e.g. base, 8453, ethereum, solana) - #[arg(short = 'c', long, env = "ZEROX_DEFAULT_CHAIN")] + #[arg(short = 'c', long, env = "ZEROEX_DEFAULT_CHAIN")] pub chain: String, /// Token to sell (contract address) @@ -594,6 +631,22 @@ pub struct SwapArgs { /// Token approval strategy (EVM only — warns and is ignored on Solana) #[arg(long, value_enum, default_value = "exact")] pub approval: ApprovalStrategy, + + /// Pay per request through the 0x agent gateway instead of an API key. + /// Pays only for the quote; the on-chain swap still uses your wallet + RPC. + /// EVM AllowanceHolder only (rejected with --gasless / Solana / Tron). + #[arg(long, value_enum)] + pub pay: Option, + + /// Maximum payment per request in USD; the CLI refuses to sign/broadcast + /// above this (only used with --pay). + #[arg(long, default_value = "0.05")] + pub max_payment: String, + + /// Tempo RPC URL for --pay mpp. Falls back to `rpc.tempo` in config + /// (`0x config set rpc.tempo `), then https://rpc.tempo.xyz. + #[arg(long)] + pub tempo_rpc: Option, } impl SwapArgs { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 07b9e48..1fa9df4 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -10,6 +10,35 @@ pub mod swap; use crate::error::{CliError, ErrorCode}; +/// `--pay` was used on a non-EVM chain. The agent gateway only proxies the EVM +/// AllowanceHolder price/quote. +pub fn pay_requires_evm() -> CliError { + CliError::Api { + code: ErrorCode::InputInvalid, + message: "--pay (agent payments) is only supported on EVM chains".into(), + status: None, + details: None, + suggestion: Some( + "The agent gateway proxies the EVM AllowanceHolder swap only. Use an EVM chain like --chain base." + .into(), + ), + } +} + +/// `--pay` combined with a flag it can't coexist with (e.g. `--gasless`). +pub fn pay_incompatible(flag: &str) -> CliError { + CliError::Api { + code: ErrorCode::InputInvalid, + message: format!("--pay cannot be combined with {flag}"), + status: None, + details: None, + suggestion: Some( + "Agent payments route through the EVM AllowanceHolder gateway, which doesn't support that mode." + .into(), + ), + } +} + /// Error for `--buy-amount` (exact-out) used where only exact-in is supported /// (Solana, gasless, cross-chain). `context` names the unsupported path. pub fn exact_out_unsupported(context: &str) -> CliError { diff --git a/src/commands/price.rs b/src/commands/price.rs index 460b492..8a46c69 100644 --- a/src/commands/price.rs +++ b/src/commands/price.rs @@ -128,6 +128,23 @@ pub async fn run( } } + // Agent-payment path: pay per request through the gateway instead of an + // API key. EVM AllowanceHolder only. + if let Some(pay) = args.pay { + if !chain_info.is_evm() { + return Err(super::pay_requires_evm()); + } + if args.gasless { + return Err(super::pay_incompatible("--gasless")); + } + // Reject exact-out up front (free) rather than paying for a request the + // gateway may not support. + if amount_spec.is_exact_out() { + return Err(super::exact_out_unsupported("agent payments (--pay)")); + } + return run_paid_price(args, pay, &config, output, global, chain_info, &amount_spec).await; + } + let client = crate::api::client_for(global, &config, output)?; let spinner = output.spinner("Fetching price..."); @@ -318,6 +335,100 @@ pub async fn run( Ok(output.emit_success("price", &result, metadata, warnings, 0)) } +/// Fetch an indicative price through the agent gateway, paying per request via +/// x402 / MPP. Mirrors the standard EVM price flow (token metadata + render) +/// but the transport is a paid handshake, and the settlement is surfaced in +/// `metadata.payment` plus a human info line. +#[allow(clippy::too_many_arguments)] +async fn run_paid_price( + args: &PriceArgs, + pay: crate::cli::PaymentArg, + config: &crate::config::types::AppConfig, + output: &OutputHandler, + global: &crate::GlobalOpts, + chain_info: &chain::ChainInfo, + amount_spec: &crate::api::types::AmountSpec, +) -> Result { + let cap = crate::payment::max_payment_to_base_units(&args.max_payment)?; + let signer = crate::wallet::evm::load_evm_signer(config, global.wallet.as_deref())?; + + let chain_id = chain_info.evm_chain_id()?; + let chain_id_str = chain_id.to_string(); + let (amount_key, amount_val) = amount_spec.query_param(); + let query: Vec<(&str, &str)> = vec![ + ("chainId", chain_id_str.as_str()), + ("sellToken", args.sell.as_str()), + ("buyToken", args.buy.as_str()), + (amount_key, amount_val), + ]; + + // Tempo RPC (mpp only): --tempo-rpc flag → `[rpc].tempo` config → default. + let tempo_rpc = args + .tempo_rpc + .as_deref() + .or_else(|| config.rpc.get("tempo").map(String::as_str)); + + let spinner = output.spinner(&format!("Paying for price via {}...", pay.method().as_str())); + let result = crate::payment::fetch::( + pay.method(), + &signer, + false, + &query, + cap, + global.timeout, + tempo_rpc, + ) + .await; + if let Some(s) = &spinner { + s.set_message("Resolving token metadata..."); + } + let (resp, receipt) = result?; + + let rpc_url = + config::try_resolve_rpc_url_with_override(global.rpc_url.as_deref(), config, chain_info); + let mut cache = TokenCache::new(); + let mut warnings: Vec = Vec::new(); + let (sell_meta, buy_meta) = resolve_pair_evm( + &mut cache, + rpc_url.as_deref(), + chain_info.numeric_id().unwrap_or(0), + &resp.sell_token, + &resp.buy_token, + &mut warnings, + ) + .await; + let sell = SideMeta::from_meta(resp.sell_token.clone(), sell_meta); + let buy = SideMeta::from_meta(resp.buy_token.clone(), buy_meta); + + if let Some(s) = spinner { + s.finish_and_clear(); + } + + output.info(&format!( + "Paid via {} ({} base units USDC){}", + receipt.method, + receipt.amount_base_units.as_deref().unwrap_or("?"), + receipt + .tx_hash + .as_deref() + .map(|t| format!(" — tx {t}")) + .unwrap_or_default(), + )); + + let mut metadata = Metadata::for_chain(chain_info); + metadata.zid = resp.zid.clone(); + metadata.payment = Some(receipt); + + let result = build_price_result(chain_info, &resp, &sell, &buy); + if !result.liquidity_available { + warnings.push(Warning { + code: "NO_LIQUIDITY".into(), + message: "Liquidity may not be available for this pair".into(), + }); + } + Ok(output.emit_success("price", &result, metadata, warnings, 0)) +} + fn build_price_result( chain_info: &chain::ChainInfo, resp: &PriceResponse, diff --git a/src/commands/swap.rs b/src/commands/swap.rs index cb8114d..60ddb1e 100644 --- a/src/commands/swap.rs +++ b/src/commands/swap.rs @@ -228,6 +228,23 @@ pub async fn run( } } + // Agent payments cover only the EVM AllowanceHolder path. Reject before + // dispatching to the Solana / gasless flows (which would silently ignore + // --pay otherwise). + if args.pay.is_some() { + if !chain_info.is_evm() { + return Err(super::pay_requires_evm()); + } + if args.gasless { + return Err(super::pay_incompatible("--gasless")); + } + // Reject exact-out up front (free) rather than paying for a quote the + // gateway may not support. + if amount_spec.is_exact_out() { + return Err(super::exact_out_unsupported("agent payments (--pay)")); + } + } + if chain_info.is_solana() { return super::solana_swap::run(args, output, &config, global, chain_info).await; } @@ -252,22 +269,63 @@ async fn run_evm_swap( let taker_address = format!("{:?}", signer.address()); let mut metadata = Metadata::for_chain(chain_info); - let client = crate::api::client_for(global, config, output)?; // Step 1: Get quote. Spinner is cleared automatically on Drop, so a `?` - // early-return from the API call doesn't leak tick characters. + // early-return from the API call doesn't leak tick characters. With --pay + // the quote is bought through the agent gateway (no API key); the on-chain + // swap below is unchanged and still uses the wallet + RPC. let spinner = output.spinner_guard("Fetching Allowance Holder quote..."); - let quote = client - .get_evm_quote( - chain_id, - &args.sell, - &args.buy, - &args.amount_spec(), - &taker_address, - Some(args.slippage), - args.recipient.as_deref(), + let mut pay_receipt: Option = None; + let quote: QuoteResponse = if let Some(pay) = args.pay { + spinner.set_message("Paying for Allowance Holder quote..."); + let cap = crate::payment::max_payment_to_base_units(&args.max_payment)?; + let chain_id_str = chain_id.to_string(); + let amount_spec = args.amount_spec(); + let (amount_key, amount_val) = amount_spec.query_param(); + let slippage_str = args.slippage.to_string(); + let mut query: Vec<(&str, &str)> = vec![ + ("chainId", chain_id_str.as_str()), + ("sellToken", args.sell.as_str()), + ("buyToken", args.buy.as_str()), + (amount_key, amount_val), + ("taker", taker_address.as_str()), + ("slippageBps", slippage_str.as_str()), + ]; + if let Some(r) = args.recipient.as_deref() { + query.push(("recipient", r)); + } + // Tempo RPC (mpp only): --tempo-rpc flag → `[rpc].tempo` config → default. + let tempo_rpc = args + .tempo_rpc + .as_deref() + .or_else(|| config.rpc.get("tempo").map(String::as_str)); + let (q, receipt) = crate::payment::fetch::( + pay.method(), + &signer, + true, + &query, + cap, + global.timeout, + tempo_rpc, ) .await?; + metadata.payment = Some(receipt.clone()); + pay_receipt = Some(receipt); + q + } else { + let client = crate::api::client_for(global, config, output)?; + client + .get_evm_quote( + chain_id, + &args.sell, + &args.buy, + &args.amount_spec(), + &taker_address, + Some(args.slippage), + args.recipient.as_deref(), + ) + .await? + }; metadata.zid = quote.zid.clone(); @@ -291,6 +349,19 @@ async fn run_evm_swap( drop(spinner); + if let Some(receipt) = &pay_receipt { + output.info(&format!( + "Paid via {} ({} base units USDC){}", + receipt.method, + receipt.amount_base_units.as_deref().unwrap_or("?"), + receipt + .tx_hash + .as_deref() + .map(|t| format!(" — tx {t}")) + .unwrap_or_default(), + )); + } + if quote.liquidity_available == Some(false) { return Err(CliError::Api { code: ErrorCode::NoLiquidity, diff --git a/src/config/mod.rs b/src/config/mod.rs index 59d2965..f6f6481 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -10,7 +10,7 @@ use types::AppConfig; /// emit a warning when running against a public fallback endpoint. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RpcSource { - /// From `--rpc-url` or `ZEROX_RPC_URL`. + /// From `--rpc-url` or `ZEROEX_RPC_URL`. Override, /// From `rpc.` in the config file. Config, @@ -62,11 +62,11 @@ pub struct ResolvedEnv { pub api_key: String, } -/// Resolve the API environment: `--profile` / `ZEROX_PROFILE` first, then +/// Resolve the API environment: `--profile` / `ZEROEX_PROFILE` first, then /// `active_profile` from the config file, then the default `[api]` section. /// Within a profile, unset fields fall back to the default section, so a /// profile may override just the key or just the URL. The `--api-key` flag -/// (and `ZEROX_API_KEY`, which clap feeds into it) wins over both. +/// (and `ZEROEX_API_KEY`, which clap feeds into it) wins over both. pub fn resolve_env( global: &crate::GlobalOpts, config: &AppConfig, @@ -243,16 +243,16 @@ pub fn load_config() -> Result { let mut config = load_config_disk_only()?; // Environment variable overrides — applied to the in-memory view only. - if let Ok(key) = std::env::var("ZEROX_API_KEY") { + if let Ok(key) = std::env::var("ZEROEX_API_KEY") { config.api.api_key = Some(key); } - if let Ok(key) = std::env::var("ZEROX_EVM_PRIVATE_KEY") { + if let Ok(key) = std::env::var("ZEROEX_EVM_PRIVATE_KEY") { config.wallet.evm = Some(key); } - if let Ok(key) = std::env::var("ZEROX_SOLANA_KEYPAIR") { + if let Ok(key) = std::env::var("ZEROEX_SOLANA_KEYPAIR") { config.wallet.solana = Some(key); } - if let Ok(chain) = std::env::var("ZEROX_DEFAULT_CHAIN") { + if let Ok(chain) = std::env::var("ZEROEX_DEFAULT_CHAIN") { config.defaults.chain = Some(chain); } @@ -788,7 +788,7 @@ mod tests { config.active_profile = None; config.profiles.remove("other"); - // 4. --api-key / ZEROX_API_KEY beats the profile's key. + // 4. --api-key / ZEROEX_API_KEY beats the profile's key. let env = resolve_env(&global_with(Some("stg"), Some("flag-key")), &config).unwrap(); assert_eq!(env.api_key, "flag-key"); diff --git a/src/config/types.rs b/src/config/types.rs index 56c6f2b..35bdad6 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -126,7 +126,7 @@ pub struct WalletConfig { pub struct TelemetryConfig { /// Whether anonymous usage stats may be sent. User-settable via /// `0x config set telemetry.enabled false` (also overridable by the - /// `ZEROX_TELEMETRY` / `DO_NOT_TRACK` env vars). + /// `ZEROEX_TELEMETRY` / `DO_NOT_TRACK` env vars). #[serde(default = "default_telemetry_enabled")] pub enabled: bool, diff --git a/src/error.rs b/src/error.rs index 9454db8..4447c10 100644 --- a/src/error.rs +++ b/src/error.rs @@ -57,6 +57,22 @@ pub enum ErrorCode { BridgeFailed, BridgeTimeout, + // Agent-payment errors (x402 / MPP via the agent gateway) + /// The 402 challenge from the gateway was missing, malformed, or offered + /// no payment scheme this CLI can satisfy. + PaymentChallengeInvalid, + /// The challenge's required amount exceeded the `--max-payment` cap, so the + /// CLI refused to sign/broadcast. No money was spent. + PaymentExceedsLimit, + /// Signing the payment authorization (x402 EIP-3009) failed. + PaymentSigningFailed, + /// The payment was submitted but the gateway rejected it or on-chain + /// settlement failed. Money may have been spent without a usable response. + PaymentSettlementFailed, + /// The payment wallet lacks the funds to pay (USDC / USDC.e, or native gas + /// for an MPP push broadcast). + PaymentWalletUnfunded, + // User errors UserCancelled, } @@ -82,6 +98,11 @@ impl ErrorCode { | Self::TransactionTimeout | Self::QuoteExpired => "execution", Self::BridgeFailed | Self::BridgeTimeout => "bridge", + Self::PaymentChallengeInvalid + | Self::PaymentExceedsLimit + | Self::PaymentSigningFailed + | Self::PaymentSettlementFailed + | Self::PaymentWalletUnfunded => "payment", Self::UserCancelled => "input", } } @@ -124,6 +145,14 @@ impl ErrorCode { Self::QuoteExpired => 1, Self::BridgeFailed => 1, Self::BridgeTimeout => 12, + // Dedicated 40-range for agent-payment failures so agents can + // distinguish "cap refused, nothing spent" (41) from "money spent, + // no usable result" (43). Mirrored in SKILL.md's exit-code table. + Self::PaymentChallengeInvalid => 40, + Self::PaymentExceedsLimit => 41, + Self::PaymentSigningFailed => 42, + Self::PaymentSettlementFailed => 43, + Self::PaymentWalletUnfunded => 44, Self::UserCancelled => 20, } } @@ -163,6 +192,11 @@ impl ErrorCode { Self::QuoteExpired => "QUOTE_EXPIRED", Self::BridgeFailed => "BRIDGE_FAILED", Self::BridgeTimeout => "BRIDGE_TIMEOUT", + Self::PaymentChallengeInvalid => "PAYMENT_CHALLENGE_INVALID", + Self::PaymentExceedsLimit => "PAYMENT_EXCEEDS_LIMIT", + Self::PaymentSigningFailed => "PAYMENT_SIGNING_FAILED", + Self::PaymentSettlementFailed => "PAYMENT_SETTLEMENT_FAILED", + Self::PaymentWalletUnfunded => "PAYMENT_WALLET_UNFUNDED", Self::UserCancelled => "USER_CANCELLED", } } @@ -229,10 +263,10 @@ impl CliError { Self::Transaction { suggestion, .. } => suggestion.as_deref(), Self::Config { code, .. } => match code { ErrorCode::ApiKeyMissing => { - Some("Run '0x config set api_key ' or set ZEROX_API_KEY env var") + Some("Run '0x config set api_key ' or set ZEROEX_API_KEY env var") } ErrorCode::WalletNotFound => Some( - "Run '0x config set wallet.evm ' or set ZEROX_EVM_PRIVATE_KEY", + "Run '0x config set wallet.evm ' or set ZEROEX_EVM_PRIVATE_KEY", ), ErrorCode::ConfigNotFound => Some("Run '0x config init' to set up your config"), _ => None, @@ -390,6 +424,11 @@ pub(crate) const ALL_ERROR_CODES: &[ErrorCode] = &[ ErrorCode::QuoteExpired, ErrorCode::BridgeFailed, ErrorCode::BridgeTimeout, + ErrorCode::PaymentChallengeInvalid, + ErrorCode::PaymentExceedsLimit, + ErrorCode::PaymentSigningFailed, + ErrorCode::PaymentSettlementFailed, + ErrorCode::PaymentWalletUnfunded, ErrorCode::UserCancelled, ]; diff --git a/src/lib.rs b/src/lib.rs index 0d61d7d..b7dbccf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod config; pub mod confirm; pub mod error; pub mod output; +pub mod payment; pub mod telemetry; pub mod token_cache; pub mod wallet; diff --git a/src/output/envelope.rs b/src/output/envelope.rs index c0c1412..b152dd1 100644 --- a/src/output/envelope.rs +++ b/src/output/envelope.rs @@ -46,6 +46,10 @@ pub struct Metadata { /// 0x request tracking ID from API responses. #[serde(skip_serializing_if = "Option::is_none")] pub zid: Option, + /// Agent-payment settlement, present only when the command paid per + /// request via `--pay` (x402 / MPP) instead of an API key. + #[serde(skip_serializing_if = "Option::is_none")] + pub payment: Option, } impl Default for Metadata { @@ -55,6 +59,7 @@ impl Default for Metadata { chain_name: None, api_version: "v2", zid: None, + payment: None, } } } diff --git a/src/payment/mod.rs b/src/payment/mod.rs new file mode 100644 index 0000000..a00287d --- /dev/null +++ b/src/payment/mod.rs @@ -0,0 +1,232 @@ +//! Agent-payment support: pay per request through the 0x agent gateway +//! (`https://agent.api.0x.org`) instead of using a `0x-api-key`. +//! +//! Two open standards are supported (Phase 1): +//! - **x402** (`--pay x402-evm`): an EIP-3009 `transferWithAuthorization` +//! signature sent in the `PAYMENT-SIGNATURE` header. Handled by the +//! `x402-reqwest` crate via [`x402`]. +//! - **MPP** (`--pay mpp`): the Machine Payments Protocol over Tempo +//! (chainId 4217), challenge→credential→receipt with a "push" broadcast. +//! Handled by the `mpp` crate via [`mpp`]. +//! +//! Both gateway endpoints proxy the same Swap API v2 AllowanceHolder +//! price/quote, so the response bodies deserialize into the existing +//! [`crate::api::evm_swap`] types — only the transport (no API key, a 402 +//! payment handshake) differs from [`crate::api::ApiClient`]. +//! +//! ## Safety +//! `--max-payment` is enforced **client-side before any signature or +//! broadcast** (x402: in the payment selector; MPP: in an unpaid pre-flight +//! that reads the challenge amount). Failures map to the dedicated +//! `PAYMENT_*` error codes so an agent can tell "refused, nothing spent" +//! (exit 41) from "money spent, no result" (exit 43). + +pub mod mpp; +pub mod x402; + +use crate::error::{CliError, ErrorCode}; +use alloy::primitives::U256; +use alloy::signers::local::PrivateKeySigner as EvmSigner; +use serde::Serialize; + +/// Agent-gateway base URL. Hardcoded with no env/config override on purpose: +/// payments are signed against whatever host this resolves to, so a +/// redirectable base URL would be a payment-redirect risk. A staging hook, if +/// ever needed, should be a deliberate, reviewed addition. +const AGENT_BASE_URL: &str = "https://agent.api.0x.org"; + +/// USDC / USDC.e are 6-decimal tokens; `--max-payment` is given in USD and +/// converted to base units against this. +const USDC_DECIMALS: u32 = 6; + +/// Which agent-payment rail to use. The variant set is intentionally open to a +/// future `X402Solana` without reshaping callers. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PaymentMethod { + /// x402 over an EVM payment network (Base), EIP-3009 exact scheme. + X402Evm, + /// MPP over Tempo mainnet (chainId 4217), push settlement. + MppTempo, +} + +impl PaymentMethod { + /// Stable label for output / receipts. + pub fn as_str(self) -> &'static str { + match self { + Self::X402Evm => "x402-evm", + Self::MppTempo => "mpp", + } + } + + /// Gateway path prefix for this method's AllowanceHolder endpoints. + fn gateway_prefix(self) -> &'static str { + match self { + Self::X402Evm => "/v1/x402", + Self::MppTempo => "/v1/mpp-tempo", + } + } + + /// Full gateway URL for `price` or `quote` under this method. + fn endpoint_url(self, quote: bool) -> String { + let leaf = if quote { + "swap-allowance-holder-quote" + } else { + "swap-allowance-holder-price" + }; + format!("{AGENT_BASE_URL}{}/{leaf}/", self.gateway_prefix()) + } +} + +/// A protocol-agnostic view of the settlement the gateway returns +/// (`PAYMENT-RESPONSE` for x402, `Payment-Receipt` / event for MPP). Every +/// field is best-effort: agents get whatever the rail exposed, and at minimum +/// the payer address (which the CLI always knows — it signed). +#[derive(Debug, Clone, Serialize)] +pub struct PaymentReceipt { + /// Payment method label (`x402-evm` / `mpp`). + pub method: &'static str, + /// CAIP-2-style network the payment settled on, when known + /// (e.g. `eip155:8453`, `tempo:4217`). + #[serde(skip_serializing_if = "Option::is_none")] + pub network: Option, + /// On-chain payment transaction hash, when the rail reports one. + #[serde(skip_serializing_if = "Option::is_none")] + pub tx_hash: Option, + /// The paying wallet address. + #[serde(skip_serializing_if = "Option::is_none")] + pub payer: Option, + /// Amount paid, in the asset's base units (USDC/USDC.e have 6 decimals). + #[serde(skip_serializing_if = "Option::is_none")] + pub amount_base_units: Option, +} + +/// Convert a `--max-payment` USD value (e.g. `0.05`) into USDC base units using +/// exact decimal arithmetic (no float). Rejects non-positive / unparseable / +/// over-precise input fail-closed — an unusable cap must never silently become +/// "unlimited". +pub fn max_payment_to_base_units(usd: &str) -> Result { + let invalid = || CliError::Config { + code: ErrorCode::InputInvalid, + message: format!("--max-payment '{usd}' must be a positive USD amount with at most 6 decimals (e.g. 0.05)"), + }; + + let trimmed = usd.trim(); + if trimmed.is_empty() || trimmed.starts_with('-') || trimmed.starts_with('+') { + return Err(invalid()); + } + let (int_part, frac_part) = trimmed.split_once('.').unwrap_or((trimmed, "")); + if !int_part.chars().all(|c| c.is_ascii_digit()) + || !frac_part.chars().all(|c| c.is_ascii_digit()) + { + return Err(invalid()); + } + // More than 6 fractional digits is sub-base-unit precision — refuse rather + // than silently truncate it away. + if frac_part.len() > USDC_DECIMALS as usize { + return Err(invalid()); + } + // Right-pad the fraction to exactly 6 digits, concatenate with the integer + // part, and parse the whole thing as base units. + let frac_padded = format!("{frac_part:0 Result { + let bytes = signer.to_bytes(); + alloy_signer_local::PrivateKeySigner::from_slice(bytes.as_slice()).map_err(|e| { + CliError::Transaction { + code: ErrorCode::PaymentSigningFailed, + message: format!("Failed to load payment signer: {e}"), + tx_hash: None, + suggestion: None, + } + }) +} + +/// Pay for and fetch one gateway price/quote response, deserialized into `T`. +/// +/// `query` is the same parameter list the keyed API uses (chainId, sellToken, +/// …). `signer` is the EVM wallet that pays. `max_payment` is the +/// already-parsed cap in USDC base units. `tempo_rpc` is only consulted for +/// [`PaymentMethod::MppTempo`]. +/// +/// Returns the deserialized body plus a [`PaymentReceipt`]. The payment is a +/// single, non-idempotent pay-and-submit — there is no automatic retry that +/// could double-sign or double-broadcast. +pub async fn fetch( + method: PaymentMethod, + signer: &EvmSigner, + quote: bool, + query: &[(&str, &str)], + max_payment: U256, + timeout_secs: u64, + tempo_rpc: Option<&str>, +) -> Result<(T, PaymentReceipt), CliError> { + let url = method.endpoint_url(quote); + match method { + PaymentMethod::X402Evm => { + x402::fetch(signer, &url, query, max_payment, timeout_secs).await + } + PaymentMethod::MppTempo => { + mpp::fetch(signer, &url, query, max_payment, timeout_secs, tempo_rpc).await + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn max_payment_parses_usd_to_base_units() { + assert_eq!(max_payment_to_base_units("0.05").unwrap(), U256::from(50_000u64)); + assert_eq!(max_payment_to_base_units("1").unwrap(), U256::from(1_000_000u64)); + assert_eq!( + max_payment_to_base_units("0.000001").unwrap(), + U256::from(1u64) + ); + } + + #[test] + fn max_payment_rejects_bad_input_fail_closed() { + assert!(max_payment_to_base_units("0").is_err()); + assert!(max_payment_to_base_units("-1").is_err()); + assert!(max_payment_to_base_units("abc").is_err()); + assert!(max_payment_to_base_units("").is_err()); + // Below one base unit must not round to zero / unlimited. + assert!(max_payment_to_base_units("0.0000001").is_err()); + } + + #[test] + fn endpoint_urls_match_gateway_layout() { + assert_eq!( + PaymentMethod::X402Evm.endpoint_url(false), + "https://agent.api.0x.org/v1/x402/swap-allowance-holder-price/" + ); + assert_eq!( + PaymentMethod::MppTempo.endpoint_url(true), + "https://agent.api.0x.org/v1/mpp-tempo/swap-allowance-holder-quote/" + ); + } + + #[test] + fn method_labels_are_stable() { + assert_eq!(PaymentMethod::X402Evm.as_str(), "x402-evm"); + assert_eq!(PaymentMethod::MppTempo.as_str(), "mpp"); + } +} diff --git a/src/payment/mpp.rs b/src/payment/mpp.rs new file mode 100644 index 0000000..cdc196e --- /dev/null +++ b/src/payment/mpp.rs @@ -0,0 +1,424 @@ +//! MPP-Tempo adapter over the `mpp` crate. +//! +//! Uses the per-request `PaymentExt::send_with_payment` API on a plain reqwest +//! client (no middleware, no auto-retry of a paid request). The `--max-payment` +//! cap — plus the expected currency (USDC.e) and chain (Tempo mainnet) — is +//! enforced **in-band**, inside a [`CappedTempoProvider`] wrapper whose `pay()` +//! is the single chokepoint the real (paid) challenge passes through *before* +//! any on-chain broadcast. This avoids the time-of-check/time-of-use gap a +//! separate unpaid pre-flight would have (the probe's challenge and the paid +//! challenge can differ). + +use super::{to_payment_signer, PaymentReceipt}; +use crate::error::{CliError, ErrorCode}; +use alloy::primitives::{Address, U256}; +use alloy::signers::local::PrivateKeySigner as EvmSigner; +use base64::{engine::general_purpose::STANDARD, Engine}; +use mpp::client::tempo::charge::TempoCharge; +use mpp::client::{Fetch, PaymentProvider, TempoProvider}; +use mpp::protocol::core::{PaymentChallenge, PaymentCredential}; +use mpp::MppError; +// Payment path speaks reqwest 0.13 (what mpp is built on), aliased to coexist +// with the keyed API client's reqwest 0.12. +use reqwest_payments as reqwest; +use serde::Deserialize; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +/// Tempo mainnet (chainId 4217) default RPC, matching the `mpp` crate's own +/// `DEFAULT_RPC_URL`. Overridable via `--tempo-rpc` or `[rpc].tempo` config. +const DEFAULT_TEMPO_RPC: &str = "https://rpc.tempo.xyz"; +const TEMPO_CHAIN_ID: u64 = 4217; +/// USDC.e on Tempo mainnet — the only currency the `--max-payment` cap (in +/// 6-decimal base units) is denominated against. A challenge in any other +/// currency is refused fail-closed so the cap can't be silently misapplied to +/// a token with different decimals/value. +const TEMPO_USDC_E: &str = "0x20C000000000000000000000b9537d11c60E8b50"; + +/// Why the capped provider refused to pay (recorded before returning an error +/// so the caller can emit a precise, safe exit code — none of these spent +/// anything). +#[derive(Debug, Clone)] +enum Refusal { + ExceedsCap { amount: U256, cap: U256 }, + WrongChain(u64), + WrongCurrency(String), + ChallengeUnparseable(String), +} + +#[derive(Default)] +struct CapOutcome { + /// The amount/chain actually authorized for payment (set only when the + /// cap+currency+chain checks passed and we delegated to the inner pay). + paid: Option<(U256, u64)>, + /// Set instead when we refused — nothing was spent. + refusal: Option, +} + +/// Wraps [`TempoProvider`] and enforces the spend cap + expected currency/chain +/// on the genuine challenge inside `pay()`, before the inner provider signs or +/// broadcasts anything. +#[derive(Clone)] +struct CappedTempoProvider { + inner: TempoProvider, + cap: U256, + expected_currency: Address, + outcome: Arc>, +} + +impl PaymentProvider for CappedTempoProvider { + fn supports(&self, method: &str, intent: &str) -> bool { + self.inner.supports(method, intent) + } + + fn accept_payment_header(&self) -> Option { + self.inner.accept_payment_header() + } + + async fn pay(&self, challenge: &PaymentChallenge) -> Result { + let charge = match TempoCharge::from_challenge(challenge) { + Ok(c) => c, + Err(e) => { + self.record(Refusal::ChallengeUnparseable(e.to_string())); + return Err(e); + } + }; + let amount = charge.amount(); + let chain_id = charge.chain_id(); + let currency = charge.currency(); + + if chain_id != TEMPO_CHAIN_ID { + self.record(Refusal::WrongChain(chain_id)); + return Err(MppError::InvalidConfig(format!( + "MPP challenge chainId {chain_id} is not Tempo mainnet ({TEMPO_CHAIN_ID}) — refusing" + ))); + } + if currency != self.expected_currency { + self.record(Refusal::WrongCurrency(currency.to_string())); + return Err(MppError::InvalidConfig(format!( + "MPP challenge currency {currency} is not the expected USDC.e — refusing" + ))); + } + if amount > self.cap { + self.record(Refusal::ExceedsCap { amount, cap: self.cap }); + return Err(MppError::InvalidConfig(format!( + "MPP payment {amount} base units exceeds --max-payment cap {} — refusing before broadcast", + self.cap + ))); + } + + // Within cap and bound to USDC.e on Tempo — authorize the broadcast. + self.outcome.lock().unwrap_or_else(|e| e.into_inner()).paid = Some((amount, chain_id)); + self.inner.pay(challenge).await + } +} + +impl CappedTempoProvider { + fn record(&self, r: Refusal) { + self.outcome.lock().unwrap_or_else(|e| e.into_inner()).refusal = Some(r); + } +} + +pub(super) async fn fetch( + signer: &EvmSigner, + url: &str, + query: &[(&str, &str)], + max_payment: U256, + timeout_secs: u64, + tempo_rpc: Option<&str>, +) -> Result<(T, PaymentReceipt), CliError> { + let payer_addr = format!("{:?}", signer.address()); + // Tempo RPC resolution (flag → `[rpc].tempo` config) is done by the caller; + // fall back to the canonical mainnet endpoint here. + let rpc = tempo_rpc.unwrap_or(DEFAULT_TEMPO_RPC).to_string(); + + let expected_currency = + Address::from_str(TEMPO_USDC_E).expect("TEMPO_USDC_E is a valid address literal"); + + let inner_provider = TempoProvider::new(to_payment_signer(signer)?, &rpc).map_err(|e| { + CliError::Config { + code: ErrorCode::ConfigInvalid, + message: format!("Failed to initialize Tempo provider (rpc={rpc}): {e}"), + } + })?; + let outcome = Arc::new(Mutex::new(CapOutcome::default())); + let provider = CappedTempoProvider { + inner: inner_provider, + cap: max_payment, + expected_currency, + outcome: Arc::clone(&outcome), + }; + + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(timeout_secs.max(120))) + .build() + .map_err(|e| CliError::Config { + code: ErrorCode::NetworkError, + message: format!("Failed to build MPP HTTP client: {e}"), + })?; + + // The on-chain push broadcast happens inside send_with_payment against the + // Tempo RPC, which the reqwest timeout doesn't bound — so wrap the whole + // flow in an explicit deadline. + let broadcast_deadline = Duration::from_secs(timeout_secs.max(120).saturating_add(60)); + let send = client.get(url).query(query).send_with_payment(&provider); + let response = match tokio::time::timeout(broadcast_deadline, send).await { + Ok(Ok(resp)) => resp, + Ok(Err(http_err)) => { + // Prefer the in-band refusal reason (nothing spent) over the + // crate's generic payment error. + if let Some(err) = refusal_to_error(&outcome) { + return Err(err); + } + return Err(map_mpp_http_error(http_err)); + } + Err(_) => { + return Err(CliError::Transaction { + code: ErrorCode::PaymentSettlementFailed, + message: format!( + "MPP payment timed out after {}s (Tempo broadcast may have started)", + broadcast_deadline.as_secs() + ), + tx_hash: None, + suggestion: Some( + "Check your wallet on Tempo before retrying — a transfer may have broadcast." + .into(), + ), + }); + } + }; + + let status = response.status(); + if !status.is_success() { + let body = response.text().await.unwrap_or_default(); + return Err(CliError::Transaction { + code: ErrorCode::PaymentSettlementFailed, + message: format!( + "MPP gateway returned {} after payment: {}", + status.as_u16(), + crate::api::truncate_for_error(&body) + ), + tx_hash: None, + suggestion: Some( + "The Tempo payment may have broadcast without a usable response — check your wallet before retrying.".into(), + ), + }); + } + + let tx_hash = response + .headers() + .get("payment-receipt") + .and_then(|h| h.to_str().ok()) + .and_then(extract_receipt_tx); + + let (amount, chain_id) = outcome + .lock() + .unwrap_or_else(|e| e.into_inner()) + .paid + .map(|(a, c)| (Some(a.to_string()), c)) + .unwrap_or((None, TEMPO_CHAIN_ID)); + + let body = response.text().await.map_err(|e| CliError::Api { + code: ErrorCode::PaymentSettlementFailed, + message: format!("Failed to read gateway response body: {e}"), + status: Some(status.as_u16()), + details: None, + suggestion: None, + })?; + let value: T = serde_json::from_str(&body).map_err(|e| parse_err(&body, e))?; + + let receipt = PaymentReceipt { + method: super::PaymentMethod::MppTempo.as_str(), + network: Some(format!("tempo:{chain_id}")), + tx_hash, + // The payer is authoritatively the wallet we signed with. + payer: Some(payer_addr), + amount_base_units: amount, + }; + Ok((value, receipt)) +} + +/// Turn a recorded in-band refusal into a precise CliError. All refusals happen +/// *before* `inner.pay`, so nothing was spent. +fn refusal_to_error(outcome: &Arc>) -> Option { + // Recover through a poisoned lock rather than dropping the refusal — losing + // it would downgrade a safe "nothing spent" (exit 41) into a misleading + // "money may have moved" (exit 43). That's the dangerous direction. + let refusal = outcome + .lock() + .unwrap_or_else(|e| e.into_inner()) + .refusal + .clone()?; + Some(match refusal { + Refusal::ExceedsCap { amount, cap } => CliError::Config { + code: ErrorCode::PaymentExceedsLimit, + message: format!( + "MPP payment of {amount} base units exceeds --max-payment cap {cap} — refused before broadcasting. Nothing was spent." + ), + }, + Refusal::WrongChain(cid) => CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: format!("MPP challenge was for chainId {cid}, not Tempo mainnet ({TEMPO_CHAIN_ID}) — refused. Nothing was spent."), + status: Some(402), + details: None, + suggestion: None, + }, + Refusal::WrongCurrency(c) => CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: format!("MPP challenge currency {c} is not the expected USDC.e — refused. Nothing was spent."), + status: Some(402), + details: None, + suggestion: None, + }, + Refusal::ChallengeUnparseable(e) => CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: format!("MPP challenge could not be parsed: {e}"), + status: Some(402), + details: None, + suggestion: None, + }, + }) +} + +/// Best-effort tx-hash extraction from a `Payment-Receipt` header. Prefer the +/// reliable JSON / base64-JSON forms; fall back to scanning for a 0x… hash. +fn extract_receipt_tx(header: &str) -> Option { + #[derive(Deserialize)] + struct Receipt { + #[serde(alias = "txHash", alias = "transaction", alias = "tx_hash")] + tx: Option, + } + if let Ok(r) = serde_json::from_str::(header) { + if r.tx.is_some() { + return r.tx; + } + } + if let Ok(bytes) = STANDARD.decode(header) { + if let Ok(r) = serde_json::from_slice::(&bytes) { + if r.tx.is_some() { + return r.tx; + } + } + } + if let Some(pos) = header.find("0x") { + let candidate = &header[pos..]; + let hex_len = candidate + .chars() + .skip(2) + .take_while(|c| c.is_ascii_hexdigit()) + .count(); + if hex_len == 64 { + return Some(candidate[..66].to_string()); + } + } + None +} + +fn map_mpp_http_error(e: mpp::client::HttpError) -> CliError { + use mpp::client::HttpError; + match e { + HttpError::MissingChallenge + | HttpError::NoSupportedChallenge(_) + | HttpError::InvalidChallenge(_) => CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: format!("MPP challenge error: {e}"), + status: Some(402), + details: None, + suggestion: None, + }, + HttpError::InvalidCredential(_) | HttpError::CloneFailed => CliError::Transaction { + code: ErrorCode::PaymentSigningFailed, + message: format!("MPP credential error: {e}"), + tx_hash: None, + suggestion: None, + }, + HttpError::Payment(_) => CliError::Transaction { + code: ErrorCode::PaymentSettlementFailed, + message: format!("MPP payment failed: {e}"), + tx_hash: None, + suggestion: Some( + "If Tempo broadcast started, money may have moved — check your wallet. Ensure the wallet holds USDC.e and native gas on Tempo.".into(), + ), + }, + HttpError::Request(_) => CliError::Api { + code: ErrorCode::NetworkError, + message: format!("MPP request failed: {e}"), + status: None, + details: None, + suggestion: Some("Check your network connection and the Tempo RPC URL.".into()), + }, + } +} + +fn parse_err(body: &str, e: serde_json::Error) -> CliError { + CliError::Api { + code: ErrorCode::ApiError, + message: format!("Failed to parse gateway response: {e}"), + status: None, + details: Some(serde_json::json!({ + "body_preview": crate::api::truncate_for_error(body) + })), + suggestion: None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tempo_usdc_e_is_a_valid_address() { + assert!(Address::from_str(TEMPO_USDC_E).is_ok()); + } + + #[test] + fn cap_comparison_is_base_units() { + // $0.01 challenge (10000) is within a $0.05 cap (50000). + let amount = U256::from_str("10000").unwrap(); + let cap = U256::from(50_000u64); + assert!(amount <= cap); + } + + #[test] + fn refusal_over_cap_maps_to_exceeds_limit() { + let outcome = Arc::new(Mutex::new(CapOutcome { + paid: None, + refusal: Some(Refusal::ExceedsCap { + amount: U256::from(100_000u64), + cap: U256::from(50_000u64), + }), + })); + assert_eq!( + refusal_to_error(&outcome).unwrap().code(), + ErrorCode::PaymentExceedsLimit + ); + } + + #[test] + fn refusal_wrong_currency_maps_to_challenge_invalid() { + let outcome = Arc::new(Mutex::new(CapOutcome { + paid: None, + refusal: Some(Refusal::WrongCurrency("0xdead".into())), + })); + assert_eq!( + refusal_to_error(&outcome).unwrap().code(), + ErrorCode::PaymentChallengeInvalid + ); + } + + #[test] + fn no_refusal_is_none() { + let outcome = Arc::new(Mutex::new(CapOutcome::default())); + assert!(refusal_to_error(&outcome).is_none()); + } + + #[test] + fn extract_receipt_tx_prefers_json_then_hex() { + let json = r#"{"txHash":"0xdeadbeef"}"#; + assert_eq!(extract_receipt_tx(json).as_deref(), Some("0xdeadbeef")); + let h = "0x".to_string() + &"a".repeat(64); + assert_eq!(extract_receipt_tx(&h).as_deref(), Some(h.as_str())); + assert!(extract_receipt_tx("no hash here").is_none()); + } +} diff --git a/src/payment/x402.rs b/src/payment/x402.rs new file mode 100644 index 0000000..2e86400 --- /dev/null +++ b/src/payment/x402.rs @@ -0,0 +1,355 @@ +//! x402-EVM adapter over the `x402-reqwest` crate. +//! +//! Wraps a `reqwest` client with the x402 middleware: on a `402` it parses the +//! `PAYMENT-REQUIRED` challenge, signs an EIP-3009 `transferWithAuthorization` +//! (V2 EIP-155 "exact" scheme), and retries with the `PAYMENT-SIGNATURE` +//! header. The `--max-payment` cap is enforced inside the payment selector, +//! **before any signature**. + +use super::{to_payment_signer, PaymentReceipt}; +use crate::error::{CliError, ErrorCode}; +use alloy::primitives::U256; +use alloy::signers::local::PrivateKeySigner as EvmSigner; +use base64::{engine::general_purpose::STANDARD, Engine}; +// The payment path speaks reqwest 0.13 (what x402-reqwest/mpp are built on), +// aliased so it doesn't collide with the keyed API client's reqwest 0.12. +use reqwest_payments as reqwest; +use serde::Deserialize; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use x402_chain_eip155::V2Eip155ExactClient; +use x402_reqwest::{ReqwestWithPayments, ReqwestWithPaymentsBuild, X402Client}; +use x402_types::scheme::client::{PaymentCandidate, PaymentSelector}; + +/// USDC on Base — the only asset the `--max-payment` cap (6-decimal base units) +/// is denominated against for x402. The gateway always offers Base USDC (or +/// Solana, which this Phase-1 EVM client doesn't register). Binding the asset +/// stops the 6-decimal cap from being misapplied to a token with different +/// decimals/value. +const BASE_USDC: &str = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; +/// Base mainnet, CAIP-2 `eip155:8453` — the x402 payment network. Bound +/// alongside the asset so a candidate carrying the USDC *address* on a +/// different chain can't slip through the cap (belt-and-suspenders; the signed +/// EIP-712 domain also carries chainId). +const BASE_CHAIN_NAMESPACE: &str = "eip155"; +const BASE_CHAIN_REFERENCE: &str = "8453"; + +/// What the selector saw, captured so the caller can produce a precise error +/// after the middleware finishes: distinguish "no payable scheme offered" +/// (exit 40) from "cheapest option exceeded the cap" (exit 41) — in both cases +/// nothing was signed or spent. +#[derive(Debug, Default)] +struct SelectionOutcome { + /// The selector ran at least once (i.e. a 402 was parsed into candidates). + invoked: bool, + /// Number of candidates the registered scheme produced. + candidate_count: usize, + /// Cheapest candidate amount seen, in asset base units. + min_amount: Option, +} + +/// Picks the cheapest candidate within the cap, recording what it saw. Returns +/// `None` (which the middleware turns into an error) when nothing is payable +/// within the cap — we then classify *why* from the captured outcome. +struct CappedSelector { + cap: U256, + /// Asset the cap is denominated in; candidates in any other asset are + /// ineligible (fail-closed) so the 6-decimal cap can't be misapplied. + expected_asset: String, + outcome: Arc>, +} + +impl PaymentSelector for CappedSelector { + fn select<'a>(&self, candidates: &'a [PaymentCandidate]) -> Option<&'a PaymentCandidate> { + // Only consider candidates in the expected asset. `candidate_count` / + // `min_amount` reflect these eligible ones so the over-cap vs. + // no-scheme classification stays meaningful. + let eligible: Vec<&PaymentCandidate> = candidates + .iter() + .filter(|c| { + c.asset.eq_ignore_ascii_case(&self.expected_asset) + && c.chain_id.namespace() == BASE_CHAIN_NAMESPACE + && c.chain_id.reference() == BASE_CHAIN_REFERENCE + }) + .collect(); + { + let mut o = self.outcome.lock().unwrap_or_else(|e| e.into_inner()); + o.invoked = true; + o.candidate_count = eligible.len(); + o.min_amount = eligible.iter().map(|c| c.amount).min(); + } + eligible + .into_iter() + .filter(|c| c.amount <= self.cap) + .min_by(|a, b| a.amount.cmp(&b.amount)) + } +} + +/// Settlement data from the `PAYMENT-RESPONSE` header (base64 JSON). +#[derive(Debug, Default, Deserialize)] +struct X402Settlement { + #[serde(default)] + transaction: Option, + #[serde(default)] + network: Option, +} + +pub(super) async fn fetch( + signer: &EvmSigner, + url: &str, + query: &[(&str, &str)], + max_payment: U256, + timeout_secs: u64, +) -> Result<(T, PaymentReceipt), CliError> { + let payer_addr = format!("{:?}", signer.address()); + let payment_signer = to_payment_signer(signer)?; + + let outcome = Arc::new(Mutex::new(SelectionOutcome::default())); + let selector = CappedSelector { + cap: max_payment, + expected_asset: BASE_USDC.to_string(), + outcome: Arc::clone(&outcome), + }; + let x402_client = X402Client::new() + .with_selector(selector) + .register(V2Eip155ExactClient::new(payment_signer)); + + // A fresh client with ONLY the x402 middleware — deliberately no + // reqwest-retry layer, so a transient failure never silently re-signs or + // re-submits a paid request. Payment broadcasts can be slow; floor the + // timeout generously. + let inner = reqwest::Client::builder() + .timeout(Duration::from_secs(timeout_secs.max(60))) + .build() + .map_err(|e| CliError::Config { + code: ErrorCode::NetworkError, + message: format!("Failed to build x402 HTTP client: {e}"), + })?; + let http = inner.with_payments(x402_client).build(); + + // The reqwest-middleware RequestBuilder doesn't proxy `.query()`, so fold + // the params into the URL up front. + let full_url = reqwest::Url::parse_with_params(url, query.iter().copied()).map_err(|e| { + CliError::Config { + code: ErrorCode::InputInvalid, + message: format!("Failed to build gateway URL: {e}"), + } + })?; + let result = http.get(full_url).send().await; + + let snapshot = { + let o = outcome.lock().unwrap_or_else(|e| e.into_inner()); + (o.invoked, o.candidate_count, o.min_amount) + }; + + let response = match result { + Ok(r) => r, + Err(e) => return Err(classify_send_error(&e.to_string(), snapshot, max_payment)), + }; + + let status = response.status(); + if status.as_u16() == 402 { + // Defensive: when the selector returns None the middleware surfaces an + // Err (handled above), not Ok(402) — so this branch is normally + // unreachable. Kept fail-safe in case a future middleware version + // returns the original 402 instead. + return Err(classify_unpaid(snapshot, max_payment)); + } + if !status.is_success() { + let body = response.text().await.unwrap_or_default(); + return Err(CliError::Transaction { + code: ErrorCode::PaymentSettlementFailed, + message: format!( + "Agent gateway returned {} after payment: {}", + status.as_u16(), + crate::api::truncate_for_error(&body) + ), + tx_hash: None, + suggestion: Some( + "The payment may have settled without a usable response — check your wallet before retrying.".into(), + ), + }); + } + + let receipt = decode_receipt(response.headers(), &payer_addr, max_payment); + + let body = response.text().await.map_err(|e| CliError::Api { + code: ErrorCode::PaymentSettlementFailed, + message: format!("Failed to read gateway response body: {e}"), + status: Some(status.as_u16()), + details: None, + suggestion: None, + })?; + let value: T = serde_json::from_str(&body).map_err(|e| CliError::Api { + code: ErrorCode::ApiError, + message: format!("Failed to parse gateway response: {e}"), + status: Some(status.as_u16()), + details: Some(serde_json::json!({ + "body_preview": crate::api::truncate_for_error(&body) + })), + suggestion: None, + })?; + + Ok((value, receipt)) +} + +/// Build the receipt from the `PAYMENT-RESPONSE` header. The payer is always +/// known (we signed); the rest is best-effort. +fn decode_receipt( + headers: &reqwest::header::HeaderMap, + payer_addr: &str, + amount: U256, +) -> PaymentReceipt { + let settlement = headers + .get("payment-response") + .and_then(|h| h.to_str().ok()) + .and_then(|h| STANDARD.decode(h).ok()) + .and_then(|b| serde_json::from_slice::(&b).ok()) + .unwrap_or_default(); + + PaymentReceipt { + method: super::PaymentMethod::X402Evm.as_str(), + network: settlement.network, + tx_hash: settlement.transaction, + // The payer is authoritatively the wallet we signed with — don't trust + // a gateway-reported payer over what we know locally. + payer: Some(payer_addr.to_string()), + amount_base_units: Some(amount.to_string()), + } +} + +/// Map a `402` that survived the middleware (selector returned `None`) to a +/// precise, safe error. Nothing was signed in either branch. +fn classify_unpaid( + snapshot: (bool, usize, Option), + cap: U256, +) -> CliError { + let (invoked, count, min_amount) = snapshot; + if invoked && count > 0 { + if let Some(min) = min_amount { + if min > cap { + return CliError::Config { + code: ErrorCode::PaymentExceedsLimit, + message: format!( + "Cheapest x402 payment is {min} base units but --max-payment cap is {cap} — refused before signing. Nothing was spent." + ), + }; + } + } + } + CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: "The agent gateway offered no x402 payment scheme this CLI can satisfy on EVM" + .into(), + status: Some(402), + details: None, + suggestion: Some("Ensure --pay x402-evm targets a gateway endpoint that accepts EVM (Base) USDC payment.".into()), + } +} + +/// Map a middleware send error. Uses the captured selection outcome (rather +/// than fragile error-string matching) to keep the over-cap case precise. +fn classify_send_error( + message: &str, + snapshot: (bool, usize, Option), + cap: U256, +) -> CliError { + let (invoked, count, min_amount) = snapshot; + if invoked { + // The 402 was parsed, so this is a payment-decision outcome. + if count == 0 { + return classify_unpaid(snapshot, cap); + } + if let Some(min) = min_amount { + if min > cap { + return classify_unpaid(snapshot, cap); + } + } + // A candidate within cap was selected → failure is in signing or the + // paid resubmission. Money may or may not have moved. + return CliError::Transaction { + code: ErrorCode::PaymentSettlementFailed, + message: format!("x402 payment failed after selecting a payable option: {message}"), + tx_hash: None, + suggestion: Some( + "Check your wallet before retrying — a payment may have been submitted.".into(), + ), + }; + } + // Selector never ran: either the 402 couldn't be parsed into payment + // requirements, or it was a transport error before any 402. Distinguish so + // an agent doesn't retry a malformed-challenge as if it were a network blip. + let lower = message.to_lowercase(); + if lower.contains("parse") || lower.contains("402") { + return CliError::Api { + code: ErrorCode::PaymentChallengeInvalid, + message: format!("x402 gateway returned a 402 this CLI couldn't parse: {message}"), + status: Some(402), + details: None, + suggestion: None, + }; + } + CliError::Api { + code: ErrorCode::NetworkError, + message: format!("x402 request to the agent gateway failed: {message}"), + status: None, + details: None, + suggestion: Some("Check your network connection and try again.".into()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn snap(invoked: bool, count: usize, min: Option) -> (bool, usize, Option) { + (invoked, count, min.map(U256::from)) + } + + #[test] + fn over_cap_maps_to_exceeds_limit_nothing_spent() { + // Cheapest option 100000 base units, cap 50000 → refuse. + let err = classify_unpaid(snap(true, 1, Some(100_000)), U256::from(50_000u64)); + assert_eq!(err.code(), ErrorCode::PaymentExceedsLimit); + } + + #[test] + fn no_candidates_maps_to_challenge_invalid() { + let err = classify_unpaid(snap(true, 0, None), U256::from(50_000u64)); + assert_eq!(err.code(), ErrorCode::PaymentChallengeInvalid); + } + + #[test] + fn transport_error_before_402_is_network() { + let err = classify_send_error("connection reset", snap(false, 0, None), U256::from(50_000u64)); + assert_eq!(err.code(), ErrorCode::NetworkError); + } + + #[test] + fn send_error_after_selection_is_settlement() { + let err = classify_send_error("signing boom", snap(true, 1, Some(10_000)), U256::from(50_000u64)); + assert_eq!(err.code(), ErrorCode::PaymentSettlementFailed); + } + + #[test] + fn send_error_over_cap_is_exceeds_limit() { + // The real path: selector returns None → middleware Err → classify via + // the captured snapshot. Over-cap must map to "nothing spent". + let err = classify_send_error( + "No matching payment option found", + snap(true, 1, Some(100_000)), + U256::from(50_000u64), + ); + assert_eq!(err.code(), ErrorCode::PaymentExceedsLimit); + } + + #[test] + fn unparseable_402_is_challenge_invalid_not_network() { + let err = classify_send_error( + "Failed to parse 402 response: bad json", + snap(false, 0, None), + U256::from(50_000u64), + ); + assert_eq!(err.code(), ErrorCode::PaymentChallengeInvalid); + } +} diff --git a/src/telemetry.rs b/src/telemetry.rs index ac57b07..a414c44 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -7,7 +7,7 @@ //! `option_env!`, so dev and CI builds have no key and this module does //! nothing — tests can't accidentally phone home. //! - **Opt-out, three ways.** `0x config set telemetry.enabled false`, the -//! `ZEROX_TELEMETRY` env var (falsy), or the cross-tool `DO_NOT_TRACK` +//! `ZEROEX_TELEMETRY` env var (falsy), or the cross-tool `DO_NOT_TRACK` //! standard. Resolution is the pure [`telemetry_allowed`] function. //! - **Hybrid delivery.** Events spool to `~/.0x-config/telemetry-queue.jsonl`. //! [`init`] spawns a background flush of the backlog that overlaps the @@ -47,7 +47,7 @@ struct OptOutInputs { } /// Whether telemetry may run this invocation. Order: no compiled key → off; -/// `DO_NOT_TRACK` set to anything other than empty/`0` → off; `ZEROX_TELEMETRY` +/// `DO_NOT_TRACK` set to anything other than empty/`0` → off; `ZEROEX_TELEMETRY` /// falsy → off; config `telemetry.enabled == false` → off. fn telemetry_allowed(i: &OptOutInputs) -> bool { if i.compiled_key.is_none() { @@ -111,7 +111,7 @@ pub fn init() -> Option { let inputs = OptOutInputs { compiled_key: AMPLITUDE_API_KEY, - zerox_telemetry: std::env::var("ZEROX_TELEMETRY").ok(), + zerox_telemetry: std::env::var("ZEROEX_TELEMETRY").ok(), do_not_track: std::env::var("DO_NOT_TRACK").ok(), config_enabled: config.telemetry.enabled, }; diff --git a/src/wallet/evm.rs b/src/wallet/evm.rs index 4db44d6..fad036c 100644 --- a/src/wallet/evm.rs +++ b/src/wallet/evm.rs @@ -5,7 +5,7 @@ use std::str::FromStr; /// Load an EVM signer from CLI flag, env var, OS keyring, or config file. /// -/// Priority: `--wallet` flag → `ZEROX_EVM_PRIVATE_KEY` env → OS keyring → config plaintext. +/// Priority: `--wallet` flag → `ZEROEX_EVM_PRIVATE_KEY` env → OS keyring → config plaintext. /// Keyring failures (no daemon, denied access, etc.) fall through silently to /// the config file rather than aborting the load — `config set` is the place /// where keyring errors should surface, not arbitrary read paths. @@ -15,7 +15,7 @@ pub fn load_evm_signer( ) -> Result { let key = if let Some(wallet_arg) = cli_wallet { wallet_arg.to_string() - } else if let Ok(env_key) = std::env::var("ZEROX_EVM_PRIVATE_KEY") { + } else if let Ok(env_key) = std::env::var("ZEROEX_EVM_PRIVATE_KEY") { env_key } else if let Some(keyring_key) = crate::wallet::keyring_store::get(crate::wallet::keyring_store::keys::WALLET_EVM) @@ -27,7 +27,7 @@ pub fn load_evm_signer( } else { return Err(CliError::Wallet { code: ErrorCode::WalletNotFound, - message: "No EVM wallet configured. Set via --wallet, ZEROX_EVM_PRIVATE_KEY env var, or 'config set wallet.evm '".into(), + message: "No EVM wallet configured. Set via --wallet, ZEROEX_EVM_PRIVATE_KEY env var, or 'config set wallet.evm '".into(), }); }; diff --git a/src/wallet/keyring_store.rs b/src/wallet/keyring_store.rs index 1973c18..6182820 100644 --- a/src/wallet/keyring_store.rs +++ b/src/wallet/keyring_store.rs @@ -53,7 +53,7 @@ mod real { CliError::Wallet { code: ErrorCode::KeyringUnavailable, message: format!( - "OS keyring error: {e}. Use --plaintext on `config set`, or set ZEROX_EVM_PRIVATE_KEY / ZEROX_SOLANA_KEYPAIR." + "OS keyring error: {e}. Use --plaintext on `config set`, or set ZEROEX_EVM_PRIVATE_KEY / ZEROEX_SOLANA_KEYPAIR." ), } } diff --git a/src/wallet/solana.rs b/src/wallet/solana.rs index 5f42127..5f4ca5d 100644 --- a/src/wallet/solana.rs +++ b/src/wallet/solana.rs @@ -5,7 +5,7 @@ use solana_sdk::signer::Signer; /// Load a Solana keypair from CLI flag, env var, OS keyring, or config file. /// -/// Priority: `--wallet` flag → `ZEROX_SOLANA_KEYPAIR` env → OS keyring → config plaintext. +/// Priority: `--wallet` flag → `ZEROEX_SOLANA_KEYPAIR` env → OS keyring → config plaintext. /// The keyring stores key material (base58 or JSON array). File paths are stored /// in the config file because the path itself isn't secret. Keyring failures /// fall through silently to the config file. @@ -15,7 +15,7 @@ pub fn load_solana_keypair( ) -> Result { let source = if let Some(wallet_arg) = cli_wallet { wallet_arg.to_string() - } else if let Ok(env_val) = std::env::var("ZEROX_SOLANA_KEYPAIR") { + } else if let Ok(env_val) = std::env::var("ZEROEX_SOLANA_KEYPAIR") { env_val } else if let Some(keyring_val) = crate::wallet::keyring_store::get(crate::wallet::keyring_store::keys::WALLET_SOLANA) @@ -27,7 +27,7 @@ pub fn load_solana_keypair( } else { return Err(CliError::Wallet { code: ErrorCode::WalletNotFound, - message: "No Solana wallet configured. Set via --wallet, ZEROX_SOLANA_KEYPAIR env var, or 'config set wallet.solana '".into(), + message: "No Solana wallet configured. Set via --wallet, ZEROEX_SOLANA_KEYPAIR env var, or 'config set wallet.solana '".into(), }); }; diff --git a/src/wallet/tron.rs b/src/wallet/tron.rs index aa7c5d2..b6c96c1 100644 --- a/src/wallet/tron.rs +++ b/src/wallet/tron.rs @@ -45,7 +45,7 @@ fn derive_address(signing_key: &SigningKey) -> String { /// Load a Tron signer from CLI flag, env var, OS keyring, or config file. /// -/// Priority: `--wallet` flag → `ZEROX_TRON_PRIVATE_KEY` env → OS keyring → +/// Priority: `--wallet` flag → `ZEROEX_TRON_PRIVATE_KEY` env → OS keyring → /// `config.wallet.tron` (config-file plaintext). pub fn load_tron_signer( config: &AppConfig, @@ -53,7 +53,7 @@ pub fn load_tron_signer( ) -> Result { let key = if let Some(wallet_arg) = cli_wallet { wallet_arg.to_string() - } else if let Ok(env_key) = std::env::var("ZEROX_TRON_PRIVATE_KEY") { + } else if let Ok(env_key) = std::env::var("ZEROEX_TRON_PRIVATE_KEY") { env_key } else if let Some(keyring_key) = crate::wallet::keyring_store::get(crate::wallet::keyring_store::keys::WALLET_TRON) @@ -65,7 +65,7 @@ pub fn load_tron_signer( } else { return Err(CliError::Wallet { code: ErrorCode::WalletNotFound, - message: "No Tron wallet configured. Set via --wallet, ZEROX_TRON_PRIVATE_KEY env var, or 'config set wallet.tron '".into(), + message: "No Tron wallet configured. Set via --wallet, ZEROEX_TRON_PRIVATE_KEY env var, or 'config set wallet.tron '".into(), }); }; diff --git a/tests/cli_output.rs b/tests/cli_output.rs index 971916c..cfa410c 100644 --- a/tests/cli_output.rs +++ b/tests/cli_output.rs @@ -181,7 +181,7 @@ fn test_swap_no_wallet_json_error() { let (mut cmd, _tmp) = cmd_in_temp_home(); // API key must be set, otherwise the failure surfaces as API_KEY_MISSING // before we ever reach the wallet load. - cmd.env("ZEROX_API_KEY", "dummy-test-key"); + cmd.env("ZEROEX_API_KEY", "dummy-test-key"); let output = cmd .args([ "swap", @@ -212,7 +212,7 @@ fn test_swap_no_wallet_json_error() { #[test] fn test_swap_no_wallet_envelope_error() { let (mut cmd, _tmp) = cmd_in_temp_home(); - cmd.env("ZEROX_API_KEY", "dummy-test-key"); + cmd.env("ZEROEX_API_KEY", "dummy-test-key"); let output = cmd .args([ "swap", @@ -246,8 +246,8 @@ fn test_swap_no_wallet_envelope_error() { #[test] fn test_solana_swap_no_wallet() { let (mut cmd, _tmp) = cmd_in_temp_home(); - cmd.env("ZEROX_API_KEY", "dummy-test-key") - .env_remove("ZEROX_SOLANA_KEYPAIR"); + cmd.env("ZEROEX_API_KEY", "dummy-test-key") + .env_remove("ZEROEX_SOLANA_KEYPAIR"); let output = cmd .args([ "swap", @@ -276,7 +276,7 @@ fn test_solana_swap_no_wallet() { #[test] fn test_cross_chain_no_wallet() { let (mut cmd, _tmp) = cmd_in_temp_home(); - cmd.env("ZEROX_API_KEY", "dummy-test-key"); + cmd.env("ZEROEX_API_KEY", "dummy-test-key"); let output = cmd .args([ "cross-chain", @@ -481,7 +481,7 @@ fn test_telemetry_inert_without_compiled_key() { /// Opt-out env vars are honored without error and leave no telemetry trace. #[test] fn test_telemetry_opt_out_env_leaves_no_trace() { - for (k, v) in [("DO_NOT_TRACK", "1"), ("ZEROX_TELEMETRY", "0")] { + for (k, v) in [("DO_NOT_TRACK", "1"), ("ZEROEX_TELEMETRY", "0")] { let (mut cmd, tmp) = cmd_in_temp_home(); cmd.env(k, v).args(["chains", "-o", "json"]).assert().success(); let queue = tmp.path().join(".0x-config/telemetry-queue.jsonl"); @@ -534,7 +534,7 @@ fn test_completions_zsh() { fn test_price_without_api_key_exits_5() { let (mut cmd, _tmp) = cmd_in_temp_home(); let output = cmd - .env_remove("ZEROX_API_KEY") + .env_remove("ZEROEX_API_KEY") .args([ "price", "--chain", @@ -574,10 +574,10 @@ fn test_swap_zero_amount_rejected() { let (mut cmd, _tmp) = cmd_in_temp_home(); let output = cmd .env( - "ZEROX_EVM_PRIVATE_KEY", + "ZEROEX_EVM_PRIVATE_KEY", "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", ) - .env("ZEROX_API_KEY", "test") + .env("ZEROEX_API_KEY", "test") .args([ "swap", "--chain", @@ -608,8 +608,8 @@ fn test_dry_run_flag_accepted() { // We run with no wallet so the call short-circuits at WALLET_NOT_FOUND // before any RPC / API call. let (mut cmd, _tmp) = cmd_in_temp_home(); - cmd.env("ZEROX_API_KEY", "dummy-test-key") - .env_remove("ZEROX_EVM_PRIVATE_KEY"); + cmd.env("ZEROEX_API_KEY", "dummy-test-key") + .env_remove("ZEROEX_EVM_PRIVATE_KEY"); let output = cmd .args([ "swap", @@ -659,3 +659,97 @@ fn swap_rejects_tron_with_cross_chain_hint() { let stdout = String::from_utf8_lossy(&out.stdout); assert!(stdout.contains("cross-chain"), "expected cross-chain hint, got: {stdout}"); } + +// ─── Agent payments (--pay) ─────────────────────────────────── + +#[test] +fn price_help_lists_pay_flags() { + cmd().args(["price", "--help"]).assert().success().stdout( + predicate::str::contains("--pay") + .and(predicate::str::contains("--max-payment")) + .and(predicate::str::contains("x402-evm")) + .and(predicate::str::contains("mpp")), + ); +} + +#[test] +fn swap_help_lists_pay_flag() { + cmd() + .args(["swap", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--pay").and(predicate::str::contains("--tempo-rpc"))); +} + +#[test] +fn pay_invalid_value_rejected_by_clap() { + // Unknown --pay value is an arg-parse error listing the valid choices. + cmd() + .args([ + "price", "--chain", "base", + "--sell", "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "--buy", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "--amount", "100000", "--pay", "bogus", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("x402-evm").and(predicate::str::contains("mpp"))); +} + +#[test] +fn pay_rejected_on_solana_chain() { + // --pay is EVM-only; rejected before any wallet/API-key load. + let (mut cmd, _tmp) = cmd_in_temp_home(); + let out = cmd + .args([ + "price", "--chain", "solana", + "--sell", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "--buy", "So11111111111111111111111111111111111111112", + "--amount", "1000000", "--pay", "x402-evm", "-o", "json", + ]) + .output() + .expect("failed to run"); + assert!(!out.status.success()); + let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("invalid JSON"); + assert_eq!(json["code"], "INPUT_INVALID"); +} + +#[test] +fn pay_rejected_with_gasless() { + let (mut cmd, _tmp) = cmd_in_temp_home(); + let out = cmd + .args([ + "price", "--chain", "base", + "--sell", "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "--buy", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "--amount", "100000", "--pay", "x402-evm", "--gasless", "-o", "json", + ]) + .output() + .expect("failed to run"); + assert!(!out.status.success()); + let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("invalid JSON"); + assert_eq!(json["code"], "INPUT_INVALID"); +} + +#[test] +fn pay_invalid_max_payment_rejected() { + // A non-positive cap must fail closed (never become "unlimited"). Uses a + // throwaway key so the wallet loads and we reach the cap parse. + let (mut cmd, _tmp) = cmd_in_temp_home(); + let out = cmd + .env( + "ZEROEX_EVM_PRIVATE_KEY", + "0x0000000000000000000000000000000000000000000000000000000000000001", + ) + .args([ + "price", "--chain", "base", + "--sell", "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "--buy", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "--amount", "100000", "--pay", "x402-evm", "--max-payment", "0", "-o", "json", + ]) + .output() + .expect("failed to run"); + assert!(!out.status.success()); + let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("invalid JSON"); + assert_eq!(json["code"], "INPUT_INVALID"); +}