diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md deleted file mode 100644 index c5d1df1..0000000 --- a/.github/ISSUE_TEMPLATE/task.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Task -about: A generic call forwarder related task -title: "" -labels: "" -assignees: "" ---- - -## Context - - - -## Scope - - - -## Definition of Done - - - -## Outcome - - diff --git a/.github/workflows/bindings.yml b/.github/workflows/crates.yml similarity index 68% rename from .github/workflows/bindings.yml rename to .github/workflows/crates.yml index 150eb0c..2e87417 100644 --- a/.github/workflows/bindings.yml +++ b/.github/workflows/crates.yml @@ -1,4 +1,4 @@ -name: Bindings +name: Crates on: push: @@ -8,10 +8,13 @@ on: jobs: tests: - name: Bindings Tests + name: Crates Tests runs-on: macos-latest env: ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} + QUEUE_BASE_URL: ${{ secrets.QUEUE_BASE_URL }} + QUEUE_AUTH_TOKEN: ${{ secrets.QUEUE_AUTH_TOKEN }} + RISC0_SKIP_BUILD_KERNELS: true steps: - name: Checkout repository @@ -41,17 +44,17 @@ jobs: with: path: | target - key: rust-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + key: rust-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} restore-keys: rust- - name: Run rustfmt - run: just bindings-fmt-check + run: just crates-fmt-check - - name: Build Bindings - run: just bindings-build + - name: Build + run: just crates-build - - name: Lint Bindings - run: just bindings-lint + - name: Lint + run: just crates-lint - name: Run Tests - run: just bindings-test + run: just crates-test diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..977826c --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,29 @@ +# Generic Call Forwarder + +The forwarder contract and integration-test layer that lets ARM resources trigger +**arbitrary EVM calls** through the [Anoma EVM protocol adapter](https://github.com/anoma/pa-evm). + +## Language + +**Generic Call Forwarder**: +The EVM contract through which the protocol adapter executes a list of arbitrary +calls on behalf of an action. Use "generic call forwarder" for the contract. + +**Generic Call**: +A single `(to, value, data)` EVM call carried by a resource and executed by the +forwarder. A resource's label commits to the calls it authorizes. + +**Forwarder**: +The generic call forwarder contract (above) — the same forwarder role the protocol +adapter drives in other applications. + +## Note on upstream names + +`generic_call_library` and `generic_call_witness` (the resource logic, witness +types, and underlying circuit) live in `anoma/generic-call-resource` and keep those +names — they are immutable from this repo's perspective. + +The integration-test crate reuses the AnomaPay ERC20 wrap / transfer / unwrap +fixtures (via a dev-dependency on `anomapay-erc20-forwarder-integration-test`) to +move WETH into a shielded resource before driving a generic call; see that repo's +`CONTEXT.md` for those terms. diff --git a/Cargo.lock b/Cargo.lock index 3f38254..f809724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1633,7 +1633,7 @@ dependencies = [ "alloy 2.0.5", "alloy-chains", "anoma-generic-call-library", - "anoma-pa-evm-bindings", + "anoma-pa-evm-bindings 2.2.0", "dotenvy", "serde", "serde_json", @@ -1641,6 +1641,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "anoma-generic-call-forwarder-integration-test" +version = "0.2.0" +dependencies = [ + "alloy 2.0.5", + "alloy-chains", + "anoma-generic-call-forwarder-bindings", + "anoma-generic-call-library", + "anoma-generic-call-witness", + "anoma-pa-evm-integration-test", + "anoma-pa-testkit", + "anoma-rm-risc0", + "anomapay-erc20-forwarder-integration-test", + "anyhow", + "bincode", + "regex", + "risc0-zkvm", + "rstest", + "tokio", +] + [[package]] name = "anoma-generic-call-library" version = "1.0.0-rc.0" @@ -1681,6 +1702,68 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "anoma-pa-evm-bindings" +version = "3.0.0-rc.1" +source = "git+https://github.com/anoma/pa-evm?rev=95179723a8515337a90276d47374237c3e5a7784#95179723a8515337a90276d47374237c3e5a7784" +dependencies = [ + "alloy 2.0.5", + "alloy-chains", + "anoma-rm-risc0", + "dotenvy", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "anoma-pa-evm-integration-test" +version = "0.1.0" +source = "git+https://github.com/anoma/pa-evm?rev=95179723a8515337a90276d47374237c3e5a7784#95179723a8515337a90276d47374237c3e5a7784" +dependencies = [ + "alloy 2.0.5", + "alloy-chains", + "anoma-pa-evm-bindings 3.0.0-rc.1", + "anoma-pa-testkit", + "anoma-risc0-verifier-bindings", + "anoma-rm-risc0", + "anyhow", + "dotenvy", + "hex", + "risc0-zkvm", +] + +[[package]] +name = "anoma-pa-testkit" +version = "0.2.0" +source = "git+https://github.com/anoma/pa-testkit?rev=74cf6ad7cd488021128c34662e68171c7ae28e6d#74cf6ad7cd488021128c34662e68171c7ae28e6d" +dependencies = [ + "anoma-rm-risc0", + "anoma-rm-risc0-gadgets", + "anyhow", + "bincode", + "futures", + "heliax-ap-orchestrator-sdk", + "hex", + "k256", + "mockall", + "regex", + "risc0-zkvm", + "serde", + "sha2 0.11.0", + "tokio", +] + +[[package]] +name = "anoma-risc0-verifier-bindings" +version = "1.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9de2aea49f20548795e42423d7dd0ea32f3ec1c6737902aba0623e9033b2b5" +dependencies = [ + "alloy 2.0.5", + "serde", +] + [[package]] name = "anoma-rm-risc0" version = "1.1.1" @@ -1718,6 +1801,44 @@ dependencies = [ "zeroize", ] +[[package]] +name = "anomapay-erc20-forwarder-bindings" +version = "3.0.0-rc.3" +source = "git+https://github.com/anoma/anomapay-erc20-forwarder?rev=ccdf93b869521261c8ebc2d05145abb782a8cc4d#ccdf93b869521261c8ebc2d05145abb782a8cc4d" +dependencies = [ + "alloy 2.0.5", + "alloy-chains", + "dotenvy", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "anomapay-erc20-forwarder-integration-test" +version = "0.2.0" +source = "git+https://github.com/anoma/anomapay-erc20-forwarder?rev=ccdf93b869521261c8ebc2d05145abb782a8cc4d#ccdf93b869521261c8ebc2d05145abb782a8cc4d" +dependencies = [ + "alloy 2.0.5", + "alloy-chains", + "anoma-pa-evm-integration-test", + "anoma-pa-testkit", + "anoma-rm-risc0", + "anoma-rm-risc0-gadgets", + "anomapay-erc20-forwarder-bindings", + "anyhow", + "bincode", + "risc0-zkvm", + "transfer_library", + "transfer_witness", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.102" @@ -2861,9 +2982,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.63" +version = "1.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", "jobserver", @@ -2890,8 +3011,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] @@ -3338,7 +3461,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -3513,6 +3635,12 @@ dependencies = [ "clap", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -3851,6 +3979,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -3934,6 +4071,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + [[package]] name = "futures-util" version = "0.3.32" @@ -4202,6 +4345,40 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "heliax-ap-orchestrator-sdk" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56eef6564772c8be23bc7ebe41ae1ea1cab830a2873f30e27335fe57bf3916d5" +dependencies = [ + "heliax-ap-orchestrator-shared", + "reqwest 0.12.28", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "heliax-ap-orchestrator-shared" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbdf274755868c87e16da5ec59e36c9bc037813cdc4844d8cc2f61be942825c" +dependencies = [ + "aes-gcm", + "base64", + "chrono", + "hex", + "pem", + "rand 0.8.6", + "ring", + "serde", + "serde_json", + "thiserror 2.0.18", + "utoipa", + "uuid", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -4795,6 +4972,7 @@ dependencies = [ "once_cell", "serdect", "sha2 0.10.9", + "signature", ] [[package]] @@ -5173,6 +5351,32 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mockall" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ndarray" version = "0.16.1" @@ -5654,6 +5858,32 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -5693,6 +5923,30 @@ dependencies = [ "toml_edit 0.25.12+spec-1.1.0", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -6132,6 +6386,12 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.12.28" @@ -6626,6 +6886,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn 2.0.117", + "unicode-ident", +] + [[package]] name = "ruint" version = "1.18.0" @@ -7205,6 +7494,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -7436,6 +7735,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "textwrap" version = "0.16.2" @@ -7493,12 +7798,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "fc1aa89044e7786ffb2ec017acb22cb7de5b0be46d0f21aea2b224b8561e5db2" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -7508,15 +7812,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "9d3bfe86347f0cc659f586f01e26303ccd32418f26f30c7b0309b3ca3a07d695" dependencies = [ "num-conv", "time-core", @@ -7556,7 +7860,9 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", @@ -7833,6 +8139,37 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "transfer_library" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c4d5d5b92eb32a8d9ea6a6672e7f6da6a763fefaf3a232880bad410a264c7" +dependencies = [ + "anoma-rm-risc0", + "anoma-rm-risc0-gadgets", + "hex", + "k256", + "lazy_static", + "serde", + "transfer_witness", +] + +[[package]] +name = "transfer_witness" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e70440e869b2b4a47e064e31a3d3116e7f9f76dd6e9e287a967e575bda09710" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "anoma-rm-risc0", + "anoma-rm-risc0-gadgets", + "bincode", + "k256", + "rand 0.9.4", + "serde", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -8039,13 +8376,40 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", + "uuid", +] + [[package]] name = "uuid" version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ + "getrandom 0.4.2", "js-sys", + "serde_core", "wasm-bindgen", ] @@ -8109,9 +8473,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen 0.57.1", ] @@ -8792,18 +9156,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index a21befb..a01e435 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["bindings"] +members = ["crates/bindings", "crates/integration-test"] [workspace.package] keywords = ["anoma", "evm", "protocol-adapter", "forwarder"] @@ -14,11 +14,28 @@ edition = "2024" alloy = { version = "2.0.5", features = ["full", "eip712", "node-bindings"] } alloy-chains = "0.2.33" anoma-generic-call-library = "1.0.0-rc.0" +anoma-generic-call-witness = "1.0.0-rc.0" anoma-pa-evm-bindings = "2.2.0" +anoma-rm-risc0 = { version = "1.1.1", default-features = false } +anoma-rm-risc0-gadgets = "1.1.1" +anyhow = "1.0" +bincode = "1.3.3" dotenvy = "0.15.7" +regex = "1.12.3" +risc0-zkvm = "3.0.3" +rstest = "0.26.1" serde = { version = "1.0.228", default-features = false } serde_json = "1.0" thiserror = "2.0.18" tokio = { version = "1.52", features = ["rt-multi-thread"] } -anoma-generic-call-forwarder-bindings = { path = "bindings" } + +# Forwarder bindings (this repo) +anoma-generic-call-forwarder-bindings = { path = "crates/bindings" } + +# Upstream test crates. anoma-pa-testkit and pa-evm are pinned by git rev (see +# ADR-0002); the erc20 integration-test crate stays a local path until erc20 is +# pushed and pinned by git rev too. For cross-repo dev, override with `[patch]`. +anoma-pa-testkit = { git = "https://github.com/anoma/pa-testkit", rev = "74cf6ad7cd488021128c34662e68171c7ae28e6d" } +anoma-pa-evm-integration-test = { git = "https://github.com/anoma/pa-evm", rev = "95179723a8515337a90276d47374237c3e5a7784" } +anomapay-erc20-forwarder-integration-test = { git = "https://github.com/anoma/anomapay-erc20-forwarder", rev = "ccdf93b869521261c8ebc2d05145abb782a8cc4d" } diff --git a/README.md b/README.md index 63be1af..493d605 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[](https://github.com/anoma/generic-call-forwarder/actions/workflows/contracts.yml) [](https://soldeer.xyz/project/anoma--generic--call) [](https://raw.githubusercontent.com/anoma/generic-call-forwarder/refs/heads/main/contracts/LICENSE) +[](https://github.com/anoma/generic-call-forwarder/actions/workflows/contracts.yml) [](https://soldeer.xyz/project/anoma-generic-call-forwarder) [](https://raw.githubusercontent.com/anoma/generic-call-forwarder/refs/heads/main/contracts/LICENSE) -[](https://github.com/anoma/generic-call-forwarder/actions/workflows/bindings.yml) [](https://crates.io/crates/anoma-generic-call-forwarder-bindings) [](https://raw.githubusercontent.com/anoma/anoma-generic-call-forwarder/refs/heads/main/bindings/LICENSE) +[](https://github.com/anoma/generic-call-forwarder/actions/workflows/crates.yml) [](https://crates.io/crates/anoma-generic-call-forwarder-bindings) [](https://raw.githubusercontent.com/anoma/generic-call-forwarder/refs/heads/main/crates/bindings/LICENSE) # Generic Call Forwarder @@ -12,9 +12,10 @@ This monorepo is structured as follows: ``` . -├── audits -├── bindings ├── contracts +├── crates +│ ├── bindings +│ └── integration-test ├── Cargo.lock ├── Cargo.toml ├── README.md @@ -23,8 +24,10 @@ This monorepo is structured as follows: The [contracts](./contracts/) folder contains the contracts written in [Solidity](https://soliditylang.org/) as well as [Foundry forge](https://book.getfoundry.sh/forge/) tests and deploy scripts. -The [bindings](./bindings/) folder provides [Rust](https://www.rust-lang.org/) bindings for the conversion of Rust and [RISC Zero](https://risczero.com/) types into [EVM types](https://docs.soliditylang.org/en/latest/types.html) and exposes the deployment addresses on the different supported networks using the [alloy-rs](https://github.com/alloy-rs) -library. +The [crates](./crates/) folder contains the Rust workspace: + +- [bindings](./crates/bindings/) provides [Rust](https://www.rust-lang.org/) bindings for the forwarder contract and exposes its deployment addresses on the different supported networks using the [alloy-rs](https://github.com/alloy-rs) library. +- [integration-test](./crates/integration-test/) contains the Rust integration and e2e tests that deploy the forwarder against a local or forked chain and exercise generic EVM calls with risc0-proven transactions. ## Audits diff --git a/contracts/README.md b/contracts/README.md index f5a4ff4..6804821 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,4 +1,4 @@ -[](https://github.com/anoma/generic-call-forwarder/actions/workflows/contracts.yml) [](https://soldeer.xyz/project/anoma-generic-call-forwarder) [](https://raw.githubusercontent.com/anoma/generic-call-forwarder/refs/heads/main/bindings/LICENSE) +[](https://github.com/anoma/generic-call-forwarder/actions/workflows/contracts.yml) [](https://soldeer.xyz/project/anoma-generic-call-forwarder) [](https://raw.githubusercontent.com/anoma/generic-call-forwarder/refs/heads/main/contracts/LICENSE) # Generic Call Forwarder Contract diff --git a/contracts/soldeer.lock b/contracts/soldeer.lock deleted file mode 100644 index a6758bc..0000000 --- a/contracts/soldeer.lock +++ /dev/null @@ -1,41 +0,0 @@ -[[dependencies]] -name = "@openzeppelin-contracts" -version = "5.6.1" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_6_1_15-03-2026_09:19:50_contracts.zip" -checksum = "a3b6bc661be858c7c27f60a1708cbebe8c71034b4cc1e9fe270d0a05b069352f" -integrity = "bce03af7ada1eee21a7fff393f238bcd7cd75a022a4db55ffb6b0dbb32433d35" - -[[dependencies]] -name = "anoma-forwarder-bases" -version = "1.0.0-rc.3" -url = "https://soldeer-revisions.s3.amazonaws.com/anoma-forwarder-bases/1_0_0-rc_3_11-06-2026_11:53:46_contracts.zip" -checksum = "ef9802c435049e9139020f1a2ddc6decece9afcd689275e9bb0ff566a36ec1fd" -integrity = "2a27fa3b3a490ed45d75191fc561dff9d4eab202201d8e71dd3d76b5051b9f06" - -[[dependencies]] -name = "anomapay-erc20-forwarder" -version = "1.1.0-rc.2" -url = "https://soldeer-revisions.s3.amazonaws.com/anomapay-erc20-forwarder/1_1_0-rc_2_11-06-2026_13:35:35_contracts.zip" -checksum = "6a283016d4019116a814576796957aa8cf38be81cc9ce2057f9ca7a4a948dd90" -integrity = "a21f6ff5e5b010e6fb0661733de5c15142b9092b98e0d2e4935c35ce0053422e" - -[[dependencies]] -name = "forge-std" -version = "1.16.1" -url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_16_1_08-05-2026_08:51:16_forge-std-1.16.zip" -checksum = "839b61832925c7152c7b6dffbfa4998d9e606211179bd8f604733124e8a7cb57" -integrity = "60e55d10150354ca4a1e2985c5456c834b92b82ef85ab0e1d92a7786cddbd219" - -[[dependencies]] -name = "solady" -version = "0.1.26" -url = "https://soldeer-revisions.s3.amazonaws.com/solady/0_1_26_25-08-2025_15:30:06_solady.zip" -checksum = "9872ac7cfd32c1eba32800508a1325c49f4a4aa8c6f670454db91971a583e26b" -integrity = "5da4b5ca9cbad98812a4b75ad528ff34c72a0b84433204be6d1420c81de1d6ff" - -[[dependencies]] -name = "uniswap-permit2" -version = "0x000000000022D473030F116dDEE9F6B43aC78BA3" -url = "https://soldeer-revisions.s3.amazonaws.com/uniswap-permit2/0x000000000022D473030F116dDEE9F6B43aC78BA3_11-11-2024_08:00:23_permit2.zip" -checksum = "d4fb59a5d0b9bb91c6800b55a0fa4037dcf4d2a17e55750ffb369572a4dd5906" -integrity = "775a6885592b17bdc4556ca41127bf30c0d117264a6a494e37865f1ca28bcb0c" diff --git a/contracts/test/mocks/DexRouter.m.sol b/contracts/test/mocks/DexRouter.m.sol index 2b9eafe..d4afad6 100644 --- a/contracts/test/mocks/DexRouter.m.sol +++ b/contracts/test/mocks/DexRouter.m.sol @@ -12,6 +12,9 @@ import { ISignatureTransfer } from "uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/src/interfaces/ISignatureTransfer.sol"; +/// @notice A minimal DEX router mock swapping `amountIn` of `tokenIn` for a fixed `amountOutMin` of `tokenOut`. +/// The variants differ only in how the input token is pulled from the caller: a classic ERC-20 allowance, +/// a Permit2 allowance, or a Permit2 signature. contract DexRouterMock { using SafeERC20 for IERC20; diff --git a/bindings/Cargo.toml b/crates/bindings/Cargo.toml similarity index 100% rename from bindings/Cargo.toml rename to crates/bindings/Cargo.toml diff --git a/bindings/LICENSE b/crates/bindings/LICENSE similarity index 100% rename from bindings/LICENSE rename to crates/bindings/LICENSE diff --git a/bindings/README.md b/crates/bindings/README.md similarity index 100% rename from bindings/README.md rename to crates/bindings/README.md diff --git a/bindings/deployments.json b/crates/bindings/deployments.json similarity index 100% rename from bindings/deployments.json rename to crates/bindings/deployments.json diff --git a/bindings/src/addresses.rs b/crates/bindings/src/addresses.rs similarity index 100% rename from bindings/src/addresses.rs rename to crates/bindings/src/addresses.rs diff --git a/bindings/src/contract.rs b/crates/bindings/src/contract.rs similarity index 100% rename from bindings/src/contract.rs rename to crates/bindings/src/contract.rs diff --git a/bindings/src/generated/generic_call_forwarder.rs b/crates/bindings/src/generated/generic_call_forwarder.rs similarity index 100% rename from bindings/src/generated/generic_call_forwarder.rs rename to crates/bindings/src/generated/generic_call_forwarder.rs diff --git a/bindings/src/generated/mod.rs b/crates/bindings/src/generated/mod.rs similarity index 100% rename from bindings/src/generated/mod.rs rename to crates/bindings/src/generated/mod.rs diff --git a/bindings/src/lib.rs b/crates/bindings/src/lib.rs similarity index 100% rename from bindings/src/lib.rs rename to crates/bindings/src/lib.rs diff --git a/bindings/tests/contract.rs b/crates/bindings/tests/contract.rs similarity index 100% rename from bindings/tests/contract.rs rename to crates/bindings/tests/contract.rs diff --git a/bindings/tests/deployments.rs b/crates/bindings/tests/deployments.rs similarity index 100% rename from bindings/tests/deployments.rs rename to crates/bindings/tests/deployments.rs diff --git a/crates/integration-test/Cargo.toml b/crates/integration-test/Cargo.toml new file mode 100644 index 0000000..ea0f95b --- /dev/null +++ b/crates/integration-test/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "anoma-generic-call-forwarder-integration-test" +description = "Integration and e2e tests for the Anoma generic call forwarder." +version = "0.2.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +keywords.workspace = true +publish = false + +[features] +default = [] +e2e = ["anoma-pa-evm-integration-test/e2e"] + +[dependencies] +anoma-pa-testkit = { workspace = true, features = ["fixtures"] } +# Normal dep (not dev) only because the `e2e` feature references it; the lib +# itself uses it solely via tests. +anoma-pa-evm-integration-test = { workspace = true } +anoma-generic-call-forwarder-bindings = { workspace = true } +anoma-generic-call-library = { workspace = true } +anoma-generic-call-witness = { workspace = true } +anoma-rm-risc0 = { workspace = true } + +alloy = { workspace = true, features = [ + "contract", + "json", + "providers", + "signer-local", + "sol-types", +] } +alloy-chains = { workspace = true } +anyhow = { workspace = true } +bincode = { workspace = true } +regex = { workspace = true } +risc0-zkvm = { workspace = true } + +[dev-dependencies] +anoma-pa-testkit = { workspace = true, features = ["mocks"] } +# generic-call only needs the AnomaPay ERC20 app *deployed* in its test setup. +anomapay-erc20-forwarder-integration-test = { workspace = true } +rstest = { workspace = true } +tokio = { workspace = true } diff --git a/crates/integration-test/artifacts/DexRouterMock.json b/crates/integration-test/artifacts/DexRouterMock.json new file mode 100644 index 0000000..ae85ce8 --- /dev/null +++ b/crates/integration-test/artifacts/DexRouterMock.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"permit2","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"swapExactTokensForTokens","inputs":[{"name":"amountIn","type":"uint256","internalType":"uint256"},{"name":"amountOutMin","type":"uint256","internalType":"uint256"},{"name":"path","type":"address[]","internalType":"address[]"},{"name":"to","type":"address","internalType":"address"},{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"amountOut","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"error","name":"SafeCastOverflowedUintDowncast","inputs":[{"name":"bits","type":"uint8","internalType":"uint8"},{"name":"value","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"name":"token","type":"address","internalType":"address"}]}],"bytecode":{"object":"0x60a034606757601f61044f38819003918201601f19168301916001600160401b03831184841017606b57808492602094604052833981010312606757516001600160a01b038116908190036067576080526040516103cf908161008082396080518160d30152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c6338ed173914610025575f80fd5b3461033f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033f5760043560243560443567ffffffffffffffff811161033f573660238201121561033f57806004013567ffffffffffffffff811161033f573660248260051b8401011161033f576064359373ffffffffffffffffffffffffffffffffffffffff851680950361033f5773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821161037057821561034357610120602485016103a1565b91813b1561033f57608473ffffffffffffffffffffffffffffffffffffffff915f80948460405197889687957f36c785160000000000000000000000000000000000000000000000000000000087523360048801523060248801521660448601521660648401525af18015610334576102ea575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908082116102bd5781101561029057602473ffffffffffffffffffffffffffffffffffffffff926101ed9260051b01016103a1565b1692604051927fa9059cbb0000000000000000000000000000000000000000000000000000000082526004528160245260208160448180885af16001825114811615610271575b836040521561024557602083838152f35b80847f5274afe70000000000000000000000000000000000000000000000000000000060249352600452fd5b600181151661028757843b15153d151616610234565b833d83823e3d90fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b90945067ffffffffffffffff8111610307576040525f935f610194565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b507f6dfcc650000000000000000000000000000000000000000000000000000000005f5260a060045260245260445ffd5b3573ffffffffffffffffffffffffffffffffffffffff8116810361033f579056fea164736f6c6343000823000a","sourceMap":"445:628:26:-:0;;;;;;;;;;;;;-1:-1:-1;;445:628:26;;;;-1:-1:-1;;;;;445:628:26;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;445:628:26;;;;;;;;599:38;;445:628;;;;;;;;599:38;445:628;;;;;;;-1:-1:-1;445:628:26;;;;;;-1:-1:-1;445:628:26;;;;;-1:-1:-1;445:628:26","linkReferences":{}},"deployedBytecode":{"object":"0x60806040526004361015610011575f80fd5b5f5f3560e01c6338ed173914610025575f80fd5b3461033f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033f5760043560243560443567ffffffffffffffff811161033f573660238201121561033f57806004013567ffffffffffffffff811161033f573660248260051b8401011161033f576064359373ffffffffffffffffffffffffffffffffffffffff851680950361033f5773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821161037057821561034357610120602485016103a1565b91813b1561033f57608473ffffffffffffffffffffffffffffffffffffffff915f80948460405197889687957f36c785160000000000000000000000000000000000000000000000000000000087523360048801523060248801521660448601521660648401525af18015610334576102ea575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908082116102bd5781101561029057602473ffffffffffffffffffffffffffffffffffffffff926101ed9260051b01016103a1565b1692604051927fa9059cbb0000000000000000000000000000000000000000000000000000000082526004528160245260208160448180885af16001825114811615610271575b836040521561024557602083838152f35b80847f5274afe70000000000000000000000000000000000000000000000000000000060249352600452fd5b600181151661028757843b15153d151616610234565b833d83823e3d90fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b90945067ffffffffffffffff8111610307576040525f935f610194565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b507f6dfcc650000000000000000000000000000000000000000000000000000000005f5260a060045260245260445ffd5b3573ffffffffffffffffffffffffffffffffffffffff8116810361033f579056fea164736f6c6343000823000a","sourceMap":"445:628:26:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;876:8;445:628;;7305:25:13;;7301:105;;445:628:26;;;;955:7;445:628;;;955:7;:::i;:::-;876:87;;;;;;445:628;;;;;;;;;876:87;;;;;445:628;876:87;;898:10;445:628;876:87;;445:628;918:4;445:628;;;;;;;;;;;;;;876:87;;;;;;;;445:628;;;;;;;;;;;;;;;;;;;1014:21;445:628;;;;;1014:21;:::i;:::-;445:628;1306:37:5;445:628:26;8544:1067:5;;8509:24;8544:1067;;445:628:26;8544:1067:5;;445:628:26;8544:1067:5;445:628:26;8544:1067:5;445:628:26;8544:1067:5;;;;;445:628:26;8544:1067:5;;;;;;;;445:628:26;8544:1067:5;445:628:26;8544:1067:5;1305:38;1301:116;;445:628:26;;;;;;1301:116:5;1366:40;;;445:628:26;1366:40:5;;445:628:26;;1366:40:5;8544:1067;445:628:26;8544:1067:5;;;;;;;;;;;;;;;;;;;;;;;;445:628:26;;;;;;;;;;;;;;;;;;;;876:87;445:628;;;;;;;;;;;876:87;;;;445:628;;;;;;;;;;876:87;445:628;;;;;;;;;876:87;445:628;;;;;;;;;;;;;7301:105:13;7353:42;;445:628:26;7353:42:13;445:628:26;;;;;;;7353:42:13;445:628:26;;;;;;;;;;:::o","linkReferences":{},"immutableReferences":{"4318":[{"start":211,"length":32}]}},"methodIdentifiers":{"swapExactTokensForTokens(uint256,uint256,address[],address,uint256)":"38ed1739"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.35+commit.47b9dedd\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"permit2\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"bits\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"SafeCastOverflowedUintDowncast\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"path\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"swapExactTokensForTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"SafeCastOverflowedUintDowncast(uint8,uint256)\":[{\"details\":\"Value doesn't fit in a uint of `bits` size.\"}],\"SafeERC20FailedOperation(address)\":[{\"details\":\"An operation with an ERC-20 token failed.\"}]},\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"test/mocks/DexRouter.m.sol\":\"DexRouterMock\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":@openzeppelin-contracts-5.6.1/=dependencies/@openzeppelin-contracts-5.6.1/\",\":anoma-forwarder-bases-1.0.0-rc.2/=dependencies/anoma-forwarder-bases-1.0.0-rc.2/\",\":anoma-pa-evm-1.2.0-rc.0/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/\",\":anomapay-erc20-forwarder-1.1.0-rc.1/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/\",\":elliptic-curve-solidity-0.2.5/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/dependencies/elliptic-curve-solidity-0.2.5/\",\":forge-std-1.16.1/=dependencies/forge-std-1.16.1/\",\":openzeppelin/contracts/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/dependencies/risc0-risc0-ethereum-3.0.1/lib/openzeppelin-contracts/contracts/\",\":solady-0.1.26/=dependencies/solady-0.1.26/\",\":solmate/=dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/lib/solmate/\",\":uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/=dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/\"],\"viaIR\":true},\"sources\":{\"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC1363.sol\":{\"keccak256\":\"0xd5ea07362ab630a6a3dee4285a74cf2377044ca2e4be472755ad64d7c5d4b69d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://da5e832b40fc5c3145d3781e2e5fa60ac2052c9d08af7e300dc8ab80c4343100\",\"dweb:/ipfs/QmTzf7N5ZUdh5raqtzbM11yexiUoLC9z3Ws632MCuycq1d\"]},\"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC165.sol\":{\"keccak256\":\"0x0afcb7e740d1537b252cb2676f600465ce6938398569f09ba1b9ca240dde2dfc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c299900ac4ec268d4570ecef0d697a3013cd11a6eb74e295ee3fbc945056037\",\"dweb:/ipfs/Qmab9owJoxcA7vJT5XNayCMaUR1qxqj1NDzzisduwaJMcZ\"]},\"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC20.sol\":{\"keccak256\":\"0x1a6221315ce0307746c2c4827c125d821ee796c74a676787762f4778671d4f44\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1bb2332a7ee26dd0b0de9b7fe266749f54820c99ab6a3bcb6f7e6b751d47ee2d\",\"dweb:/ipfs/QmcRWpaBeCYkhy68PR3B4AgD7asuQk7PwkWxrvJbZcikLF\"]},\"dependencies/@openzeppelin-contracts-5.6.1/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303\",\"dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV\"]},\"dependencies/@openzeppelin-contracts-5.6.1/token/ERC20/utils/SafeERC20.sol\":{\"keccak256\":\"0x304d732678032a9781ae85c8f204c8fba3d3a5e31c02616964e75cfdc5049098\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://299ced486011781dc98f638059678323c03079fefae1482abaa2135b22fa92d0\",\"dweb:/ipfs/QmbZNbcPTBxNvwChavN2kkZZs7xHhYL7mv51KrxMhsMs3j\"]},\"dependencies/@openzeppelin-contracts-5.6.1/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]},\"dependencies/@openzeppelin-contracts-5.6.1/utils/math/SafeCast.sol\":{\"keccak256\":\"0xc8cae21c9ae4a46e5162ff9bf5b351d6fa6a6eba72d515f3bc1bdfeda7fdf083\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ce830ebcf28e31643caba318996db3763c36d52cd0f23798ba83c135355d45e9\",\"dweb:/ipfs/QmdGPcvptHN7UBCbUYBbRX3hiRVRFLRwno8b4uga6uFNif\"]},\"dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/src/interfaces/IAllowanceTransfer.sol\":{\"keccak256\":\"0x37f0ac203b6ef605c9533e1a739477e8e9dcea90710b40e645a367f8a21ace29\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://e0104d72aeaec1cd66cc232e7de7b7ead08608efcc179491b8a66387614670b0\",\"dweb:/ipfs/QmfAZDyuNC9FXXbnJUwqHNwmAK6uRrXxtWEytLsxjskPsN\"]},\"dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/src/interfaces/IEIP712.sol\":{\"keccak256\":\"0xfdccf2b9639070803cd0e4198427fb0df3cc452ca59bd3b8a0d957a9a4254138\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f7c936ac42ce89e827db905a1544397f8bdf46db34cdb6aa1b90dea42fdb4c72\",\"dweb:/ipfs/QmVgurxo1N31qZqkPBirw9Z7S9tLYmv6jSwQp8R8ur2cBk\"]},\"test/mocks/DexRouter.m.sol\":{\"keccak256\":\"0x92676b354203eb284569cfd899bf25ae0f51e35de73951f6d17f9399232655f5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://875b795e537972d97a809725f33646184a2a9f4405e5d5c3ec8389ac17c36731\",\"dweb:/ipfs/QmUwibsRfsnvjtdWUFmBYZk3NZDDgEeXnM9qDoyK3KBNcK\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.35+commit.47b9dedd"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"permit2","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"type":"error","name":"SafeCastOverflowedUintDowncast"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"type":"error","name":"SafeERC20FailedOperation"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin-contracts-5.6.1/=dependencies/@openzeppelin-contracts-5.6.1/","anoma-forwarder-bases-1.0.0-rc.2/=dependencies/anoma-forwarder-bases-1.0.0-rc.2/","anoma-pa-evm-1.2.0-rc.0/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/","anomapay-erc20-forwarder-1.1.0-rc.1/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/","elliptic-curve-solidity-0.2.5/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/dependencies/elliptic-curve-solidity-0.2.5/","forge-std-1.16.1/=dependencies/forge-std-1.16.1/","openzeppelin/contracts/=dependencies/anomapay-erc20-forwarder-1.1.0-rc.1/dependencies/anoma-pa-evm-1.2.0-rc.0/dependencies/risc0-risc0-ethereum-3.0.1/lib/openzeppelin-contracts/contracts/","solady-0.1.26/=dependencies/solady-0.1.26/","solmate/=dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/lib/solmate/","uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/=dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/"],"optimizer":{"enabled":true,"runs":10000},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"test/mocks/DexRouter.m.sol":"DexRouterMock"},"evmVersion":"osaka","libraries":{},"viaIR":true},"sources":{"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC1363.sol":{"keccak256":"0xd5ea07362ab630a6a3dee4285a74cf2377044ca2e4be472755ad64d7c5d4b69d","urls":["bzz-raw://da5e832b40fc5c3145d3781e2e5fa60ac2052c9d08af7e300dc8ab80c4343100","dweb:/ipfs/QmTzf7N5ZUdh5raqtzbM11yexiUoLC9z3Ws632MCuycq1d"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC165.sol":{"keccak256":"0x0afcb7e740d1537b252cb2676f600465ce6938398569f09ba1b9ca240dde2dfc","urls":["bzz-raw://1c299900ac4ec268d4570ecef0d697a3013cd11a6eb74e295ee3fbc945056037","dweb:/ipfs/Qmab9owJoxcA7vJT5XNayCMaUR1qxqj1NDzzisduwaJMcZ"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/interfaces/IERC20.sol":{"keccak256":"0x1a6221315ce0307746c2c4827c125d821ee796c74a676787762f4778671d4f44","urls":["bzz-raw://1bb2332a7ee26dd0b0de9b7fe266749f54820c99ab6a3bcb6f7e6b751d47ee2d","dweb:/ipfs/QmcRWpaBeCYkhy68PR3B4AgD7asuQk7PwkWxrvJbZcikLF"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/token/ERC20/IERC20.sol":{"keccak256":"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2","urls":["bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303","dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/token/ERC20/utils/SafeERC20.sol":{"keccak256":"0x304d732678032a9781ae85c8f204c8fba3d3a5e31c02616964e75cfdc5049098","urls":["bzz-raw://299ced486011781dc98f638059678323c03079fefae1482abaa2135b22fa92d0","dweb:/ipfs/QmbZNbcPTBxNvwChavN2kkZZs7xHhYL7mv51KrxMhsMs3j"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"},"dependencies/@openzeppelin-contracts-5.6.1/utils/math/SafeCast.sol":{"keccak256":"0xc8cae21c9ae4a46e5162ff9bf5b351d6fa6a6eba72d515f3bc1bdfeda7fdf083","urls":["bzz-raw://ce830ebcf28e31643caba318996db3763c36d52cd0f23798ba83c135355d45e9","dweb:/ipfs/QmdGPcvptHN7UBCbUYBbRX3hiRVRFLRwno8b4uga6uFNif"],"license":"MIT"},"dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/src/interfaces/IAllowanceTransfer.sol":{"keccak256":"0x37f0ac203b6ef605c9533e1a739477e8e9dcea90710b40e645a367f8a21ace29","urls":["bzz-raw://e0104d72aeaec1cd66cc232e7de7b7ead08608efcc179491b8a66387614670b0","dweb:/ipfs/QmfAZDyuNC9FXXbnJUwqHNwmAK6uRrXxtWEytLsxjskPsN"],"license":"MIT"},"dependencies/uniswap-permit2-0x000000000022D473030F116dDEE9F6B43aC78BA3/src/interfaces/IEIP712.sol":{"keccak256":"0xfdccf2b9639070803cd0e4198427fb0df3cc452ca59bd3b8a0d957a9a4254138","urls":["bzz-raw://f7c936ac42ce89e827db905a1544397f8bdf46db34cdb6aa1b90dea42fdb4c72","dweb:/ipfs/QmVgurxo1N31qZqkPBirw9Z7S9tLYmv6jSwQp8R8ur2cBk"],"license":"MIT"},"test/mocks/DexRouter.m.sol":{"keccak256":"0x92676b354203eb284569cfd899bf25ae0f51e35de73951f6d17f9399232655f5","urls":["bzz-raw://875b795e537972d97a809725f33646184a2a9f4405e5d5c3ec8389ac17c36731","dweb:/ipfs/QmUwibsRfsnvjtdWUFmBYZk3NZDDgEeXnM9qDoyK3KBNcK"],"license":"MIT"}},"version":1},"id":26} \ No newline at end of file diff --git a/crates/integration-test/src/deploy/dex_router.rs b/crates/integration-test/src/deploy/dex_router.rs new file mode 100644 index 0000000..af96d2e --- /dev/null +++ b/crates/integration-test/src/deploy/dex_router.rs @@ -0,0 +1,26 @@ +use alloy::primitives::Address; +use alloy::providers::Provider; +use alloy::sol; +use anyhow::Context; + +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] + DexRouterMock, + "artifacts/DexRouterMock.json" +); + +/// Deploys the mock Uniswap-style DEX router used to demo swapping one ERC20 for +/// another from inside the generic-call forwarder. It pulls the input token from +/// the caller via Permit2 and pays out a fixed `amountOutMin` of the output token. +pub async fn deploy_dex_router_mock
(provider: P, permit2: Address) -> anyhow::Result
+where + P: Provider + Clone, +{ + let deployed = DexRouterMock::deploy(provider, permit2) + .await + .context("failed to deploy DexRouterMock")?; + + Ok(*deployed.address()) +} diff --git a/crates/integration-test/src/deploy/forwarder.rs b/crates/integration-test/src/deploy/forwarder.rs new file mode 100644 index 0000000..7326624 --- /dev/null +++ b/crates/integration-test/src/deploy/forwarder.rs @@ -0,0 +1,49 @@ +use alloy::primitives::Address; +use alloy::primitives::B256; +use alloy::providers::Provider; +use anoma_generic_call_forwarder_bindings::generated::generic_call_forwarder::GenericCallForwarder; +use anyhow::Context; + +use crate::state::addresses::insert_generic_call_forwarder_v1_address; + +#[inline] +pub fn generic_call_forwarder( + address: Address, + provider: P, +) -> GenericCallForwarder::GenericCallForwarderInstance
+where + P: Provider, +{ + GenericCallForwarder::GenericCallForwarderInstance::new(address, provider) +} + +pub async fn deploy_generic_call_forwarder
( + provider: P, + protocol_adapter: Address, + logic_ref: B256, +) -> anyhow::Result
+where + P: Provider + Clone, +{ + let deployed = GenericCallForwarder::deploy(provider, protocol_adapter, logic_ref) + .await + .context("failed to deploy GenericCallForwarder")?; + + Ok(*deployed.address()) +} + +pub async fn deploy_and_insert_generic_call_forwarder( + builder: &mut anoma_pa_testkit::environment::StateBuilder, + provider: P, + protocol_adapter: Address, + logic_ref: B256, +) -> anyhow::Result
+where + P: Provider + Clone, +{ + let address = deploy_generic_call_forwarder(provider, protocol_adapter, logic_ref).await?; + + insert_generic_call_forwarder_v1_address(builder, address); + + Ok(address) +} diff --git a/crates/integration-test/src/deploy/mod.rs b/crates/integration-test/src/deploy/mod.rs new file mode 100644 index 0000000..13895d6 --- /dev/null +++ b/crates/integration-test/src/deploy/mod.rs @@ -0,0 +1,4 @@ +//! Generic call forwarder deploy bindings. + +pub mod dex_router; +pub mod forwarder; diff --git a/crates/integration-test/src/fixtures/generic_call/action.rs b/crates/integration-test/src/fixtures/generic_call/action.rs new file mode 100644 index 0000000..d0eff39 --- /dev/null +++ b/crates/integration-test/src/fixtures/generic_call/action.rs @@ -0,0 +1,90 @@ +use anoma_generic_call_library::GenericCall; +use anoma_generic_call_library::GenericCallLogic; +use anoma_pa_testkit::witness::ActionWitnesses; +use anoma_rm_risc0::action_tree::MerkleTree as ArmTree; +use anoma_rm_risc0::compliance::ComplianceWitness; +use anoma_rm_risc0::resource::Resource; +use anyhow::Context; + +use super::resource; +use super::resource::Overrides; +use crate::logic; + +/// The derived data of a built generic-call action: the action witnesses +/// plus the ephemeral resources it consumes and creates. +pub struct ActionData { + pub witnesses: ActionWitnesses, + pub consumed_ephemeral: Resource, + pub created_ephemeral: Resource, +} + +/// Build a generic-call action: consumes an ephemeral resource whose label +/// commits to the forwarder `calls` and creates an ephemeral resource carrying +/// no calls. +pub fn build( + seed: u8, + forwarder_addr: Vec