diff --git a/.github/workflows/nightly-e2e-lst.yml b/.github/workflows/nightly-e2e-lst.yml new file mode 100644 index 0000000000..c014a91fb3 --- /dev/null +++ b/.github/workflows/nightly-e2e-lst.yml @@ -0,0 +1,20 @@ +on: + schedule: + - cron: "20 4 * * *" + workflow_dispatch: + +jobs: + deploy-preview: + runs-on: ['ubuntu-latest'] + steps: + - uses: actions/checkout@v4 + with: + lfs: true + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g= union.cachix.org-1:TV9o8jexzNVbM1VNBOq9fu8NK+hL6ZhOyOh0quATy+M= + trusted-substituters = https://cache.nixos.org https://cache.garnix.io https://union.cachix.org + - name: Fetch from Cache + run: | + nix build .#checks.x86_64-linux.e2e-lst -L --option sandbox false diff --git a/Cargo.lock b/Cargo.lock index 3353ffe9e1..ca28e4c57e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,7 +573,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru 0.13.0", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "reqwest 0.12.15", "serde", @@ -596,7 +596,7 @@ dependencies = [ "alloy-transport", "bimap", "futures", - "parking_lot 0.12.3", + "parking_lot", "serde", "serde_json", "tokio", @@ -825,7 +825,7 @@ dependencies = [ "derive_more 2.0.1", "futures", "futures-utils-wasm", - "parking_lot 0.12.3", + "parking_lot", "serde", "serde_json", "thiserror 2.0.12", @@ -1839,6 +1839,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -3071,6 +3080,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "cmac" version = "0.7.2" @@ -3861,7 +3879,7 @@ dependencies = [ "crossterm_winapi", "libc", "mio 0.8.11", - "parking_lot 0.12.3", + "parking_lot", "signal-hook", "signal-hook-mio", "winapi", @@ -4415,7 +4433,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -4429,7 +4447,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -4455,7 +4473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] @@ -5934,7 +5952,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -5943,6 +5961,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "1.1.0" @@ -6005,7 +6029,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.3", + "parking_lot", ] [[package]] @@ -6294,7 +6318,7 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot 0.12.3", + "parking_lot", "portable-atomic", "quanta", "rand 0.8.5", @@ -6322,7 +6346,7 @@ dependencies = [ "ff 0.13.1", "rand 0.8.5", "rand_core 0.6.4", - "rand_xorshift", + "rand_xorshift 0.3.0", "subtle 2.6.1", ] @@ -7194,7 +7218,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg", + "autocfg 1.4.0", "hashbrown 0.12.3", "serde", ] @@ -7251,15 +7275,6 @@ dependencies = [ "similar", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interprocess" version = "2.2.3" @@ -7591,7 +7606,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "jsonrpsee-types 0.24.9", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "rand 0.8.5", "rustc-hash 2.1.1", @@ -7617,7 +7632,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "jsonrpsee-types 0.25.1", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "rand 0.9.1", "rustc-hash 2.1.1", @@ -8086,7 +8101,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg", + "autocfg 1.4.0", "scopeguard", ] @@ -8380,7 +8395,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -8477,7 +8492,7 @@ dependencies = [ "event-listener 5.4.0", "futures-util", "loom", - "parking_lot 0.12.3", + "parking_lot", "portable-atomic", "rustc_version 0.4.1", "smallvec", @@ -9007,7 +9022,7 @@ dependencies = [ "futures", "mysten-metrics", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "rand 0.8.5", "reqwest 0.12.15", "snap", @@ -9027,7 +9042,7 @@ dependencies = [ "dashmap 5.5.3", "futures", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "prometheus", "prometheus-closure-metric", "scopeguard", @@ -9263,7 +9278,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ - "autocfg", + "autocfg 1.4.0", "num-integer", "num-traits", ] @@ -9327,7 +9342,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "autocfg", + "autocfg 1.4.0", "num-integer", "num-traits", ] @@ -9349,7 +9364,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg", + "autocfg 1.4.0", "libm", ] @@ -9452,7 +9467,7 @@ dependencies = [ "hyper 1.6.0", "itertools 0.13.0", "md-5", - "parking_lot 0.12.3", + "parking_lot", "percent-encoding", "quick-xml", "rand 0.8.5", @@ -9858,17 +9873,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -9876,21 +9880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -9901,7 +9891,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.11", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -10560,7 +10550,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.3", + "parking_lot", "protobuf", "thiserror 1.0.69", ] @@ -10588,7 +10578,7 @@ dependencies = [ "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", - "rand_xorshift", + "rand_xorshift 0.3.0", "regex-syntax 0.8.5", "rusty-fork", "tempfile", @@ -10822,6 +10812,25 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift 0.1.1", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -10832,7 +10841,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", ] [[package]] @@ -10858,6 +10867,16 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -10888,6 +10907,21 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -10916,6 +10950,15 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -10925,6 +10968,59 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_xorshift" version = "0.3.0" @@ -11006,6 +11102,15 @@ dependencies = [ "yasna", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "readonly" version = "0.2.13" @@ -11035,15 +11140,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.11" @@ -12264,28 +12360,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serial_test" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" -dependencies = [ - "lazy_static", - "parking_lot 0.11.2", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sha1" version = "0.10.6" @@ -12484,7 +12558,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -12526,7 +12600,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.101", @@ -13141,7 +13215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "parking_lot 0.12.3", + "parking_lot", "phf_shared", "precomputed-hash", ] @@ -13740,7 +13814,7 @@ dependencies = [ "num_enum 0.6.1", "once_cell", "p384", - "parking_lot 0.12.3", + "parking_lot", "passkey-types", "prometheus", "proptest", @@ -14280,7 +14354,7 @@ dependencies = [ "bytes", "libc", "mio 1.0.3", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -15241,6 +15315,7 @@ name = "union-test" version = "0.0.0" dependencies = [ "alloy", + "alloy-sol-types", "axum 0.8.3", "bip32 0.5.3", "cometbft-rpc", @@ -15249,21 +15324,24 @@ dependencies = [ "cosmos-sdk-event", "cosmwasm-std", "cw20", + "embed-commit", "hex-literal 0.4.1", "ibc-solidity", "ibc-union-msg", "ibc-union-spec", "jsonrpsee 0.25.1", + "lst", "once_cell", "protos", + "rand 0.6.5", "rand 0.8.5", "regex", "serde", "serde-utils", - "serial_test", "thiserror 2.0.12", "tokio", "tracing", + "tracing-subscriber", "trusted-mpt-light-client-types", "ucs03-zkgm", "unionlabs", @@ -17726,7 +17804,7 @@ checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.3", + "parking_lot", "pin-utils", "slab", "wasm-bindgen", @@ -17804,7 +17882,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.11", + "redox_syscall", "wasite", ] diff --git a/cosmwasm/cosmwasm.nix b/cosmwasm/cosmwasm.nix index 71b78e33e4..2a012869bd 100644 --- a/cosmwasm/cosmwasm.nix +++ b/cosmwasm/cosmwasm.nix @@ -108,11 +108,8 @@ _: { }; # lightclients = pkgs.lib.lists.remove "cometbls" (builtins.attrNames all-lightclients); lightclients = [ + "trusted-mpt" # "sui" - # "trusted-mpt" - # "parlia" - "sui" - # "trusted-mpt" ]; } { diff --git a/cosmwasm/lst/src/contract.rs b/cosmwasm/lst/src/contract.rs index 66498f1efe..468163f6a0 100644 --- a/cosmwasm/lst/src/contract.rs +++ b/cosmwasm/lst/src/contract.rs @@ -58,6 +58,8 @@ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND // TITLE. +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; use cosmwasm_std::{ ensure, to_json_binary, Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdResult, }; diff --git a/e2e/all-tests.nix b/e2e/all-tests.nix index 3e93f99ccc..f4f1b2587f 100644 --- a/e2e/all-tests.nix +++ b/e2e/all-tests.nix @@ -23,7 +23,7 @@ ... }: let - # full-e2e = import ./full-e2e.nix { inherit e2e pkgs; }; + union-test = import ./union-test.nix { inherit e2e pkgs self'; }; epoch-staking = import ./epoch-staking.nix { inherit e2e pkgs dbg; }; upgrades = import ./upgrades.nix { inherit e2e pkgs; @@ -36,8 +36,8 @@ # TODO: Fix Ensure Blocks Workflow unionlabs/union#2067 # ensure-blocks = import ./ensure-blocks/ensure-blocks.nix { inherit e2e networks pkgs nixpkgs crane; }; # - # Tests from ./full-e2e.nix - # inherit (full-e2e) all-works; + # Tests from ./union-test.nix + inherit (union-test) e2e-lst; # Tests from ./epoch-staking.nix inherit (epoch-staking) epoch-completes; diff --git a/e2e/e2e.nix b/e2e/e2e.nix index 2a9133b657..89fd8c89ac 100644 --- a/e2e/e2e.nix +++ b/e2e/e2e.nix @@ -78,9 +78,9 @@ }; vlans = [ 1 ]; }; - networking.hostName = "devnetVoyager"; - environment.systemPackages = with pkgs; [ jq ]; + services.resolved.enable = true; + nix.settings.sandbox = false; }; }; @@ -121,29 +121,6 @@ networking.hostName = "devnetUnion"; }; }; - - galoisNode = { - wait_for_console_text = "Serving..."; - wait_for_open_port = 9999; - node = _: { - imports = [ - inputs.arion.nixosModules.arion - ]; - virtualisation = { - diskSize = 16 * 1024; - memorySize = 32 * 1024; - # TODO(aeryz): remove this - cores = 32; - arion = { - backend = "docker"; - projects.galois.settings = galois-arion-project; - }; - vlans = [ 1 ]; - }; - networking.hostName = "galois"; - }; - }; - in { _module.args.e2e = { @@ -152,6 +129,7 @@ unionNode unionTestnetGenesisNode devnetEthNode + mkVoyagerNode ; # TODO: This is poorly named, it only starts devnet-union and devnet-eth @@ -190,50 +168,74 @@ ); }; - # TODO: This is poorly named, it only starts devnet-union and devnet-eth mkE2eTestEthUnion = + voyagerConfigFile: { name, testScript, nodes ? { }, + openConnection ? false, }: let - voyagerNode = mkVoyagerNode ../tools/union-test/config.jsonc; + voyagerNode = mkVoyagerNode voyagerConfigFile; voyagerBin = "${self'.packages.voyager}/bin/voyager"; in mkTest { inherit name; testScript = '' - galois.start() + openConnection = ${if openConnection then "True" else "False"} + devnetUnion.wait_for_open_port(${toString unionNode.wait_for_open_port}) devnetEth.wait_for_open_port(${toString devnetEthNode.wait_for_open_port}) + # deploy contract on union devnetUnion.succeed('${self'.packages.cosmwasm-scripts.union-devnet.deploy}/bin/union-devnet-deploy-full') # match non-zero blocks devnetUnion.wait_until_succeeds('[[ $(curl "http://localhost:26657/block" --fail --silent | ${pkgs.lib.meta.getExe pkgs.jq} ".result.block.header.height | tonumber > 1") == "true" ]]') - devnetEth.wait_until_succeeds('[[ $(curl http://localhost:9596/eth/v2/beacon/blocks/head --fail --silent | ${pkgs.lib.meta.getExe pkgs.jq} \'.data.message.slot | tonumber > 1\') == "true" ]]') + + # we are waiting until slot 20 so that we are sure that the contracts are deployed + devnetEth.wait_until_succeeds('[[ $(curl http://localhost:9596/eth/v2/beacon/blocks/head --fail --silent | ${pkgs.lib.meta.getExe pkgs.jq} \'.data.message.slot | tonumber > 20\') == "true" ]]') devnetVoyager.wait_for_open_port(${toString voyagerNode.wait_for_open_port}) devnetVoyager.wait_until_succeeds('${voyagerBin} rpc info') - galois.wait_for_console_text('${galoisNode.wait_for_console_text}') + # index the chains on voyager + devnetVoyager.wait_until_succeeds('${voyagerBin} -c ${voyagerNode.voyagerConfig} index union-devnet-1 -e') + devnetVoyager.wait_until_succeeds('${voyagerBin} -c ${voyagerNode.voyagerConfig} index 32382 -e') - devnetUnion.wait_until_succeeds('[[ $(curl "http://localhost:26660/block" --fail --silent | ${pkgs.lib.meta.getExe pkgs.jq} ".result.block.header.height | tonumber > 200000") == "true" ]]') + # create clients + devnetVoyager.wait_until_succeeds('${voyagerBin} -c ${voyagerNode.voyagerConfig} msg create-client --on union-devnet-1 --tracking 32382 --ibc-interface ibc-cosmwasm --ibc-spec-id ibc-union --client-type trusted/evm/mpt -e') + devnetVoyager.wait_until_succeeds('${voyagerBin} -c ${voyagerNode.voyagerConfig} msg create-client --on 32382 --tracking union-devnet-1 --ibc-interface ibc-solidity --ibc-spec-id ibc-union --client-type cometbls -e') + + # give some time for the clients to be created + devnetVoyager.wait_until_succeeds('sleep 10') + + if openConnection: + devnetVoyager.succeed( + "echo '{\"@type\":\"call\",\"@value\":{\"@type\":\"submit_tx\",\"@value\":{\"chain_id\":\"union-devnet-1\",\"datagrams\":[{\"ibc_spec_id\":\"ibc-union\",\"datagram\":{\"@type\":\"connection_open_init\",\"@value\":{\"client_id\":1,\"counterparty_client_id\":1}}}]}}}' > /tmp/payload.json" + ) + + devnetVoyager.wait_until_succeeds("${voyagerBin} -c ${voyagerNode.voyagerConfig} q e $(cat /tmp/payload.json)") + + # wait until the connection is opened + devnetVoyager.wait_until_succeeds("[[ $(${voyagerBin} rpc ibc-state 32382 '{ \"connection\": { \"connection_id\": 1 } }' | jq '.state.state == \"open\"') == true ]]") + else: + print("Skipping connection...") + + ${testScript} ''; nodes = (pkgs.lib.throwIf (builtins.hasAttr "devnetUnion" nodes) "union node already exists; use a different name") (pkgs.lib.throwIf (builtins.hasAttr "devnetEth" nodes) "devnetEth node already exists; use a different name") (pkgs.lib.throwIf (builtins.hasAttr "voyager" nodes) "voyager node already exists; use a different name") - (pkgs.lib.throwIf (builtins.hasAttr "galois" nodes) "galois node already exists; use a different name") ( { devnetUnion = unionNode.node; devnetEth = devnetEthNode.node; devnetVoyager = voyagerNode.node; - galois = galoisNode.node; } // nodes ); diff --git a/e2e/full-e2e.nix b/e2e/full-e2e.nix deleted file mode 100644 index 063780b632..0000000000 --- a/e2e/full-e2e.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - e2e, - pkgs, - ... -}: -{ - all-works = e2e.mkE2eTestEthUnion { - name = "all-works"; - - testScript = ''''; - - nodes = { - }; - }; -} diff --git a/e2e/union-test.nix b/e2e/union-test.nix new file mode 100644 index 0000000000..af4b11d27a --- /dev/null +++ b/e2e/union-test.nix @@ -0,0 +1,89 @@ +{ + e2e, + self', + ... +}: +let + voyagerConfigFile = ../tools/union-test/config.jsonc; + voyagerNode = e2e.mkVoyagerNode voyagerConfigFile; + voyagerBin = "${self'.packages.voyager}/bin/voyager"; +in +{ + e2e-lst = e2e.mkE2eTestEthUnion voyagerConfigFile { + name = "lst"; + + openConnection = true; + + testScript = '' + # open a channel since the lst tests require only 1 channel + devnetVoyager.succeed( + "echo '{\"@type\":\"call\",\"@value\":{\"@type\":\"submit_tx\",\"@value\":{\"chain_id\":\"32382\",\"datagrams\":[{\"ibc_spec_id\":\"ibc-union\",\"datagram\":{\"@type\":\"channel_open_init\",\"@value\":{\"counterparty_port_id\":\"0x756e696f6e3172667a33797467366c363077786b357278736b32376a766e32393037637961763034737a386b64653378686d6d66396e706c7871723879303563\",\"port_id\":\"0x05FD55C1AbE31D3ED09A76216cA8F0372f4B2eC5\",\"connection_id\":1,\"version\":\"ucs03-zkgm-0\"}}}]}}}' > /tmp/payload.json" + ) + + devnetVoyager.wait_until_succeeds("${voyagerBin} -c ${voyagerNode.voyagerConfig} q e $(cat /tmp/payload.json)") + + # wait until the channel is opened + devnetVoyager.wait_until_succeeds("[[ $(${voyagerBin} rpc ibc-state 32382 '{ \"channel\": { \"channel_id\": 1 } }' | jq '.state.state == \"open\"') == true ]]") + + # deploy lst staker + devnetUnion.wait_until_succeeds("${self'.packages.cosmwasm-deployer}/bin/cosmwasm-deployer deploy-contract --rpc-url http://devnetUnion:26657 --private-key 0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f --bytecode ${self'.packages.lst-staker} --init-msg '{ \"local\": { \"admin\": \"union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2\" } }' --salt apps/lst-staker --gas feemarket --max-gas 100000000 --gas-multiplier 1.4") + + # deploy lst + devnetUnion.wait_until_succeeds("${self'.packages.cosmwasm-deployer}/bin/cosmwasm-deployer deploy-contract --rpc-url http://devnetUnion:26657 --private-key 0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f --bytecode ${self'.packages.lst} --init-msg '{ \"native_token_denom\": \"au\", \"minimum_liquid_stake_amount\": \"10\", \"staker_address\": \"union160a75a608j6w80x5ykckvd9cavs2xk8yfjzy2eqhpq0nprxg05qqf067nj\", \"protocol_fee_config\": {\"fee_rate\": \"10000\", \"fee_recipient\": \"union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2\" }, \"lst_address\": \"union1nluwd0qfymmdwfczezvgrmvz43n4xwdyfvshxj82sj7smuk9m42stgfwcz\", \"batch_period_seconds\": \"20\", \"monitors\": [], \"admin\": \"union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2\", \"unbonding_period_seconds\": \"100\" }' --salt apps/lst --gas feemarket --max-gas 100000000 --gas-multiplier 1.4") + + # deploy eU + devnetUnion.wait_until_succeeds("${self'.packages.cosmwasm-deployer}/bin/cosmwasm-deployer deploy-contract --rpc-url http://devnetUnion:26657 --private-key 0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f --bytecode ${self'.packages.cw-unionversal-token} --init-msg '{ \"zkgm\": \"union1rfz3ytg6l60wxk5rxsk27jvn2907cyav04sz8kde3xhmmf9nplxqr8y05c\", \"admin\": \"union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2\", \"cw20_init\": { \"cw20\": { \"name\": \"eU\", \"symbol\": \"eU\", \"decimals\": 6, \"initial_balances\": [], \"mint\": {\"minter\": \"union1qg3gm3f87w6al9u9ldkqhjdeaxrd0tae5w70les88egql8nzp95qs5rrz0\"} } }, \"extra_minters\": [] }' --salt tokens/eu --gas feemarket --max-gas 100000000 --gas-multiplier 1.4") + + # set the fungible counterparty for U on union + devnetUnion.wait_until_succeeds("\ + ${self'.packages.uniond}/bin/uniond tx \ + wasm execute \ + union1skg5244hpkad603zz77kdekzw6ffgpfrde3ldk8rpdz06n62k4hqct0w4j \ + '{\"set_fungible_counterparty\": {\"path\": \"0\", \"channel_id\": 1, \"base_token\": \"0x0c8C6f58156D10d18193A8fFdD853e1b9F8D8836\", \"counterparty_beneficiary\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", \"escrowed_denom\": \"au\"}}' \ + --from alice \ + --gas auto \ + --gas-adjustment 10.0 \ + --node http://devnetUnion:26657 \ + --chain-id union-devnet-1 -y \ + --home ${self'.packages.devnet-union-home} \ + --keyring-backend test \ + --gas-prices 1au\ + ") + + # set the validators on the staker + devnetUnion.wait_until_succeeds("\ + ${self'.packages.uniond}/bin/uniond tx \ + wasm execute \ + union160a75a608j6w80x5ykckvd9cavs2xk8yfjzy2eqhpq0nprxg05qqf067nj \ + '{\"set_validators\": { \"unionvaloper1qp4uzhet2sd9mrs46kemse5dt9ncz4k3xuz7ej\": \"100\" }}' \ + --from alice \ + --gas auto \ + --gas-adjustment 10.0 \ + --node http://devnetUnion:26657 \ + --chain-id union-devnet-1 -y \ + --home ${self'.packages.devnet-union-home} \ + --keyring-backend test \ + --gas-prices 1au\ + ") + + # set the lst hub address on staker + devnetUnion.wait_until_succeeds("\ + ${self'.packages.uniond}/bin/uniond tx \ + wasm execute \ + union160a75a608j6w80x5ykckvd9cavs2xk8yfjzy2eqhpq0nprxg05qqf067nj \ + '{\"set_lst_hub_address\": \"union1qg3gm3f87w6al9u9ldkqhjdeaxrd0tae5w70les88egql8nzp95qs5rrz0\"}' \ + --from alice \ + --gas auto \ + --gas-adjustment 10.0 \ + --node http://devnetUnion:26657 \ + --chain-id union-devnet-1 -y \ + --home ${self'.packages.devnet-union-home} \ + --keyring-backend test \ + --gas-prices 1au\ + ") + + # run the tests, note that we do `1>&2` because otherwise we won't get the full prints + devnetUnion.wait_until_succeeds("RUST_LOG=info ${self'.packages.e2e-lst-tests}/lst --nocapture 1>&2") + ''; + }; +} diff --git a/flake.nix b/flake.nix index 73cb7b4b22..57d34d7dbb 100644 --- a/flake.nix +++ b/flake.nix @@ -229,6 +229,7 @@ ./sentinel2/sentinel.nix ./lib/embed-commit ./networks/services/voyager.nix + ./tools/union-test/tests/e2e.nix treefmt-nix.flakeModule ]; diff --git a/garnix.yaml b/garnix.yaml index 148afe141b..0687abb484 100644 --- a/garnix.yaml +++ b/garnix.yaml @@ -15,6 +15,7 @@ builds: - 'checks.aarch64-linux.nil' - 'checks.aarch64-linux.ssz-tests-up-to-date' - 'checks.*.all-crates-buildable-individually' + - 'checks.*.e2e-lst' - 'checks.*.devnet-eth-runs' - 'checks.*.ensure-blocks' - 'checks.*.epoch-completes' @@ -72,6 +73,7 @@ builds: - 'nixosConfigurations.*' exclude: - 'checks.*.all-crates-buildable-individually' + - 'checks.*.e2e-lst' - 'checks.*.devnet-eth-runs' - 'checks.*.ensure-blocks' - 'checks.*.epoch-completes' diff --git a/networks/services/forge.nix b/networks/services/forge.nix index 1b945050e7..5be39077f0 100644 --- a/networks/services/forge.nix +++ b/networks/services/forge.nix @@ -15,6 +15,8 @@ let cp --no-preserve=mode -r ${evm-contracts}/* . cp --no-preserve=mode -r ${evm-sources}/* . WETH_ADDRESS="0x0000000000000000000000000000000000000000" \ + DEPLOYER="0x0000000000000000000000000000000000000000" \ + SENDER="0xBe68fC2d8249eb60bfCf0e71D5A0d2F2e292c4eD" \ RATE_LIMIT_ENABLED="false" \ PRIVATE_KEY=0x${builtins.readFile ./../genesis/devnet-eth/dev-key0.prv} \ FOUNDRY_PROFILE="script" \ diff --git a/networks/services/lodestar.nix b/networks/services/lodestar.nix index 59650c7731..922ccbc95d 100644 --- a/networks/services/lodestar.nix +++ b/networks/services/lodestar.nix @@ -95,11 +95,11 @@ in ]; healthcheck = { interval = "5s"; - retries = 6; + retries = 15; test = [ "CMD-SHELL" '' - curl -f http://localhost:9596/eth/v2/beacon/blocks/2 || exit 1 + curl -f http://localhost:9596/eth/v2/beacon/blocks/3 || exit 1 '' ]; }; diff --git a/networks/services/voyager.nix b/networks/services/voyager.nix index 6efcc673a8..f09b4bc156 100644 --- a/networks/services/voyager.nix +++ b/networks/services/voyager.nix @@ -16,7 +16,7 @@ _: { pkgs.jq self'.packages.voyager voyagerConfig - self'.packages.voyager-modules-plugins + self'.packages.voyager-modules-plugins-single-build ]; }; diff --git a/tools/union-test/Cargo.toml b/tools/union-test/Cargo.toml index d2ba84fb4a..889b7e993d 100644 --- a/tools/union-test/Cargo.toml +++ b/tools/union-test/Cargo.toml @@ -9,8 +9,12 @@ publish = { workspace = true } repository = { workspace = true } +[package.metadata.crane] +test-include = ["tools/union-test/config.jsonc", "tools/union-test/tests/config.json"] + [dependencies] alloy = { workspace = true, features = ["contract", "network", "providers", "signers", "signer-local", "rpc", "rpc-types", "transports", "transport-http", "transport-ws", "reqwest", "provider-ws"] } +alloy-sol-types = { workspace = true } axum = { workspace = true, features = ["macros", "tokio", "json"] } bip32 = { workspace = true } cometbft-rpc = { workspace = true } @@ -19,11 +23,13 @@ cosmos-client = { workspace = true } cosmos-sdk-event = { workspace = true } cosmwasm-std = { workspace = true } cw20 = { workspace = true } +embed-commit = { workspace = true } hex-literal = { workspace = true } ibc-solidity = { workspace = true, features = ["serde", "rpc"] } ibc-union-msg = { workspace = true } ibc-union-spec = { workspace = true } jsonrpsee = { workspace = true, features = ["tracing", "ws-client", "http-client"] } +lst = { workspace = true, features = ["library"] } protos = { workspace = true } rand = { workspace = true, features = ["default"] } regex = "1" @@ -37,11 +43,11 @@ ucs03-zkgm = { workspace = true, features = ["library"] } unionlabs = { workspace = true } voyager-sdk = { workspace = true } - [dev-dependencies] -once_cell = "1.17" -serial_test = "0.5" -tokio = { version = "1", features = ["macros", "rt"] } +once_cell = "1.17" +rand = "0.6" +tokio = { version = "1", features = ["macros", "rt"] } +tracing-subscriber = "0.3" [lints] diff --git a/tools/union-test/config.jsonc b/tools/union-test/config.jsonc index 47dcf915a7..f5708904cb 100644 --- a/tools/union-test/config.jsonc +++ b/tools/union-test/config.jsonc @@ -1,17 +1,6 @@ { "modules": { "state": [ - { - "enabled": true, - "path": "/bin/voyager-state-module-cosmos-sdk", - "info": { - "chain_id": "union-devnet-1", - "ibc_spec_id": "1.0.0" - }, - "config": { - "rpc_url": "http://devnetUnion:26657" - } - }, { "enabled": true, "path": "/bin/voyager-state-module-cosmos-sdk-union", @@ -87,19 +76,6 @@ "finality_lag": 1 } } - // { - // "enabled": true, - // "path": "/bin/voyager-consensus-module-ethereum", - // "info": { - // "chain_id": "32382", - // "consensus_type": "ethereum" - // }, - // "config": { - // "chain_spec": "minimal", - // "rpc_url": "http://devnetEth:8545", - // "beacon_rpc_url": "http://localhost:9596" - // } - // } ], "client": [ { @@ -121,36 +97,6 @@ "ibc_interface": "ibc-cosmwasm", "ibc_spec_id": "ibc-union" } - }, - { - "enabled": true, - "path": "/bin/voyager-client-module-cometbls", - "info": { - "client_type": "cometbls", - "consensus_type": "cometbls", - "ibc_interface": "ibc-move/aptos", - "ibc_spec_id": "ibc-union" - } - }, - { - "enabled": true, - "path": "/bin/voyager-client-module-cometbls", - "info": { - "client_type": "cometbls", - "consensus_type": "cometbls", - "ibc_interface": "ibc-go-v8/08-wasm", - "ibc_spec_id": "ibc-union" - } - }, - { - "enabled": true, - "path": "/bin/voyager-client-module-ethereum", - "info": { - "client_type": "ethereum", - "consensus_type": "ethereum", - "ibc_interface": "ibc-cosmwasm", - "ibc_spec_id": "ibc-union" - } } ], "client_bootstrap": [ diff --git a/tools/union-test/src/cosmos.rs b/tools/union-test/src/cosmos.rs index 35c8a93fc3..f8eb341e67 100644 --- a/tools/union-test/src/cosmos.rs +++ b/tools/union-test/src/cosmos.rs @@ -12,6 +12,8 @@ use cosmos_client::{ BroadcastTxCommitError, TxClient, }; use cosmos_sdk_event::CosmosSdkEvent; +use cosmwasm_std::Addr; +use cw20::Cw20QueryMsg; use ibc_union_spec::{ChannelId, ClientId, ConnectionId, Timestamp}; use protos::{ cosmos::{ @@ -20,16 +22,18 @@ use protos::{ }, cosmwasm::wasm::v1::{QuerySmartContractStateRequest, QuerySmartContractStateResponse}, }; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tracing::info; use ucs03_zkgm::msg::{PredictWrappedTokenResponse, QueryMsg}; use unionlabs::{ self, google::protobuf::any::mk_any, ibc::core::client::height::Height, primitives::{encoding::HexUnprefixed, Bech32, Bytes, H160, H256}, + prost, }; use voyager_sdk::{ - anyhow::{self, anyhow, bail, Context}, + anyhow::{self, anyhow, bail}, primitives::ChainId, serde_json, vm::BoxDynError, @@ -158,7 +162,7 @@ impl Module { }) } - pub async fn native_balance(&self, address: Bech32, token: &str) -> anyhow::Result { + pub async fn native_balance(&self, address: Addr, token: &str) -> anyhow::Result { let balance: u128 = self .rpc .client() @@ -225,11 +229,9 @@ impl Module { let event = match CosmosSdkEvent::::new(raw_ev) { Ok(event) => event, Err(cosmos_sdk_event::Error::Deserialize(_error)) => { - // println!("unable to parse event: {error}"); continue; } Err(_err) => { - // println!("error parsing event: {}", ErrorReporter(err)); continue; } }; @@ -339,12 +341,12 @@ impl Module { packet_hash_param: H256, max_wait: Duration, ) -> anyhow::Result { - println!("Waiting for packet recv event with hash: {packet_hash_param:?}"); + info!("Waiting for packet recv event with hash: {packet_hash_param:?}"); Ok(self .wait_for_event( move |evt| { if let ModuleEvent::WasmPacketRecv { packet_hash, .. } = evt { - println!("Packet recv event came with hash: {packet_hash:?}"); + info!("Packet recv event came with hash: {packet_hash:?}"); if packet_hash.as_ref() == packet_hash_param.as_ref() { return Some(helpers::PacketRecv { packet_hash: *packet_hash, @@ -395,12 +397,12 @@ impl Module { packet_hash_param: H256, max_wait: Duration, ) -> anyhow::Result { - println!("Waiting for packet timeout event with hash: {packet_hash_param:?}"); + info!("Waiting for packet timeout event with hash: {packet_hash_param:?}"); Ok(self .wait_for_event( move |evt| { if let ModuleEvent::WasmPacketTimeout { packet_hash, .. } = evt { - println!("Packet timeout event came with hash: {packet_hash:?}"); + info!("Packet timeout event came with hash: {packet_hash:?}"); if packet_hash.as_ref() == packet_hash_param.as_ref() { return Some(helpers::PacketTimeout { packet_hash: *packet_hash, @@ -424,7 +426,7 @@ impl Module { packet_hash_param: H256, max_wait: Duration, ) -> anyhow::Result { - println!("Waiting for packet ack event with hash: {packet_hash_param:?}"); + info!("Waiting for packet ack event with hash: {packet_hash_param:?}"); Ok(self .wait_for_event( move |evt| { @@ -438,12 +440,9 @@ impl Module { let ack_bytes: &[u8] = acknowledgement.as_ref(); // Grab the first 32 bytes — this is the uint256 in ABI encoding - let mut tag_be = [0u8; 32]; - tag_be.copy_from_slice(&ack_bytes[..32]); - let tag_u128 = u128::from_be_bytes(tag_be[16..].try_into().ok()?); return Some(helpers::PacketAck { packet_hash: *packet_hash, - tag: tag_u128, + tag: alloy::primitives::U256::from_be_slice(&ack_bytes[..32]), }); } None @@ -495,7 +494,6 @@ impl Module { Ok(self .wait_for_event( move |evt| { - println!("EVT is: {:?}", evt); if let ModuleEvent::WithdrawRewards { validator, amount } = evt { if validator == &validator_filter { Some(helpers::WithdrawRewards { @@ -519,39 +517,21 @@ impl Module { pub async fn predict_wrapped_token( &self, - contract: Bech32, + contract: Addr, channel_id: ChannelId, token: Vec, ) -> anyhow::Result { - let msg = QueryMsg::PredictWrappedToken { - path: "0".to_string(), - channel_id, - token: token.into(), - }; - let req = QuerySmartContractStateRequest { - address: contract.to_string(), - query_data: serde_json::to_vec(&msg) - .context("serializing PredictWrappedToken QueryMsg")?, - }; - - let raw = self - .rpc - .client() - .grpc_abci_query::<_, QuerySmartContractStateResponse>( - "/cosmwasm.wasm.v1.Query/SmartContractState", - &req, - None, - false, + Ok(self + .query_wasm_smart::<_, PredictWrappedTokenResponse>( + contract, + &QueryMsg::PredictWrappedToken { + path: "0".to_string(), + channel_id, + token: token.into(), + }, ) .await? - .into_result()? - .unwrap() - .data; - - let resp: PredictWrappedTokenResponse = - serde_json::from_slice(&raw).context("deserializing PredictWrappedTokenResponse")?; - - Ok(resp.wrapped_token) + .wrapped_token) } pub async fn get_signer(&self) -> (Bech32, &LocalSigner) { @@ -566,10 +546,19 @@ impl Module { } } - pub async fn get_minter(&self, contract: Bech32) -> anyhow::Result { + pub async fn get_minter(&self, contract: Addr) -> anyhow::Result { + self.query_wasm_smart::<_, String>(contract, QueryMsg::GetMinter {}) + .await + } + + pub async fn query_wasm_smart( + &self, + contract: Addr, + req: Req, + ) -> anyhow::Result { let req = QuerySmartContractStateRequest { address: contract.to_string(), - query_data: serde_json::to_vec(&QueryMsg::GetMinter {})?, + query_data: serde_json::to_vec(&req)?, }; let raw = self @@ -586,10 +575,20 @@ impl Module { .unwrap() .data; - // 3) Deserialize the JSON `{ "minter": "union1..." }` into our struct - let resp = serde_json::from_slice(&raw).context("deserializing GetMinterResponse")?; + Ok(serde_json::from_slice(&raw)?) + } + + pub async fn get_cw20_balance(&self, address: &Addr, token: &Addr) -> anyhow::Result { + let resp = self + .query_wasm_smart::<_, cw20::BalanceResponse>( + token.clone(), + Cw20QueryMsg::Balance { + address: address.to_string(), + }, + ) + .await?; - Ok(resp) + Ok(resp.balance.u128()) } pub async fn get_balance( @@ -612,16 +611,16 @@ impl Module { .ok_or_else(|| anyhow::anyhow!("no balance for denom {}", denom)) } - pub async fn send_transaction_with_retry( + pub async fn send_cosmwasm_transaction_with_retry( &self, - contract: Bech32, + contract: Addr, msg: (Vec, Vec), signer: &LocalSigner, ) -> Option> { let max_retries = 5; for attempt in 1..=max_retries { let outcome = self - .send_transaction(contract.clone(), msg.clone(), signer) + .send_cosmwasm_transaction(contract.clone(), msg.clone(), signer) .await; if let Some(Ok(_)) = &outcome { @@ -647,10 +646,24 @@ impl Module { None } + pub async fn send_transaction( + &self, + msg: T, + signer: &LocalSigner, + ) -> Option> { + let tx_client = TxClient::new(signer, &self.rpc, &self.gas_config); + + let outcome = tx_client + .broadcast_tx_commit([mk_any(&msg)], "memo", true) + .await; + + Some(outcome) + } + // TODO(aeryz): return the digest - pub async fn send_transaction( + pub async fn send_cosmwasm_transaction( &self, - contract: Bech32, + contract: Addr, msg: (Vec, Vec), signer: &LocalSigner, ) -> Option> { @@ -678,6 +691,60 @@ impl Module { Some(outcome) } + pub async fn stake( + &self, + validator_address: String, + amount: u128, + signer: &LocalSigner, + ) -> Option> { + let tx_client = TxClient::new(signer, &self.rpc, &self.gas_config); + + let signer_address = signer.address(); + let outcome = tx_client + .broadcast_tx_commit( + [mk_any(&protos::cosmos::staking::v1beta1::MsgDelegate { + delegator_address: signer_address.to_string(), + validator_address, + amount: Some(Coin { + denom: "au".to_string(), + amount: amount.to_string(), + }), + })], + "memo", + true, + ) + .await; + + Some(outcome) + } + + pub async fn unstake( + &self, + validator_address: String, + amount: u128, + signer: &LocalSigner, + ) -> Option> { + let tx_client = TxClient::new(signer, &self.rpc, &self.gas_config); + + let signer_address = signer.address(); + let outcome = tx_client + .broadcast_tx_commit( + [mk_any(&protos::cosmos::staking::v1beta1::MsgUndelegate { + delegator_address: signer_address.to_string(), + validator_address, + amount: Some(Coin { + denom: "au".to_string(), + amount: amount.to_string(), + }), + })], + "memo", + true, + ) + .await; + + Some(outcome) + } + /// Helper to detect the ABCI “account sequence mismatch” error. fn is_sequence_mismatch(&self, err: &BroadcastTxCommitError) -> bool { match err { @@ -690,11 +757,11 @@ impl Module { pub async fn send_ibc_transaction( &self, - contract: Bech32, + contract: Addr, msg: (Vec, Vec), signer: &LocalSigner, ) -> anyhow::Result<(H256, u64)> { - let result = self.send_transaction(contract, msg, signer).await; + let result = self.send_cosmwasm_transaction(contract, msg, signer).await; let tx_result = result.ok_or_else(|| anyhow!("failed to send transaction"))??; let height = tx_result .height diff --git a/tools/union-test/src/cosmos_helpers.rs b/tools/union-test/src/cosmos_helpers.rs new file mode 100644 index 0000000000..186846d37a --- /dev/null +++ b/tools/union-test/src/cosmos_helpers.rs @@ -0,0 +1,58 @@ +use std::str::FromStr; + +use alloy_sol_types::SolValue as _; +use cosmwasm_std::{instantiate2_address, Addr, CanonicalAddr}; +use hex_literal::hex; +use unionlabs::{ + ethereum::keccak256, + primitives::{Bech32, FixedBytes}, +}; +use voyager_sdk::anyhow; + +pub const COSMOS_BASE_CONTRACT_HASH: [u8; 32] = + hex!("ec827349ed4c1fec5a9c3462ff7c979d4c40e7aa43b16ed34469d04ff835f2a1"); + +pub const SALT_IBC_CORE: &[u8] = b"ibc-is-based"; +pub const SALT_ZKGM: &[u8] = b"protocols/ucs03"; +pub const SALT_ESCROW_VAULT: &[u8] = + &hex!("50bbead29d10abe51a7c32bbc02a9b00ff4a7db57c050b7a0ff61d6173c33965"); +pub const SALT_LST_HUB: &[u8] = b"apps/lst"; +pub const SALT_EU: &[u8] = b"tokens/eu"; +pub const SALT_LST_STAKER: &[u8] = b"apps/lst-staker"; + +pub fn calculate_cosmos_contract_address(creator: &str, salt: &[u8]) -> anyhow::Result { + let bech_addr: Bech32 = Bech32::from_str(creator).unwrap(); + + let addr = instantiate2_address( + &COSMOS_BASE_CONTRACT_HASH, + &bech_addr.data().as_ref().into(), + salt, + )?; + + let bech_addr = Bech32::new(bech_addr.hrp(), addr.as_slice()); + + Ok(Addr::unchecked(bech_addr.to_string())) +} + +pub fn calculate_proxy_address( + zkgm_address: &Addr, + path: alloy::primitives::U256, + channel_id: u32, + sender: &[u8], +) -> Addr { + let addr = Bech32::>::from_str(zkgm_address.as_str()).unwrap(); + let canonical_addr = instantiate2_address( + &COSMOS_BASE_CONTRACT_HASH, + &CanonicalAddr::from(addr.data().as_ref()), + keccak256((path, channel_id, sender.to_vec()).abi_encode_params()).as_ref(), + ) + .unwrap(); + + Addr::unchecked( + Bech32::>::new( + addr.hrp().clone(), + canonical_addr.as_slice().try_into().unwrap(), + ) + .to_string(), + ) +} diff --git a/tools/union-test/src/evm.rs b/tools/union-test/src/evm.rs index f3db96298c..4aada0c08a 100644 --- a/tools/union-test/src/evm.rs +++ b/tools/union-test/src/evm.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, panic::AssertUnwindSafe, str::FromStr, time::Duration}; +use std::{panic::AssertUnwindSafe, str::FromStr, time::Duration}; use alloy::{ contract::{Error, RawCallBuilder, Result}, @@ -6,8 +6,8 @@ use alloy::{ network::{AnyNetwork, EthereumWallet}, primitives::{Address, Bytes}, providers::{ - fillers::RecommendedFillers, DynProvider, PendingTransactionError, Provider, - ProviderBuilder, + DynProvider, PendingTransactionError, Provider, ProviderBuilder, + fillers::RecommendedFillers, }, rpc::types::Filter, signers::local::LocalSigner, @@ -17,13 +17,13 @@ use alloy::{ use bip32::secp256k1::ecdsa::{self, SigningKey}; use concurrent_keyring::{ConcurrentKeyring, KeyringConfig, KeyringEntry}; use ibc_solidity::Ibc::IbcEvents; -use ibc_union_spec::{datagram::Datagram, ChannelId}; +use ibc_union_spec::{ChannelId, datagram::Datagram}; use jsonrpsee::{core::RpcResult, types::ErrorObjectOwned}; use serde::{Deserialize, Serialize}; -use tracing::{error, info_span, warn, Instrument}; +use tracing::{Instrument, debug, error, info_span, warn}; use unionlabs::{ - primitives::{FixedBytes, H160, H256, U256}, ErrorReporter, + primitives::{FixedBytes, H160, H256, U256}, }; use voyager_sdk::{ anyhow::{self}, @@ -33,7 +33,7 @@ use voyager_sdk::{ use crate::helpers; #[derive(Debug)] -pub struct Module<'a> { +pub struct Module { pub chain_id: ChainId, pub ibc_handler_address: H160, @@ -50,8 +50,6 @@ pub struct Module<'a> { pub fixed_gas_price: Option, pub gas_multiplier: f64, - - pub _marker: PhantomData<&'a ()>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -76,7 +74,7 @@ pub struct Config { pub gas_multiplier: f64, } -impl<'a> Module<'a> { +impl Module { pub async fn new(config: Config) -> anyhow::Result { let provider = DynProvider::new( ProviderBuilder::new() @@ -139,7 +137,6 @@ impl<'a> Module<'a> { max_gas_price: config.max_gas_price, fixed_gas_price: config.fixed_gas_price, gas_multiplier: config.gas_multiplier, - _marker: PhantomData, }) } @@ -177,12 +174,10 @@ impl<'a> Module<'a> { } }; for log in logs { - if let Ok(ibc_event) = IbcEvents::decode_log(&log.inner) { - println!("Decoded IBC event: {:?}", ibc_event); - if let Some(event) = filter_fn(ibc_event.data) { - println!("Event found: {:?}", event); - events.push(event); - } + if let Ok(ibc_event) = IbcEvents::decode_log(&log.inner) + && let Some(event) = filter_fn(ibc_event.data) + { + events.push(event); } } @@ -340,16 +335,9 @@ impl<'a> Module<'a> { { let ack_bytes: &[u8] = ev.acknowledgement.as_ref(); - // Grab the first 32 bytes — this is the uint256 in ABI encoding - let mut tag_be = [0u8; 32]; - tag_be.copy_from_slice(&ack_bytes[..32]); - - // If you want it as u128 (will fail if > u128::MAX) - let tag_u128 = u128::from_be_bytes(tag_be[16..].try_into().ok()?); - Some(helpers::PacketAck { packet_hash: ev.packet_hash.into(), - tag: tag_u128, + tag: alloy::primitives::U256::from_be_slice(&ack_bytes[..32]), }) } _ => None, @@ -530,12 +518,12 @@ impl<'a> Module<'a> { .map_err(|_rpc_err| TxSubmitError::InclusionError)?; if let Some(rcpt) = maybe_rcpt { - println!("✅ tx {tx_hash:?} mined in block {:?}", rcpt.block_number); + debug!("✅ tx {tx_hash:?} mined in block {:?}", rcpt.block_number); return Ok(()); } if attempts <= 5 { attempts += 1; - println!("receipt not yet available, retry {attempts}/5…"); + debug!("receipt not yet available, retry {attempts}/5…"); tokio::time::sleep(Duration::from_secs(4)).await; continue; } @@ -545,7 +533,6 @@ impl<'a> Module<'a> { fn is_nonce_too_low(&self, e: &Error) -> bool { if let Error::TransportError(TransportError::ErrorResp(rpc)) = e { - println!("Nonce is too low, error entered here.: {:?}", rpc); rpc.message.contains("nonce too low") || rpc.message.contains("replacement transaction underpriced") } else { @@ -589,13 +576,10 @@ impl<'a> Module<'a> { match pending { Ok(pending) => { let tx_hash = ::from(*pending.tx_hash()); - println!("pending: {:?}", pending); self.wait_for_tx_inclusion(&provider, tx_hash).await?; - println!("Approved spender: {spender:?} for amount: {amount:?} on contract: {contract:?}"); return Ok(tx_hash); } Err(err) if attempts <= 5 && self.is_nonce_too_low(&err) => { - println!("Nonce too low, retrying... Attempt: {attempts}"); tokio::time::sleep(Duration::from_secs(10)).await; continue; } @@ -623,13 +607,10 @@ impl<'a> Module<'a> { match pending { Ok(pending) => { let tx_hash = ::from(*pending.tx_hash()); - println!("pending: {:?}", pending); self.wait_for_tx_inclusion(&provider, tx_hash).await?; - println!("Approved spender: {spender:?} for token_id: {token_id:?} on contract: {contract:?}"); return Ok(tx_hash); } Err(err) if attempts <= 5 && self.is_nonce_too_low(&err) => { - println!("Nonce too low, retrying... Attempt: {attempts}"); tokio::time::sleep(Duration::from_secs(10)).await; continue; } @@ -714,7 +695,6 @@ impl<'a> Module<'a> { return Ok(address.into()); } Err(err) if attempts <= 5 && self.is_nonce_too_low(&err) => { - println!("Nonce too low, retrying... Attempt: {attempts}"); tokio::time::sleep(Duration::from_secs(10)).await; continue; } @@ -727,9 +707,7 @@ impl<'a> Module<'a> { pub async fn send_ibc_transaction( &self, - _contract: H160, - msg: RawCallBuilder<&DynProvider, AnyNetwork>, - _provider: &DynProvider, + msg: RawCallBuilder, AnyNetwork>, ) -> RpcResult<(FixedBytes<32>, u64)> { let res = self .keyring @@ -761,9 +739,10 @@ impl<'a> Module<'a> { for raw in logs { if let Ok(alloy_log) = IbcEvents::decode_log(&raw.inner) - && let IbcEvents::PacketSend(ev) = alloy_log.data { - return Ok((ev.packet_hash.into(), block_number)); - } + && let IbcEvents::PacketSend(ev) = alloy_log.data + { + return Ok((ev.packet_hash.into(), block_number)); + } } Err(ErrorObjectOwned::owned( @@ -790,7 +769,7 @@ impl<'a> Module<'a> { pub async fn submit_transaction( &self, _wallet: &LocalSigner, - mut call: RawCallBuilder<&DynProvider, AnyNetwork>, + mut call: RawCallBuilder, AnyNetwork>, ) -> Result { if let Some(max_gas_price) = self.max_gas_price { let gas_price = self @@ -806,15 +785,10 @@ impl<'a> Module<'a> { max: self.max_gas_price.expect("max gas price is set"), price: gas_price, }); - } else { - println!("gas price: {}", gas_price); } } - println!("submitting evm tx"); - let gas_estimate = call.estimate_gas().await.map_err(|e| { - println!("error estimating gas: {:?}", e); if ErrorReporter(&e) .to_string() .contains("gas required exceeds") @@ -826,10 +800,6 @@ impl<'a> Module<'a> { })?; let gas_to_use = ((gas_estimate as f64) * self.gas_multiplier) as u64; - println!( - "gas estimate: {gas_estimate}, gas to use: {gas_to_use}, gas multiplier: {}", - self.gas_multiplier - ); if let Some(fixed) = self.fixed_gas_price { call = call.gas_price(fixed); @@ -840,7 +810,6 @@ impl<'a> Module<'a> { let tx_hash = ::from(*ok.tx_hash()); async move { self.wait_for_tx_inclusion(&self.provider, tx_hash).await?; - println!("tx included: {:?}", tx_hash); Ok(tx_hash) } @@ -895,11 +864,9 @@ impl<'a> Module<'a> { Ok(pending) => { let tx_hash = ::from(*pending.tx_hash()); self.wait_for_tx_inclusion(&provider, tx_hash).await?; - println!("migrateV1ToV2 executed, tx_hash = {tx_hash:?}"); return Ok(tx_hash); } Err(err) if attempts <= 5 && self.is_nonce_too_low(&err) => { - println!("nonce too low – retrying ({attempts}/5)"); tokio::time::sleep(Duration::from_secs(10)).await; continue; } @@ -965,7 +932,7 @@ impl<'a> Module<'a> { .registerGovernanceToken( channel_id, zkgm::GovernanceToken { - unwrappedToken: b"muno".into(), + unwrappedToken: b"au".into(), metadataImage: metadata_image.into(), }, ) @@ -974,18 +941,14 @@ impl<'a> Module<'a> { match pending { Ok(pending) => { let tx_hash = ::from(*pending.tx_hash()); - println!("pending: {:?}", pending); self.wait_for_tx_inclusion(&provider, tx_hash).await?; - println!("Registered governance token on channel {channel_id} with metadata image {metadata_image:?}"); return Ok(tx_hash); } Err(err) if attempts <= 10 && self.is_nonce_too_low(&err) => { - println!("Nonce too low, retrying... Attempt: {attempts}"); tokio::time::sleep(Duration::from_secs(10)).await; continue; } Err(err) => { - println!("Failed to register governance token: {:?}", err); return Err(err.into()); } } @@ -994,6 +957,17 @@ impl<'a> Module<'a> { } pub mod zkgm { + + impl From for Instruction { + fn from(value: ucs03_zkgm::com::Instruction) -> Self { + Self { + version: value.version, + opcode: value.opcode, + operand: value.operand, + } + } + } + alloy::sol! { #![sol(rpc)] struct GovernanceToken { @@ -1214,7 +1188,9 @@ pub enum TxSubmitError { EmptyRevert(Vec), #[error("gas price is too high: max {max}, price {price}")] GasPriceTooHigh { max: u128, price: u128 }, - #[error("rpc error (this is just the IbcDatagram conversion functions but i need to make those errors better)")] + #[error( + "rpc error (this is just the IbcDatagram conversion functions but i need to make those errors better)" + )] RpcError(#[from] ErrorObjectOwned), #[error("batch too large")] BatchTooLarge, diff --git a/tools/union-test/src/helpers.rs b/tools/union-test/src/helpers.rs index 28e749a652..018960a294 100644 --- a/tools/union-test/src/helpers.rs +++ b/tools/union-test/src/helpers.rs @@ -1,3 +1,4 @@ +use alloy::primitives::U256; use unionlabs::primitives::FixedBytes; #[derive(Debug, Clone, PartialEq)] @@ -30,7 +31,7 @@ pub struct PacketTimeout { #[derive(Debug, Clone, PartialEq)] pub struct PacketAck { pub packet_hash: FixedBytes<32>, - pub tag: u128, + pub tag: U256, } #[derive(Debug, Clone, PartialEq)] diff --git a/tools/union-test/src/lib.rs b/tools/union-test/src/lib.rs index 4ffd44a010..f344c05853 100644 --- a/tools/union-test/src/lib.rs +++ b/tools/union-test/src/lib.rs @@ -3,12 +3,15 @@ use std::{future::Future, sync::Arc, time::Duration}; use alloy::{contract::RawCallBuilder, network::AnyNetwork, providers::DynProvider}; // use axum::async_trait; use cosmos_client::wallet::LocalSigner; +use cosmwasm_std::Addr; use ibc_union_spec::{ path::{BatchPacketsPath, StorePath}, ChannelId, MustBeZero, Packet, }; use jsonrpsee::http_client::HttpClient; use protos::cosmos::base::v1beta1::Coin; +use regex::Regex; +use tracing::info; use unionlabs::{ ibc::core::client::height::Height, primitives::{Bech32, Bytes, FixedBytes, H160, H256}, @@ -21,10 +24,11 @@ use voyager_sdk::{ }; pub mod channel_provider; pub mod cosmos; +pub mod cosmos_helpers; pub mod evm; pub mod helpers; pub mod voyager; -use regex::Regex; +pub mod zkgm_helper; use crate::{ channel_provider::{ChannelConfirm, ChannelPair, ChannelPool}, @@ -140,8 +144,8 @@ pub trait IbcEventHash { type Hash; } -impl<'a> ChainEndpoint for evm::Module<'a> { - type Msg = RawCallBuilder<&'a DynProvider, AnyNetwork>; +impl ChainEndpoint for evm::Module { + type Msg = RawCallBuilder, AnyNetwork>; type Contract = H160; type PredictWrappedTokenResponse = H160; type PredictWrappedTokenFromMetadataImageV2Response = H160; @@ -278,13 +282,11 @@ impl<'a> ChainEndpoint for evm::Module<'a> { async fn send_ibc_transaction( &self, - contract: Self::Contract, + _: Self::Contract, msg: Self::Msg, - signer: &Self::ProviderType, + _: &Self::ProviderType, ) -> anyhow::Result<(H256, u64)> { - self.send_ibc_transaction(contract, msg, signer) - .await - .map_err(Into::into) + self.send_ibc_transaction(msg).await.map_err(Into::into) } async fn wait_for_packet_recv( @@ -326,7 +328,7 @@ impl IbcEventHash for ibc_solidity::Ibc::PacketRecv { impl ChainEndpoint for cosmos::Module { type Msg = (Vec, Vec); - type Contract = Bech32; + type Contract = Addr; type PredictWrappedTokenResponse = String; type PredictWrappedTokenFromMetadataImageV2Response = String; type ProviderType = LocalSigner; @@ -405,7 +407,7 @@ impl ChainEndpoint for cosmos::Module { async fn send_ibc_transaction( &self, - contract: Bech32, + contract: Addr, msg: Self::Msg, signer: &Self::ProviderType, ) -> anyhow::Result<(H256, u64)> { @@ -485,8 +487,8 @@ where channel_count: usize, voyager_config_file_path: &str, ) -> anyhow::Result { - voyager::init_fetch(voyager_config_file_path, src.chain_id().clone())?; - voyager::init_fetch(voyager_config_file_path, dst.chain_id().clone())?; + // voyager::init_fetch(voyager_config_file_path, src.chain_id().clone())?; + // voyager::init_fetch(voyager_config_file_path, dst.chain_id().clone())?; let channel_pool = ChannelPool::new(); println!( "Creating test context for {} and {}. Init_fetch called for both chains.", @@ -721,7 +723,7 @@ where destination_chain: &Dst, timeout: Duration, signer: &Src::ProviderType, - ) -> anyhow::Result { + ) -> anyhow::Result<(helpers::PacketRecv, helpers::PacketAck)> { let (packet_hash, _height) = match source_chain .send_ibc_transaction(contract.clone(), msg.clone(), signer) .await @@ -734,25 +736,24 @@ where anyhow::bail!("send_ibc_transaction failed: {:?}", e); } }; - println!( + + info!( "Packet sent from {} to {} with hash: {}", source_chain.chain_id(), destination_chain.chain_id(), packet_hash ); - match destination_chain + let recv = destination_chain .wait_for_packet_recv(packet_hash, timeout) - .await - { - Ok(evt) => evt, - Err(e) => anyhow::bail!("wait_for_packet_recv failed: {:?}", e), - }; + .await?; - match source_chain.wait_for_packet_ack(packet_hash, timeout).await { - Ok(evt) => Ok(evt), - Err(e) => anyhow::bail!("wait_for_packet_ack failed: {:?}", e), - } + Ok(( + recv, + source_chain + .wait_for_packet_ack(packet_hash, timeout) + .await?, + )) } #[allow(clippy::too_many_arguments)] @@ -1160,7 +1161,7 @@ where retry_delay: Duration, timeout: Duration, signer: &Src::ProviderType, - ) -> anyhow::Result { + ) -> anyhow::Result<(helpers::PacketRecv, helpers::PacketAck)> { let mut attempt = 0; println!( "Starting send_and_recv_with_retry with max_retries: {}, retry_delay: {:?}", diff --git a/tools/union-test/src/zkgm_helper.rs b/tools/union-test/src/zkgm_helper.rs new file mode 100644 index 0000000000..b989b370ed --- /dev/null +++ b/tools/union-test/src/zkgm_helper.rs @@ -0,0 +1,26 @@ +use alloy::primitives::Bytes; +use alloy_sol_types::SolValue as _; +use ucs03_zkgm::com::{Call, Instruction, INSTR_VERSION_0, OP_BATCH, OP_CALL}; + +pub fn make_batch(instructions: Vec) -> Instruction { + Instruction { + version: INSTR_VERSION_0, + opcode: OP_BATCH, + operand: instructions.abi_encode_params().into(), + } +} + +pub fn make_call(sender: Bytes, contract_address: Bytes, contract_calldata: Bytes) -> Instruction { + Instruction { + version: INSTR_VERSION_0, + opcode: OP_CALL, + operand: Call { + sender, + eureka: false, + contract_address, + contract_calldata, + } + .abi_encode_params() + .into(), + } +} diff --git a/tools/union-test/tests/config.json b/tools/union-test/tests/config.json new file mode 100644 index 0000000000..7b47ca79e8 --- /dev/null +++ b/tools/union-test/tests/config.json @@ -0,0 +1,114 @@ +{ + "union": { + "chain_id": "union-devnet-1", + "ibc_host_contract_address": "union1nk3nes4ef6vcjan5tz6stf9g8p08q2kgqysx6q5exxh89zakp0msq5z79t", + "keyring": { + "name": "alice", + "keys": [ + { + "type": "raw", + "name": "bob", + "key": "0xf562d20f0a4ffd8814d262f7023f33971cbcd14a96d60027585777f174b9cdeb" + }, + { + "type": "raw", + "name": "dave", + "key": "0xedc165ff1ebc27044ddc284c9cf5da656dcbff324f6ecbb9d3203cf5f4738d6d" + }, + { + "type": "raw", + "name": "charlie", + "key": "0xa1f713e0f36404586085a599a45ca8233e23709e23cd54bc8d5452ef8f7bc1e6" + } + ] + }, + "privileged_acc_keyring": { + "name": "privileged_acc", + "keys": [ + { + "type": "raw", + "name": "privileged_acc", + "key": "0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f" + } + ] + }, + "rpc_url": "http://devnetUnion:26657", + "gas_config": { + "type": "feemarket", + "config": { + "max_gas": 10000000, + "gas_multiplier": 1.4, + "denom": null + } + }, + "fee_recipient": null + }, + "evm": { + "chain_id": "32382", + "ibc_handler_address": "0xed2af2ad7fe0d92011b26a2e5d1b4dc7d12a47c5", + "multicall_address": "0x84c4c2ee43ccfd523af9f78740256e0f60d38068", + "rpc_url": "http://devnetEth:8545", + "ws_url": "ws://devnetEth:8546", + "keyring": { + "name": "evm-keyring", + "keys": [ + { + "type": "raw", + "name": "dev-key0.prv", + "key": "0x4e9444a6efd6d42725a250b650a781da2737ea308c839eaccb0f7f3dbd2fea77" + }, + { + "type": "raw", + "name": "dev-key1.prv", + "key": "0xd9c5dc47ed678fc3e63249953866d79e5cf48418e79d8eec1a985be7393ef3b9" + }, + { + "type": "raw", + "name": "dev-key2.prv", + "key": "0xeadf66c84a1c2768a14e883512724d6023a54d500bf91d910a7dace376a97d6b" + }, + { + "type": "raw", + "name": "dev-key3.prv", + "key": "0xd56f932b298ba86341037f3871141a707330316f6f9493641a2cd59ba4a53710" + }, + { + "type": "raw", + "name": "dev-key4.prv", + "key": "0x084494a1ff88a1319e493d32aa6e127ab0eaaaf74b8714edfd670a9ddc4a060d" + }, + { + "type": "raw", + "name": "dev-key5.prv", + "key": "0xf977996449841b13ce9bbb99873006e04590ddbe28d9cd449dd33505851e74ba" + }, + { + "type": "raw", + "name": "dev-key6.prv", + "key": "0x523776c0e15a5826c85f08e0dd20d70190b0001e87f6ff9f25854d10f24db63c" + }, + { + "type": "raw", + "name": "dev-key7.prv", + "key": "0xb7d500ecae3d26deaa9547557822c95208163e230cc04345bd223da99f5bd058" + } + ] + }, + "privileged_acc_keyring": { + "name": "zkgm-deployer", + "keys": [ + { + "type": "raw", + "name": "zkgm-deployer-key", + "key": "0x4e9444a6efd6d42725a250b650a781da2737ea308c839eaccb0f7f3dbd2fea77" + } + ] + }, + "max_gas_price": null, + "fixed_gas_price": null, + "gas_multiplier": 2.0 + }, + "needed_channel_count": 1, + "voyager_config_file_path": "./voyager", + "union_deployer_addr": "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2" +} diff --git a/tools/union-test/tests/e2e.nix b/tools/union-test/tests/e2e.nix new file mode 100644 index 0000000000..92972481f1 --- /dev/null +++ b/tools/union-test/tests/e2e.nix @@ -0,0 +1,23 @@ +_: { + perSystem = + { + crane, + ... + }: + let + mkTest = + name: + (crane.buildWorkspaceMember "tools/union-test" { + dontRemoveDevDeps = true; + cargoBuildExtraArgs = "--tests"; + cargoBuildInstallPhase = '' + mkdir -p $out + FNAME=$(printf `ls -f -I '.*d' target/release/deps/${name}-*`) + mv "$FNAME" $out/${name} + ''; + }).union-test; + in + { + packages.e2e-lst-tests = mkTest "lst"; + }; +} diff --git a/tools/union-test/tests/e2e.rs b/tools/union-test/tests/e2e.rs index ea1a0a3f48..5e61620af0 100644 --- a/tools/union-test/tests/e2e.rs +++ b/tools/union-test/tests/e2e.rs @@ -1,8 +1,5 @@ -// tests/e2e.rs - use std::{ num::NonZero, - str::FromStr, sync::Arc, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -11,13 +8,13 @@ use alloy::{ hex::decode as hex_decode, sol_types::{SolCall, SolValue}, }; -use concurrent_keyring::{KeyringConfig, KeyringConfigEntry}; -use cosmos::{FeemarketConfig, GasFillerConfig}; +use cosmwasm_std::Addr; use cw20::Cw20ExecuteMsg; use hex_literal::hex; use ibc_union_spec::ChannelId; use protos::cosmos::base::v1beta1::Coin; use rand::RngCore; +use serde::Deserialize; use tokio::sync::OnceCell; use ucs03_zkgm::{ self, @@ -29,6 +26,7 @@ use ucs03_zkgm::{ }; use union_test::{ cosmos::{self}, + cosmos_helpers::{calculate_cosmos_contract_address, SALT_ESCROW_VAULT, SALT_ZKGM}, evm::{ self, zkgm::{ @@ -41,163 +39,62 @@ use union_test::{ use unionlabs::{ encoding::{Encode, Json}, ethereum::keccak256, - primitives::{Bech32, FixedBytes, H160, U256}, + primitives::{H160, U256}, }; -use voyager_sdk::primitives::{ChainId, Timestamp}; +use voyager_sdk::{primitives::Timestamp, serde_json}; -static CTX: OnceCell>> = OnceCell::const_new(); +static CTX: OnceCell> = OnceCell::const_new(); static CHANNELS_OPENED: OnceCell<()> = OnceCell::const_new(); -// static ERC20: OnceCell = OnceCell::const_new(); -static UNION_ZKGM_ADDRESS: &str = - "union1rfz3ytg6l60wxk5rxsk27jvn2907cyav04sz8kde3xhmmf9nplxqr8y05c"; -// static UNION_MINTER_ADDRESS: &str = -// "union1tt6nn3qv0q0z4gq4s2h65a2acv3lcwxjwf8ey3jgnwmtqkfnyq9q4q5y8x"; -static EVM_ZKGM_BYTES: [u8; 20] = hex!("05fd55c1abe31d3ed09a76216ca8f0372f4b2ec5"); -static EVM_IBC_BYTES: [u8; 20] = hex!("ed2af2aD7FE0D92011b26A2e5D1B4dC7D12A47C5"); +pub const ETH_ADDRESS_U: H160 = H160::new(hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836")); +pub const ETH_ADDRESS_ZKGM: H160 = H160::new(hex!("05fd55c1abe31d3ed09a76216ca8f0372f4b2ec5")); +pub const ETH_ADDRESS_IBC: H160 = H160::new(hex!("ed2af2aD7FE0D92011b26A2e5D1B4dC7D12A47C5")); + +#[derive(Deserialize)] +pub struct Config { + evm: evm::Config, + union: cosmos::Config, + needed_channel_count: u32, + voyager_config_file_path: String, + union_deployer_addr: String, +} + +pub struct UnionAddressBook { + pub zkgm: Addr, + pub escrow_vault: Addr, +} -async fn init_ctx<'a>() -> Arc>> { +pub struct ZkgmCtx { + pub union_address: UnionAddressBook, + pub ctx: TestContext, +} + +async fn init_ctx() -> Arc { CTX.get_or_init(|| async { - let cosmos_cfg = cosmos::Config { - chain_id: ChainId::new("union-devnet-1"), - ibc_host_contract_address: Bech32::from_str( - "union1nk3nes4ef6vcjan5tz6stf9g8p08q2kgqysx6q5exxh89zakp0msq5z79t", + let cfg: Config = serde_json::from_str(include_str!("./config.json")).unwrap(); + + let src = cosmos::Module::new(cfg.union).await.unwrap(); + let dst = evm::Module::new(cfg.evm).await.unwrap(); + + let ctx = ZkgmCtx { + ctx: TestContext::new( + src, + dst, + cfg.needed_channel_count as usize, + &cfg.voyager_config_file_path, ) + .await .unwrap(), - privileged_acc_keyring: KeyringConfig { - name: "privileged_acc".into(), - keys: vec![KeyringConfigEntry::Raw { - name: "privileged_acc".into(), - key: hex!("aa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f") - .to_vec(), - }], - }, - keyring: KeyringConfig { - name: "alice".into(), - keys: vec![ - KeyringConfigEntry::Raw { - name: "bob".into(), - key: hex_literal::hex!( - "f562d20f0a4ffd8814d262f7023f33971cbcd14a96d60027585777f174b9cdeb" - ) - .to_vec(), - }, - KeyringConfigEntry::Raw { - name: "dave".into(), - key: hex_literal::hex!( - "edc165ff1ebc27044ddc284c9cf5da656dcbff324f6ecbb9d3203cf5f4738d6d" - ) - .to_vec(), - }, - KeyringConfigEntry::Raw { - name: "charlie".into(), - key: hex_literal::hex!( - "a1f713e0f36404586085a599a45ca8233e23709e23cd54bc8d5452ef8f7bc1e6" - ) - .to_vec(), - }, - ], - }, - rpc_url: "http://0.0.0.0:26657".into(), - gas_config: GasFillerConfig::Feemarket(FeemarketConfig { - max_gas: 10000000, - gas_multiplier: Some(1.4), - denom: None, - }), - fee_recipient: None, - }; - let evm_cfg = evm::Config { - chain_id: ChainId::new("32382"), - ibc_handler_address: hex!("ed2af2aD7FE0D92011b26A2e5D1B4dC7D12A47C5").into(), - multicall_address: hex!("84c4c2ee43ccfd523af9f78740256e0f60d38068").into(), - rpc_url: "http://0.0.0.0:8545".into(), - ws_url: "ws://0.0.0.0:8546".into(), - privileged_acc_keyring: KeyringConfig { - name: "zkgm-deployer".into(), - keys: vec![KeyringConfigEntry::Raw { - name: "zkgm-deployer-key".into(), - key: hex!("4e9444a6efd6d42725a250b650a781da2737ea308c839eaccb0f7f3dbd2fea77") - .to_vec(), - }], - }, - keyring: KeyringConfig { - name: "evm-keyring".into(), - keys: vec![ - KeyringConfigEntry::Raw { - name: "dev-key0.prv".into(), - key: hex!( - "4e9444a6efd6d42725a250b650a781da2737ea308c839eaccb0f7f3dbd2fea77" - ) - .to_vec(), - }, - // KeyringConfigEntry::Raw { - // name: "dev-key1.prv".into(), - // key: hex!( - // "d9c5dc47ed678fc3e63249953866d79e5cf48418e79d8eec1a985be7393ef3b9" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key2.prv".into(), - // key: hex!( - // "eadf66c84a1c2768a14e883512724d6023a54d500bf91d910a7dace376a97d6b" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key3.prv".into(), - // key: hex!( - // "d56f932b298ba86341037f3871141a707330316f6f9493641a2cd59ba4a53710" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key4.prv".into(), - // key: hex!( - // "084494a1ff88a1319e493d32aa6e127ab0eaaaf74b8714edfd670a9ddc4a060d" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key5.prv".into(), - // key: hex!( - // "f977996449841b13ce9bbb99873006e04590ddbe28d9cd449dd33505851e74ba" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key6.prv".into(), - // key: hex!( - // "523776c0e15a5826c85f08e0dd20d70190b0001e87f6ff9f25854d10f24db63c" - // ) - // .to_vec(), - // }, - // KeyringConfigEntry::Raw { - // name: "dev-key7.prv".into(), - // key: hex!( - // "b7d500ecae3d26deaa9547557822c95208163e230cc04345bd223da99f5bd058" - // ) - // .to_vec(), - // }, - ], + union_address: UnionAddressBook { + zkgm: calculate_cosmos_contract_address(&cfg.union_deployer_addr, SALT_ZKGM) + .unwrap(), + escrow_vault: calculate_cosmos_contract_address( + &cfg.union_deployer_addr, + SALT_ESCROW_VAULT, + ) + .unwrap(), }, - max_gas_price: None, - fixed_gas_price: None, - gas_multiplier: 2.0, }; - let src = cosmos::Module::new(cosmos_cfg).await.unwrap(); - let dst = evm::Module::new(evm_cfg).await.unwrap(); - let needed_channel_count = 19; // TODO: Hardcoded now, it will be specified from config later. - - // TODO(aeryz): move config file into the testing framework's own config file - let ctx = TestContext::new( - src, - dst, - needed_channel_count, - "/home/kaancaglan/dev/union/voyager/config.jsonc", - ) - .await - .unwrap_or_else(|e| panic!("failed to build TestContext: {:#?}", e)); Arc::new(ctx) }) @@ -208,9 +105,10 @@ async fn init_ctx<'a>() -> Arc>> { async fn ensure_channels_opened(channel_count: usize) { CHANNELS_OPENED .get_or_init(|| async move { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (src_client, dst_client) = ctx + let (src_client, dst_client) = t + .ctx .create_clients( Duration::from_secs(60), "ibc-cosmwasm", @@ -224,11 +122,12 @@ async fn ensure_channels_opened(channel_count: usize) { assert!(src_client.client_id > 0); assert!(dst_client.client_id > 0); - let conn = ctx + let conn = t + .ctx .open_connection::( - &ctx.src, + &t.ctx.src, src_client.client_id, - &ctx.dst, + &t.ctx.dst, dst_client.client_id, Duration::from_secs(180), ) @@ -237,13 +136,14 @@ async fn ensure_channels_opened(channel_count: usize) { assert!(conn.connection_id > 0); assert!(conn.counterparty_connection_id > 0); - let current_available_count = ctx.get_available_channel_count().await; + let current_available_count = t.ctx.get_available_channel_count().await; - let opened = ctx + let opened = t + .ctx .open_channels( true, - UNION_ZKGM_ADDRESS.as_bytes().into(), - EVM_ZKGM_BYTES.to_vec().into(), + t.union_address.zkgm.as_bytes().into(), + ETH_ADDRESS_ZKGM.into(), conn.counterparty_connection_id, "ucs03-zkgm-0".into(), channel_count, @@ -253,62 +153,33 @@ async fn ensure_channels_opened(channel_count: usize) { .unwrap(); assert_eq!(opened, channel_count); - let available_count_after_open = ctx.get_available_channel_count().await; + let available_count_after_open = t.ctx.get_available_channel_count().await; assert_eq!( current_available_count + channel_count, available_count_after_open ); - let pair = ctx.get_channel().await.expect("channel available"); - let available_count_after_get = ctx.get_available_channel_count().await; + let pair = t.ctx.get_channel().await.expect("channel available"); + let available_count_after_get = t.ctx.get_available_channel_count().await; assert_eq!(available_count_after_open - 1, available_count_after_get); - ctx.release_channel(pair).await; - let available_count_after_release = ctx.get_available_channel_count().await; + t.ctx.release_channel(pair).await; + let available_count_after_release = t.ctx.get_available_channel_count().await; assert_eq!(available_count_after_open, available_count_after_release); }) .await; } -async fn _open_connection_from_evm_to_union() { - let ctx = init_ctx().await; - let (src_client, dst_client) = ctx - .create_clients( - Duration::from_secs(45), - "ibc-cosmwasm", - "trusted/evm/mpt", - "ibc-solidity", - "cometbls", - ) - .await - .unwrap(); - - assert!(src_client.client_id > 0); - assert!(dst_client.client_id > 0); - - let conn = ctx - .open_connection::( - &ctx.dst, - dst_client.client_id, - &ctx.src, - src_client.client_id, - Duration::from_secs(180), - ) - .await - .unwrap(); - assert!(conn.connection_id > 0); - assert!(conn.counterparty_connection_id > 0); -} - +#[tokio::test] async fn test_send_vault_success() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_channel_id = pair.dest; let src_channel_id = pair.src; @@ -329,7 +200,7 @@ async fn test_send_vault_success() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.into(), @@ -340,14 +211,15 @@ async fn test_send_vault_success() { .into(), }; - let (_, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; - ctx.dst + let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; + t.ctx + .dst .u_register_fungible_counterpart( H160::from(u_on_eth), zkgm_deployer_provider.clone(), alloy::primitives::U256::ZERO, dst_channel_id, - b"muno".to_vec().into(), + b"au".to_vec().into(), evm::u::U::FungibleCounterparty { beneficiary: vault_on_union.as_bytes().to_vec().into(), }, @@ -368,13 +240,12 @@ async fn test_send_vault_success() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); - - let initial_u_balance = ctx + let initial_u_balance = t + .ctx .dst .zkgmerc20_balance_of( H160::from(u_on_eth), @@ -384,21 +255,23 @@ async fn test_send_vault_success() { .await .unwrap(); - let initial_vault_balance = ctx + let initial_vault_balance = t + .ctx .src - .native_balance(Bech32::from_str(vault_on_union).unwrap(), "muno") + .native_balance(t.union_address.escrow_vault.clone(), "au") .await .unwrap(); println!("initial U balance on eth: {initial_u_balance}"); println!("initial U balance on union vault: {initial_vault_balance}"); - let ack_packet_data = ctx + let ack_packet_data = t + .ctx .send_and_recv_and_ack_with_retry::( - &ctx.src, - contract, + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -411,19 +284,17 @@ async fn test_send_vault_success() { ack_packet_data.err() ); - let new_u_balance = ctx + let new_u_balance = t + .ctx .dst - .zkgmerc20_balance_of( - H160::from(u_on_eth), - evm_address.into(), - evm_provider.clone(), - ) + .zkgmerc20_balance_of(ETH_ADDRESS_U, evm_address.into(), evm_provider.clone()) .await .unwrap(); - let new_vault_balance = ctx + let new_vault_balance = t + .ctx .src - .native_balance(Bech32::from_str(vault_on_union).unwrap(), "muno") + .native_balance(t.union_address.escrow_vault.clone(), "au") .await .unwrap(); @@ -434,17 +305,18 @@ async fn test_send_vault_success() { assert_eq!(new_vault_balance - initial_vault_balance, 10); } +#[tokio::test] async fn test_send_vault_success_with_fee() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_channel_id = pair.dest; let src_channel_id = pair.src; @@ -466,7 +338,7 @@ async fn test_send_vault_success_with_fee() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: recv_addr.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "15".parse().unwrap(), // So fee will be 5 and will be minted to relayer kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.into(), @@ -477,14 +349,15 @@ async fn test_send_vault_success_with_fee() { .into(), }; - let (_, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; - ctx.dst + let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; + t.ctx + .dst .u_register_fungible_counterpart( H160::from(u_on_eth), zkgm_deployer_provider.clone(), alloy::primitives::U256::ZERO, dst_channel_id, - b"muno".to_vec().into(), + b"au".to_vec().into(), evm::u::U::FungibleCounterparty { beneficiary: vault_on_union.as_bytes().to_vec().into(), }, @@ -505,31 +378,28 @@ async fn test_send_vault_success_with_fee() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "15".into(), // So fee will be 5 and will be minted to relayer }]; - let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); - - let initial_u_balance = ctx + let initial_u_balance = t + .ctx .dst - .zkgmerc20_balance_of(H160::from(u_on_eth), recv_addr.into(), evm_provider.clone()) + .zkgmerc20_balance_of(ETH_ADDRESS_U, recv_addr.into(), evm_provider.clone()) .await .unwrap(); - let initial_vault_balance = ctx + let initial_vault_balance = t + .ctx .src - .native_balance(Bech32::from_str(vault_on_union).unwrap(), "muno") + .native_balance(t.union_address.escrow_vault.clone(), "au") .await .unwrap(); - let initial_balance_of_relayer = ctx + let initial_balance_of_relayer = t + .ctx .dst - .zkgmerc20_balance_of( - H160::from(u_on_eth), - evm_address.into(), - evm_provider.clone(), - ) + .zkgmerc20_balance_of(ETH_ADDRESS_U, evm_address.into(), evm_provider.clone()) .await .unwrap(); @@ -537,12 +407,13 @@ async fn test_send_vault_success_with_fee() { println!("initial U balance on union vault: {initial_vault_balance}"); println!("initial balance of relayer: {initial_balance_of_relayer}"); - let ack_packet_data = ctx + let ack_packet_data = t + .ctx .send_and_recv_and_ack_with_retry::( - &ctx.src, - contract, + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -555,19 +426,22 @@ async fn test_send_vault_success_with_fee() { ack_packet_data.err() ); - let new_u_balance = ctx + let new_u_balance = t + .ctx .dst - .zkgmerc20_balance_of(H160::from(u_on_eth), recv_addr.into(), evm_provider.clone()) + .zkgmerc20_balance_of(ETH_ADDRESS_U, recv_addr.into(), evm_provider.clone()) .await .unwrap(); - let new_vault_balance = ctx + let new_vault_balance = t + .ctx .src - .native_balance(Bech32::from_str(vault_on_union).unwrap(), "muno") + .native_balance(t.union_address.escrow_vault.clone(), "au") .await .unwrap(); - let new_balance_of_relayer = ctx + let new_balance_of_relayer = t + .ctx .dst .zkgmerc20_balance_of( H160::from(u_on_eth), @@ -589,27 +463,29 @@ async fn test_send_vault_success_with_fee() { ); } +#[tokio::test] async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { - let ctx = init_ctx().await; - ensure_channels_opened(ctx.channel_count).await; + let t = init_ctx().await; + ensure_channels_opened(t.ctx.channel_count).await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - let available_channel = ctx.get_available_channel_count().await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let src_chain_id = pair.src; - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, ChannelId::new(NonZero::new(dst_chain_id).unwrap()), - "muno".into(), + "au".into(), &evm_provider, ) .await @@ -617,7 +493,6 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { let mut salt_bytes = [0u8; 32]; rand::thread_rng().fill_bytes(&mut salt_bytes); - let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); let instruction_cosmos = Instruction { version: INSTR_VERSION_1, @@ -625,10 +500,10 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { operand: TokenOrderV1 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), - base_token_symbol: "muno".into(), - base_token_name: "muno".into(), + base_token_symbol: "au".into(), + base_token_name: "au".into(), base_token_decimals: 6, base_token_path: "0".parse().unwrap(), quote_token: quote_token_addr.as_ref().to_vec().into(), @@ -648,16 +523,17 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_with_retry::( - &ctx.src, - contract, + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -680,11 +556,11 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { receiver: cosmos_address_bytes.clone().into(), base_token: quote_token_addr.as_ref().to_vec().into(), base_amount: "1".parse().unwrap(), - base_token_symbol: "muno".into(), - base_token_name: "muno".into(), + base_token_symbol: "au".into(), + base_token_name: "au".into(), base_token_decimals: 6, base_token_path: dst_chain_id.try_into().unwrap(), - quote_token: "muno".into(), + quote_token: "au".into(), quote_amount: "1".parse().unwrap(), } .abi_encode_params() @@ -693,7 +569,7 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { println!("quote_token: {:?}", quote_token_addr); - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); let call = ucs03_zkgm .send( @@ -703,14 +579,16 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_with_retry::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, - &ctx.src, + &t.ctx.src, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -727,37 +605,38 @@ async fn test_send_packet_from_union_to_evm_and_send_back_unwrap() { ); } +#[tokio::test] async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { - let ctx = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_signer) = ctx.src.get_signer().await; + let t = init_ctx().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_signer) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); println!("Cosmos Address: {:?}", cosmos_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let src_chain_id = pair.src; // let deployed_erc20 = ensure_erc20(EVM_ZKGM_BYTES.into()).await; - let deployed_erc20 = ctx + let deployed_erc20 = t + .ctx .dst - .deploy_basic_erc20(EVM_ZKGM_BYTES.into(), evm_provider.clone()) + .deploy_basic_erc20(ETH_ADDRESS_ZKGM, evm_provider.clone()) .await .expect("failed to deploy ERC20"); - let union_zkgm_contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); - - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token::( - &ctx.src, - union_zkgm_contract, + &t.ctx.src, + t.union_address.zkgm.clone(), ChannelId::new(NonZero::new(src_chain_id).unwrap()), deployed_erc20.as_ref().to_vec(), cosmos_signer, @@ -792,7 +671,7 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { .into(), }; - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); let call = ucs03_zkgm .send( @@ -802,14 +681,16 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); - let recv_packet_data: Result = ctx + let recv_packet_data: Result = t + .ctx .send_and_recv_with_retry::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, - &ctx.src, + &t.ctx.src, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -827,9 +708,10 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { recv_packet_data ); - let get_minter_result = ctx + let get_minter_result = t + .ctx .src - .get_minter(Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap()) + .get_minter(t.union_address.zkgm.clone()) .await .expect("failed to get minter address"); @@ -843,14 +725,18 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { let quote_token_bytes = hex_decode(quote_token_addr.trim_start_matches("0x")) .expect("invalid quote‐token address hex"); - let approve_contract: Bech32> = - Bech32::from_str(std::str::from_utf8("e_token_bytes).unwrap()).unwrap(); + let approve_contract = Addr::unchecked(str::from_utf8("e_token_bytes).unwrap()); println!("Calling approve on quote tokenbytes: {:?}, quote_token:{:?} -> from account: {:?}. Approve contract: {:?}", quote_token_addr, quote_token_bytes, cosmos_address, approve_contract); - let approve_recv_packet_data = ctx + let approve_recv_packet_data = t + .ctx .src - .send_transaction_with_retry(approve_contract, (approve_msg_bin, vec![]), cosmos_signer) + .send_cosmwasm_transaction_with_retry( + approve_contract, + (approve_msg_bin, vec![]), + cosmos_signer, + ) .await; // println!("Approve transaction data: {:?}", approve_recv_packet_data); @@ -889,14 +775,14 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![]; - let union_zkgm_contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_with_retry::( - &ctx.src, - union_zkgm_contract, + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -913,27 +799,29 @@ async fn test_send_packet_from_evm_to_union_and_send_back_unwrap() { ); } +#[tokio::test] async fn test_send_packet_from_union_to_evm_get_refund() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let src_chain_id = pair.src; - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, ChannelId::new(NonZero::new(dst_chain_id).unwrap()), - "muno".into(), + "au".into(), &evm_provider, ) .await @@ -943,7 +831,6 @@ async fn test_send_packet_from_union_to_evm_get_refund() { let mut salt_bytes = [0u8; 32]; rand::thread_rng().fill_bytes(&mut salt_bytes); - let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); let sending_amount = "9999999999999999999999"; let instruction_cosmos = Instruction { version: INSTR_VERSION_1, @@ -951,10 +838,10 @@ async fn test_send_packet_from_union_to_evm_get_refund() { operand: TokenOrderV1 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: sending_amount.parse().unwrap(), - base_token_symbol: "muno".into(), - base_token_name: "muno".into(), + base_token_symbol: "au".into(), + base_token_name: "au".into(), base_token_decimals: 6, base_token_path: "0".parse().unwrap(), quote_token: cosmos_address_bytes.clone().into(), // it will revert @@ -985,13 +872,14 @@ async fn test_send_packet_from_union_to_evm_get_refund() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: sending_amount.into(), }]; - let muno_balance_before_send = ctx + let muno_balance_before_send = t + .ctx .src - .get_balance(&cosmos_address.clone().to_string(), "muno") + .get_balance(&cosmos_address.clone().to_string(), "au") .await; assert!( muno_balance_before_send.is_ok(), @@ -1005,12 +893,13 @@ async fn test_send_packet_from_union_to_evm_get_refund() { old_balance ); - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_refund_with_timeout::( - &ctx.src, - contract, + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(1720), cosmos_provider, ) @@ -1026,9 +915,10 @@ async fn test_send_packet_from_union_to_evm_get_refund() { recv_packet_data ); - let muno_balance_after_send = ctx + let muno_balance_after_send = t + .ctx .src - .get_balance(&cosmos_address.clone().to_string(), "muno") + .get_balance(&cosmos_address.clone().to_string(), "au") .await; assert!( muno_balance_after_send.is_ok(), @@ -1049,37 +939,38 @@ async fn test_send_packet_from_union_to_evm_get_refund() { // new_balance, old_balance - sending_amount.into()); } +#[tokio::test] async fn test_send_packet_from_evm_to_union_get_refund() { - let ctx = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_signer) = ctx.src.get_signer().await; + let t = init_ctx().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_signer) = t.ctx.src.get_signer().await; println!("EVM Address: {:?}", evm_address); println!("Cosmos Address: {:?}", cosmos_address); - ensure_channels_opened(ctx.channel_count).await; + ensure_channels_opened(t.ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let src_chain_id = pair.src; // let deployed_erc20 = ensure_erc20(EVM_ZKGM_BYTES.into()).await; - let deployed_erc20 = ctx + let deployed_erc20 = t + .ctx .dst - .deploy_basic_erc20(EVM_ZKGM_BYTES.into(), evm_provider.clone()) + .deploy_basic_erc20(ETH_ADDRESS_ZKGM, evm_provider.clone()) .await .expect("failed to deploy ERC20"); - let union_zkgm_contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); - - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token::( - &ctx.src, - union_zkgm_contract, + &t.ctx.src, + t.union_address.zkgm.clone(), ChannelId::new(NonZero::new(src_chain_id).unwrap()), deployed_erc20.as_ref().to_vec(), cosmos_signer, @@ -1115,7 +1006,8 @@ async fn test_send_packet_from_evm_to_union_get_refund() { .into(), }; - let erc20_balance_before_send = ctx + let erc20_balance_before_send = t + .ctx .dst .zkgmerc20_balance_of(deployed_erc20, evm_address.into(), evm_provider.clone()) .await; @@ -1130,7 +1022,7 @@ async fn test_send_packet_from_evm_to_union_get_refund() { evm_address, erc20_balance_before_send ); - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); let call = ucs03_zkgm .send( @@ -1140,14 +1032,16 @@ async fn test_send_packet_from_evm_to_union_get_refund() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_refund::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, - &ctx.src, + &t.ctx.src, Duration::from_secs(720), &evm_provider, ) @@ -1163,7 +1057,8 @@ async fn test_send_packet_from_evm_to_union_get_refund() { recv_packet_data ); - let erc20_balance_after_send = ctx + let erc20_balance_after_send = t + .ctx .dst .zkgmerc20_balance_of(deployed_erc20, evm_address.into(), evm_provider.clone()) .await; @@ -1185,18 +1080,19 @@ async fn test_send_packet_from_evm_to_union_get_refund() { ); } +#[tokio::test] async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, _evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, _evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let img_metadata = ucs03_zkgm::com::TokenMetadata { implementation: hex!("999709eB04e8A30C7aceD9fd920f7e04EE6B97bA") @@ -1204,9 +1100,9 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { .into(), initializer: ZkgmERC20::initializeCall { _authority: hex!("6C1D11bE06908656D16EBFf5667F1C45372B7c89").into(), - _minter: EVM_ZKGM_BYTES.into(), - _name: "muno".into(), - _symbol: "muno".into(), + _minter: ETH_ADDRESS_ZKGM.into(), + _name: "au".into(), + _symbol: "au".into(), _decimals: 6u8, } .abi_encode() @@ -1214,7 +1110,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { } .abi_encode_params(); - let (zkgm_deployer_address, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (zkgm_deployer_address, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; let mut salt_bytes = [0u8; 32]; rand::thread_rng().fill_bytes(&mut salt_bytes); @@ -1225,7 +1121,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_INITIALIZE, metadata: img_metadata.clone().into(), @@ -1242,7 +1138,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_INITIALIZE, metadata: img_metadata.into(), @@ -1263,16 +1159,17 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let height = ctx + let height = t + .ctx .send_and_get_height::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(720), cosmos_provider, ) @@ -1303,9 +1200,10 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { timeoutTimestamp: 4294967295000000000, }]; - let proof = ctx + let proof = t + .ctx .calculate_proof::( - &ctx.dst, + &t.ctx.dst, pair.src, pair.dest, encoded_packet, @@ -1330,15 +1228,18 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { proofHeight: height, }; - let ibc = IBC::new(EVM_IBC_BYTES.into(), zkgm_deployer_provider.clone()); - - let call = ibc.recvPacket(recv_packet_msg).clear_decoder(); + let ibc = IBC::new(ETH_ADDRESS_IBC.into(), zkgm_deployer_provider.clone()); + let call = ibc + .recvPacket(recv_packet_msg) + .clear_decoder() + .with_cloned_provider(); let expected_revert_code = 0x3717ba2c; // Only maker - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_IBC_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_IBC, call, expected_revert_code, &zkgm_deployer_provider, @@ -1352,18 +1253,19 @@ async fn test_from_evm_to_union_tokenv2_unhappy_only_maker_err() { ); } +#[tokio::test] async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let img_metadata = ucs03_zkgm::com::TokenMetadata { implementation: hex!("999709eB04e8A30C7aceD9fd920f7e04EE6B97bA") @@ -1371,9 +1273,9 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { .into(), initializer: ZkgmERC20::initializeCall { _authority: hex!("6C1D11bE06908656D16EBFf5667F1C45372B7c89").into(), - _minter: EVM_ZKGM_BYTES.into(), - _name: "muno".into(), - _symbol: "muno".into(), + _minter: ETH_ADDRESS_ZKGM.into(), + _name: "au".into(), + _symbol: "au".into(), _decimals: 6u8, } .abi_encode() @@ -1381,10 +1283,11 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { } .abi_encode_params(); - let (_zkgm_deployer_address, _zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (_zkgm_deployer_address, _zkgm_deployer_provider) = + t.ctx.dst.get_provider_privileged().await; let img = keccak256(&img_metadata); - let (_, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; // let governance_token = ctx // .dst // .setup_governance_token( @@ -1403,12 +1306,13 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { let mut salt_bytes = [0u8; 32]; rand::thread_rng().fill_bytes(&mut salt_bytes); - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token_from_metadata_image_v2::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, ChannelId::new(NonZero::new(pair.dest).unwrap()), - "muno".into(), + "au".into(), img, &evm_provider, ) @@ -1423,7 +1327,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_INITIALIZE, metadata: img_metadata.clone().into(), @@ -1445,16 +1349,17 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_recv_with_retry::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, 3, Duration::from_secs(20), Duration::from_secs(720), @@ -1474,11 +1379,12 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { quote_token_addr, evm_address ); - let approve_tx_hash = ctx + let approve_tx_hash = t + .ctx .dst .zkgmerc20_approve( quote_token_addr, - EVM_ZKGM_BYTES.into(), + ETH_ADDRESS_ZKGM, U256::from(100000000000u64), evm_provider.clone(), ) @@ -1503,7 +1409,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "munooo".as_bytes().into(), // Which is wrong, so it will revert ErrInvalidUnescrow + base_token: "au".as_bytes().into(), // Which is wrong, so it will revert ErrInvalidUnescrow base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_UNESCROW, metadata: img_metadata.into(), @@ -1514,7 +1420,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { .into(), }; - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); rand::thread_rng().fill_bytes(&mut salt_bytes); let call = ucs03_zkgm @@ -1525,13 +1431,15 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); let expected_revert_code = 0x96c99a39; // ErrChannelGovernanceTokenNotSet - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, expected_revert_code, &zkgm_deployer_provider, @@ -1545,18 +1453,19 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow() { ); } +#[tokio::test] async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let img_metadata = ucs03_zkgm::com::TokenMetadata { implementation: hex!("999709eB04e8A30C7aceD9fd920f7e04EE6B97bA") @@ -1564,9 +1473,9 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { .into(), initializer: ZkgmERC20::initializeCall { _authority: hex!("6C1D11bE06908656D16EBFf5667F1C45372B7c89").into(), - _minter: EVM_ZKGM_BYTES.into(), - _name: "muno".into(), - _symbol: "muno".into(), + _minter: ETH_ADDRESS_ZKGM.into(), + _name: "au".into(), + _symbol: "au".into(), _decimals: 6u8, } .abi_encode() @@ -1577,12 +1486,13 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { let mut salt_bytes = [0u8; 32]; rand::thread_rng().fill_bytes(&mut salt_bytes); - let quote_token_addr = ctx + let quote_token_addr = t + .ctx .predict_wrapped_token::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, ChannelId::new(NonZero::new(pair.dest).unwrap()), - "muno".into(), + "au".into(), &evm_provider, ) .await @@ -1594,7 +1504,7 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_ESCROW, // Which is wrong, so it will revert CANNOT_DEPLOY metadata: img_metadata.clone().into(), @@ -1615,16 +1525,17 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let acked_packet = ctx + let acked_packet = t + .ctx .send_and_recv_ack::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(720), cosmos_provider, ) @@ -1637,34 +1548,36 @@ async fn test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy() { ); let acked_packet = acked_packet.unwrap(); assert!( - acked_packet.tag == 0, + acked_packet.tag == alloy::primitives::U256::ZERO, "Packet is acked successfully, but it should not be. Tag: {:?}", acked_packet.tag ); } +#[tokio::test] async fn test_from_evm_to_union_batch_err_invalid_batch_instruction() { - let ctx = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, _cosmos_signer) = ctx.src.get_signer().await; + let t = init_ctx().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, _cosmos_signer) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); println!("Cosmos Address: {:?}", cosmos_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let _src_chain_id = pair.src; // let deployed_erc20 = ensure_erc20(EVM_ZKGM_BYTES.into()).await; - let deployed_erc20 = ctx + let deployed_erc20 = t + .ctx .dst - .deploy_basic_erc20(EVM_ZKGM_BYTES.into(), evm_provider.clone()) + .deploy_basic_erc20(ETH_ADDRESS_ZKGM, evm_provider.clone()) .await .expect("failed to deploy ERC20"); @@ -1701,7 +1614,7 @@ async fn test_from_evm_to_union_batch_err_invalid_batch_instruction() { operand: batch_operand.into(), }; - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); let call = ucs03_zkgm .send( @@ -1711,14 +1624,16 @@ async fn test_from_evm_to_union_batch_err_invalid_batch_instruction() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); - let (_, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; let expected_revert_code = 0x746a20f8; // ErrInvalidBatchInstruction - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, expected_revert_code, &zkgm_deployer_provider, @@ -1732,26 +1647,28 @@ async fn test_from_evm_to_union_batch_err_invalid_batch_instruction() { ); } +#[tokio::test] async fn test_from_evm_to_union_batch_err_invalid_forward_instruction() { - let ctx = init_ctx().await; - let (evm_address, evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, _cosmos_signer) = ctx.src.get_signer().await; + let t = init_ctx().await; + let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, _cosmos_signer) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); println!("EVM Address: {:?}", evm_address); println!("Cosmos Address: {:?}", cosmos_address); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_chain_id = pair.dest; let _src_chain_id: u32 = pair.src; - let deployed_erc20 = ctx + let deployed_erc20 = t + .ctx .dst - .deploy_basic_erc20(EVM_ZKGM_BYTES.into(), evm_provider.clone()) + .deploy_basic_erc20(ETH_ADDRESS_ZKGM, evm_provider.clone()) .await .expect("failed to deploy ERC20"); @@ -1790,7 +1707,7 @@ async fn test_from_evm_to_union_batch_err_invalid_forward_instruction() { operand: forward_operand.into(), }; - let ucs03_zkgm = UCS03Zkgm::new(EVM_ZKGM_BYTES.into(), evm_provider.clone()); + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); let call = ucs03_zkgm .send( @@ -1800,14 +1717,16 @@ async fn test_from_evm_to_union_batch_err_invalid_forward_instruction() { salt_bytes.into(), instruction_from_evm_to_union.clone(), ) - .clear_decoder(); + .clear_decoder() + .with_cloned_provider(); - let (_, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; let expected_revert_code = 0x1dbb3218; // ErrInvalidForwardInstruction - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_ZKGM_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_ZKGM, call, expected_revert_code, &zkgm_deployer_provider, @@ -1821,17 +1740,18 @@ async fn test_from_evm_to_union_batch_err_invalid_forward_instruction() { ); } +#[tokio::test] async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, _evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, _evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_channel_id = pair.dest; let src_channel_id = pair.src; @@ -1852,7 +1772,7 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.clone().into(), @@ -1869,7 +1789,7 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.into(), @@ -1879,15 +1799,16 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { .abi_encode_params() .into(), }; - let (zkgm_deployer_address, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; + let (zkgm_deployer_address, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; let empty_beneficiary = "".as_bytes().to_vec().into(); - ctx.dst + t.ctx + .dst .u_register_fungible_counterpart( H160::from(u_on_eth), zkgm_deployer_provider.clone(), alloy::primitives::U256::ZERO, dst_channel_id, - b"muno".to_vec().into(), + b"au".to_vec().into(), evm::u::U::FungibleCounterparty { beneficiary: empty_beneficiary, // Sending it empty to make this // test revert due to U_CounterpartyIsNotFungible and get ErrOnlyMaker @@ -1909,16 +1830,17 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let height = ctx + let height = t + .ctx .send_and_get_height::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(720), cosmos_provider, ) @@ -1948,9 +1870,10 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { timeoutTimestamp: 4294967295000000000, }]; - let proof = ctx + let proof = t + .ctx .calculate_proof::( - &ctx.dst, + &t.ctx.dst, pair.src, pair.dest, encoded_packet, @@ -1975,15 +1898,18 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { proofHeight: height, }; - let ibc = IBC::new(EVM_IBC_BYTES.into(), zkgm_deployer_provider.clone()); - - let call = ibc.recvPacket(recv_packet_msg).clear_decoder(); + let ibc = IBC::new(ETH_ADDRESS_IBC.into(), zkgm_deployer_provider.clone()); + let call = ibc + .recvPacket(recv_packet_msg) + .clear_decoder() + .with_cloned_provider(); let expected_revert_code = 0x3717ba2c; // Only maker - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_IBC_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_IBC, call, expected_revert_code, &zkgm_deployer_provider, @@ -1997,17 +1923,18 @@ async fn test_send_vault_unhappy_u_counterparty_is_not_fungible() { ); } +#[tokio::test] async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, _evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, _evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_channel_id = pair.dest; let src_channel_id = pair.src; @@ -2028,7 +1955,7 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.clone().into(), @@ -2046,7 +1973,7 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.into(), @@ -2057,14 +1984,15 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { .abi_encode_params() .into(), }; - let (zkgm_deployer_address, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; - ctx.dst + let (zkgm_deployer_address, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; + t.ctx + .dst .u_register_fungible_counterpart( H160::from(u_on_eth), zkgm_deployer_provider.clone(), alloy::primitives::U256::ZERO, dst_channel_id, - b"muno".to_vec().into(), + b"au".to_vec().into(), evm::u::U::FungibleCounterparty { beneficiary: vault_on_union.as_bytes().to_vec().into(), }, @@ -2085,16 +2013,17 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let height = ctx + let height = t + .ctx .send_and_get_height::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(720), cosmos_provider, ) @@ -2124,9 +2053,10 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { timeoutTimestamp: 4294967295000000000, }]; - let proof = ctx + let proof = t + .ctx .calculate_proof::( - &ctx.dst, + &t.ctx.dst, pair.src, pair.dest, encoded_packet, @@ -2151,15 +2081,19 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { proofHeight: height, }; - let ibc = IBC::new(EVM_IBC_BYTES.into(), zkgm_deployer_provider.clone()); + let ibc = IBC::new(ETH_ADDRESS_IBC.into(), zkgm_deployer_provider.clone()); - let call = ibc.recvPacket(recv_packet_msg).clear_decoder(); + let call = ibc + .recvPacket(recv_packet_msg) + .clear_decoder() + .with_cloned_provider(); let expected_revert_code = 0x3717ba2c; // Only maker - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_IBC_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_IBC, call, expected_revert_code, &zkgm_deployer_provider, @@ -2173,17 +2107,18 @@ async fn test_send_vault_unhappy_u_base_amount_must_cover_quote_amount() { ); } +#[tokio::test] async fn test_send_vault_unhappy_u_fool() { - let ctx = init_ctx().await; + let t = init_ctx().await; - let (evm_address, _evm_provider) = ctx.dst.get_provider().await; - let (cosmos_address, cosmos_provider) = ctx.src.get_signer().await; + let (evm_address, _evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); - ensure_channels_opened(ctx.channel_count).await; - let available_channel = ctx.get_available_channel_count().await; + ensure_channels_opened(t.ctx.channel_count).await; + let available_channel = t.ctx.get_available_channel_count().await; assert!(available_channel > 0); - let pair = ctx.get_channel().await.expect("channel available"); + let pair = t.ctx.get_channel().await.expect("channel available"); let dst_channel_id = pair.dest; let src_channel_id = pair.src; @@ -2204,7 +2139,7 @@ async fn test_send_vault_unhappy_u_fool() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.clone().into(), @@ -2222,7 +2157,7 @@ async fn test_send_vault_unhappy_u_fool() { operand: TokenOrderV2 { sender: cosmos_address_bytes.clone().into(), receiver: evm_address.to_vec().into(), - base_token: "muno".as_bytes().into(), + base_token: "au".as_bytes().into(), base_amount: "10".parse().unwrap(), kind: TOKEN_ORDER_KIND_SOLVE, metadata: metadata.into(), @@ -2233,14 +2168,15 @@ async fn test_send_vault_unhappy_u_fool() { .abi_encode_params() .into(), }; - let (zkgm_deployer_address, zkgm_deployer_provider) = ctx.dst.get_provider_privileged().await; - ctx.dst + let (zkgm_deployer_address, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; + t.ctx + .dst .u_register_fungible_counterpart( H160::from(u_on_eth), zkgm_deployer_provider.clone(), alloy::primitives::U256::ZERO, dst_channel_id, - b"muno".to_vec().into(), + b"au".to_vec().into(), evm::u::U::FungibleCounterparty { beneficiary: vault_on_union.as_bytes().to_vec().into(), }, @@ -2261,16 +2197,17 @@ async fn test_send_vault_unhappy_u_fool() { let bin_msg: Vec = Encode::::encode(&cw_msg); let funds = vec![Coin { - denom: "muno".into(), + denom: "au".into(), amount: "10".into(), }]; - let height = ctx + let height = t + .ctx .send_and_get_height::( - &ctx.src, - Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(), + &t.ctx.src, + t.union_address.zkgm.clone(), (bin_msg, funds), - &ctx.dst, + &t.ctx.dst, Duration::from_secs(720), cosmos_provider, ) @@ -2300,9 +2237,10 @@ async fn test_send_vault_unhappy_u_fool() { timeoutTimestamp: 4294967295000000000, }]; - let proof = ctx + let proof = t + .ctx .calculate_proof::( - &ctx.dst, + &t.ctx.dst, pair.src, pair.dest, encoded_packet, @@ -2327,15 +2265,19 @@ async fn test_send_vault_unhappy_u_fool() { proofHeight: height, }; - let ibc = IBC::new(EVM_IBC_BYTES.into(), zkgm_deployer_provider.clone()); + let ibc = IBC::new(ETH_ADDRESS_IBC.into(), zkgm_deployer_provider.clone()); - let call = ibc.recvPacket(recv_packet_msg).clear_decoder(); + let call = ibc + .recvPacket(recv_packet_msg) + .clear_decoder() + .with_cloned_provider(); let expected_revert_code = 0x3717ba2c; // Only maker - let recv_packet_data = ctx + let recv_packet_data = t + .ctx .send_and_expect_revert::( - &ctx.dst, - EVM_IBC_BYTES.into(), + &t.ctx.dst, + ETH_ADDRESS_IBC, call, expected_revert_code, &zkgm_deployer_provider, @@ -2348,74 +2290,3 @@ async fn test_send_vault_unhappy_u_fool() { recv_packet_data.err() ); } - -#[tokio::test] -async fn from_evm_to_union0() { - self::test_send_packet_from_evm_to_union_and_send_back_unwrap().await; -} - -#[tokio::test] -async fn from_evm_to_union_refund() { - self::test_send_packet_from_evm_to_union_get_refund().await; -} - -#[tokio::test] // Note: For this one to work; timeout plugin should be enabled on voyager. -async fn from_union_to_evm_refund() { - self::test_send_packet_from_union_to_evm_get_refund().await; -} - -#[tokio::test] -async fn from_union_to_evm0() { - self::test_send_packet_from_union_to_evm_and_send_back_unwrap().await; -} - -#[tokio::test] -async fn test_vault_works() { - self::test_send_vault_success().await; -} - -#[tokio::test] -async fn test_vault_works_with_fee() { - self::test_send_vault_success_with_fee().await; -} - -// UNHAPPY PATHS -#[tokio::test] -async fn from_evm_to_union_tokenv2_unhappy_path() { - self::test_from_evm_to_union_tokenv2_unhappy_only_maker_err().await; -} - -#[tokio::test] -async fn from_evm_to_union_tokenv2_unhappy_path4() { - self::test_from_evm_to_union_tokenv2_unhappy_err_invalid_unescrow().await; -} - -#[tokio::test] -async fn from_evm_to_union_tokenv2_unhappy_path5() { - self::test_from_evm_to_union_tokenv2_unhappy_err_cannot_deploy().await; -} - -#[tokio::test] -async fn from_evm_to_union_tokenv2_unhappy_path6() { - self::test_from_evm_to_union_batch_err_invalid_batch_instruction().await; -} - -#[tokio::test] -async fn from_evm_to_union_tokenv2_unhappy_path7() { - self::test_from_evm_to_union_batch_err_invalid_forward_instruction().await; -} - -#[tokio::test] -async fn test_send_vault_unhappy_path1() { - self::test_send_vault_unhappy_u_counterparty_is_not_fungible().await; -} - -#[tokio::test] -async fn test_send_vault_unhappy_path2() { - self::test_send_vault_unhappy_u_fool().await; -} - -#[tokio::test] -async fn test_send_vault_unhappy_path3() { - self::test_send_vault_unhappy_u_base_amount_must_cover_quote_amount().await; -} diff --git a/tools/union-test/tests/lst.rs b/tools/union-test/tests/lst.rs new file mode 100644 index 0000000000..12ec7f9c94 --- /dev/null +++ b/tools/union-test/tests/lst.rs @@ -0,0 +1,1782 @@ +use std::time::Duration; + +use alloy::{network::AnyNetwork, primitives::Address, providers::DynProvider}; +use cometbft_rpc::types::code::Code; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Coin as CwCoin, CosmosMsg, Decimal, Uint128, WasmMsg, +}; +use cw20::Cw20ExecuteMsg; +use lst::{ + msg::{AccountingStateResponse, Batch, ExecuteMsg as LstExecuteMsg}, + types::{BatchId, PendingBatch}, +}; +use protos::cosmos::base::v1beta1::Coin as ProtoCoin; +use rand::RngCore; +use serde::{de::DeserializeOwned, Serialize}; +use tracing::{info, warn}; +use ucs03_zkgm::com::TAG_ACK_SUCCESS; +use union_test::{ + cosmos::{self}, + cosmos_helpers::calculate_proxy_address, + evm::{ + self, + zkgm::{Instruction as InstructionEvm, UCS03Zkgm}, + }, + zkgm_helper, +}; +use unionlabs::primitives::U256; +use voyager_sdk::{anyhow, serde_json}; + +mod lst_common; + +use lst_common::*; + +fn make_proxy_call(funded_msgs: &[(&str, Binary, Vec)]) -> Vec { + let wasm_msgs: Vec = funded_msgs + .iter() + .map(|(contract, msg, funds)| { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: contract.to_string(), + msg: msg.clone(), + funds: funds.clone(), + }) + }) + .collect(); + + serde_json::to_vec(&wasm_msgs).expect("vec cosmosmsg json") +} + +fn make_zkgm_bond_payload_via_call( + lst_hub: &str, + mint_to: &str, + min_mint_amount: u128, + funds_denom: &str, + funds_amount: u128, +) -> Vec { + make_proxy_call(&[( + lst_hub, + to_json_binary(&LstExecuteMsg::Bond { + mint_to_address: Addr::unchecked(mint_to), + min_mint_amount: min_mint_amount.into(), + }) + .unwrap(), + vec![CwCoin { + denom: funds_denom.to_string(), + amount: funds_amount.into(), + }], + )]) +} + +fn make_zkgm_unbond_payload_via_call(cw20_token: &str, lst_hub: &str, amount: u128) -> Vec { + make_proxy_call(&[ + ( + cw20_token, + to_json_binary(&Cw20ExecuteMsg::IncreaseAllowance { + spender: lst_hub.to_string(), + amount: amount.into(), + expires: None, + }) + .unwrap(), + Vec::new(), + ), + ( + lst_hub, + to_json_binary(&LstExecuteMsg::Unbond { + amount: amount.into(), + }) + .unwrap(), + Vec::new(), + ), + ]) +} + +fn make_zkgm_withdraw_payload_via_call( + lst_hub: &str, + withdraw_to_address: Addr, + batch_id: BatchId, +) -> Vec { + make_proxy_call(&[( + lst_hub, + to_json_binary(&LstExecuteMsg::Withdraw { + withdraw_to_address, + batch_id, + }) + .unwrap(), + Vec::new(), + )]) +} + +async fn bond( + t: &LstContext, + src_channel_id: u32, + dst_channel_id: u32, + sender_on_evm: Address, + sender_evm_provider: DynProvider, + min_mint_amount: u128, + bond_amount: u128, +) { + let proxy_address_on_union = calculate_proxy_address( + &t.union_address.zkgm, + alloy::primitives::U256::ZERO, + dst_channel_id, + sender_on_evm.as_ref(), + ); + + // funding the eth address that we execute bond with, with au + eth_fund_u(t, src_channel_id, sender_on_evm.into(), 100_000, 500_000) + .await + .unwrap(); + + let mut salt_bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut salt_bytes); + + let _ = t + .ctx + .dst + .zkgmerc20_approve( + ETH_ADDRESS_U, + ETH_ADDRESS_ZKGM, + U256::from(100000000000u64), + sender_evm_provider.clone(), + ) + .await + .unwrap(); + + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), sender_evm_provider.clone()); + + let call = ucs03_zkgm + .send( + dst_channel_id, + 0u64, + 4294967295000000000u64, + salt_bytes.into(), + InstructionEvm::from(zkgm_helper::make_batch(vec![ + evm_helper::make_token_order_v2( + &t.union_address.escrow_vault, + &sender_on_evm, + &proxy_address_on_union, + alloy::primitives::U256::from(bond_amount), + ), + zkgm_helper::make_call( + sender_on_evm.to_vec().into(), + proxy_address_on_union.as_bytes().to_vec().into(), + make_zkgm_bond_payload_via_call( + t.union_address.lst_hub.as_str(), + proxy_address_on_union.as_ref(), + min_mint_amount, + "au", + bond_amount, + ) + .into(), + ), + ])), + ) + .clear_decoder() + .with_cloned_provider(); + + let (_, ack) = t + .ctx + .send_and_recv_and_ack_with_retry::( + &t.ctx.dst, + ETH_ADDRESS_ZKGM, + call, + &t.ctx.src, + 3, + Duration::from_secs(20), + Duration::from_secs(720), + &sender_evm_provider, + ) + .await + .unwrap(); + + assert_eq!(ack.tag, TAG_ACK_SUCCESS); +} + +async fn unbond( + t: &LstContext, + channel_id_on_eth: u32, + channel_id_on_union: u32, + sender_on_evm: Address, + sender_evm_provider: DynProvider, + unbond_amount: u128, +) { + let proxy_address_on_union = calculate_proxy_address( + &t.union_address.zkgm, + alloy::primitives::U256::ZERO, + channel_id_on_union, + sender_on_evm.as_ref(), + ); + + let mut salt_bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut salt_bytes); + + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), sender_evm_provider.clone()); + + let call = ucs03_zkgm + .send( + channel_id_on_eth, + 0u64, + 4294967295000000000u64, + salt_bytes.into(), + InstructionEvm::from(zkgm_helper::make_call( + sender_on_evm.to_vec().into(), + proxy_address_on_union.as_bytes().to_vec().into(), + make_zkgm_unbond_payload_via_call( + t.union_address.eu.as_str(), + t.union_address.lst_hub.as_str(), + unbond_amount, + ) + .into(), + )), + ) + .clear_decoder() + .with_cloned_provider(); + + let (_, ack) = t + .ctx + .send_and_recv_and_ack_with_retry::( + &t.ctx.dst, + ETH_ADDRESS_ZKGM, + call, + &t.ctx.src, + 6, + Duration::from_secs(10), + Duration::from_secs(720), + &sender_evm_provider, + ) + .await + .unwrap(); + + assert_eq!(ack.tag, TAG_ACK_SUCCESS); +} + +async fn withdraw( + t: &LstContext, + channel_id_on_eth: u32, + channel_id_on_union: u32, + sender_on_evm: Address, + sender_evm_provider: DynProvider, + batch_id: BatchId, +) { + let proxy_address_on_union = calculate_proxy_address( + &t.union_address.zkgm, + alloy::primitives::U256::ZERO, + channel_id_on_union, + sender_on_evm.as_ref(), + ); + + let mut salt_bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut salt_bytes); + + let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), sender_evm_provider.clone()); + + let call = ucs03_zkgm + .send( + channel_id_on_eth, + 0u64, + 4294967295000000000u64, + salt_bytes.into(), + InstructionEvm::from(zkgm_helper::make_call( + sender_on_evm.to_vec().into(), + proxy_address_on_union.as_bytes().to_vec().into(), + make_zkgm_withdraw_payload_via_call( + t.union_address.lst_hub.as_str(), + proxy_address_on_union.clone(), + batch_id, + ) + .into(), + )), + ) + .clear_decoder() + .with_cloned_provider(); + + let (_, ack) = t + .ctx + .send_and_recv_and_ack_with_retry::( + &t.ctx.dst, + ETH_ADDRESS_ZKGM, + call, + &t.ctx.src, + 6, + Duration::from_secs(10), + Duration::from_secs(720), + &sender_evm_provider, + ) + .await + .unwrap(); + + assert_eq!(ack.tag, TAG_ACK_SUCCESS); +} + +async fn query_lst_hub( + t: &LstContext, + query: Q, +) -> anyhow::Result { + loop { + match t + .ctx + .src + .query_wasm_smart::<_, Res>(t.union_address.lst_hub.clone(), query.clone()) + .await + { + Ok(state) => break Ok(state), + Err(_) => { + warn!("the query didn't work for some reason, will retry in a sec."); + tokio::time::sleep(Duration::from_secs(1)).await; + } + } + } +} + +async fn get_batch_by_id(t: &LstContext, id: BatchId) -> anyhow::Result { + Ok( + query_lst_hub::<_, Option>(t, lst::msg::QueryMsg::Batch { batch_id: id }) + .await? + .unwrap(), + ) +} + +async fn get_pending_batch(t: &LstContext) -> anyhow::Result { + query_lst_hub(t, lst::msg::QueryMsg::PendingBatch {}).await +} + +async fn get_accounting_state(t: &LstContext) -> anyhow::Result { + query_lst_hub(t, lst::msg::QueryMsg::AccountingState {}).await +} + +#[tokio::test] +async fn test_bond_success() { + run_test_in_queue("bond", async |t, mut shared_data| { + let dst_channel_id = 1; + let src_channel_id = 1; + + // setting "au" as the fungible counterparty + eth_set_fungible_counterparty( + &t.ctx.dst, + src_channel_id, + b"au", + t.union_address.escrow_vault.as_bytes(), + ) + .await + .unwrap(); + + let bonds = [(120, 200), (400, 700)]; + + let evm_signers = [ + t.ctx.dst.get_provider().await, + t.ctx.dst.get_provider().await, + ]; + + shared_data.stakers = evm_signers.to_vec(); + + for (bond_amount, evm_signer) in bonds.into_iter().zip(evm_signers) { + let (evm_address, evm_provider): (Address, DynProvider) = evm_signer; + let (min_mint_amount, bond_amount) = bond_amount; + + let proxy_address = calculate_proxy_address( + &t.union_address.zkgm, + alloy::primitives::U256::ZERO, + dst_channel_id, + evm_address.as_ref(), + ); + + let eu_balance = || async { + t.ctx + .src + .get_cw20_balance(&proxy_address, &t.union_address.eu) + .await + .unwrap() + }; + + let eu_balance_before = eu_balance().await; + + let state = get_accounting_state(&t).await.unwrap(); + + bond( + &t, + src_channel_id, + dst_channel_id, + evm_address, + evm_provider, + min_mint_amount, + bond_amount, + ) + .await; + + let new_state = get_accounting_state(&t).await.unwrap(); + + // We expect to get the same amount of `total_asset` change. We don't have to check the + // actual value since it's the unit test's job. + let eu_balance_after_stake = eu_balance().await; + assert_eq!( + eu_balance_after_stake - eu_balance_before, + new_state.total_shares.u128() - state.total_shares.u128() + ); + } + + // we are making sure that the 2 mins ubonding time passes, while making sure that the + // rate continues to go down since more and more rewards will be earned with new blocks. + let mut prev_rate = Decimal::MAX; + for i in 1..8 { + let new_rate = get_accounting_state(&t).await.unwrap().purchase_rate; + + tokio::time::sleep(Duration::from_secs(15)).await; + + info!("checking the rate ({i}).. new_rate: {new_rate} prev_rate: {prev_rate}"); + + assert!(new_rate < prev_rate); + + prev_rate = new_rate; + } + + shared_data + }) + .await; +} + +#[tokio::test] +async fn test_unbond_success() { + run_test_in_queue("unbond", async |t, shared_data| { + let dst_channel_id = 1; + let src_channel_id = 1; + + let unbond_amounts = [30, 40]; + + let mut total_unbond_amount = 0; + + for (amount, staker) in unbond_amounts.into_iter().zip(&shared_data.stakers) { + unbond( + &t, + dst_channel_id, + src_channel_id, + staker.0, + staker.1.clone(), + amount, + ) + .await; + + // we are unbonding twice so that the `is_new_request` can be triggered + // and we test that case as well + unbond( + &t, + dst_channel_id, + src_channel_id, + staker.0, + staker.1.clone(), + amount, + ) + .await; + + total_unbond_amount += amount * 2; + } + + let (_, signer) = t.ctx.src.get_signer().await; + + let state = get_accounting_state(&t).await.unwrap(); + let mut worked = false; + for _ in 1..6 { + if let Ok(outcome) = t + .ctx + .src + .send_cosmwasm_transaction( + t.union_address.lst_hub.clone(), + ( + to_json_binary(&LstExecuteMsg::SubmitBatch {}) + .unwrap() + .into(), + vec![], + ), + signer, + ) + .await + .unwrap() + { + assert_eq!(outcome.tx_result.code, Code::Ok); + worked = true; + break; + } else { + info!("waiting 20 seconds before submitting batch"); + tokio::time::sleep(Duration::from_secs(20)).await; + } + } + assert!(worked); + let new_state = get_accounting_state(&t).await.unwrap(); + + // We burned exactly the `total_unbond_amount` of eU's + assert_eq!( + total_unbond_amount, + state.total_shares.u128() - new_state.total_shares.u128() + ); + + shared_data + }) + .await; +} + +#[tokio::test] +async fn test_withdraw_success() { + run_test_in_queue("withdraw", async |t, shared_data| { + // Waiting for at least the unbond amount before receiving the tokens + tokio::time::sleep(Duration::from_secs(120)).await; + + let dst_channel_id = 1; + let src_channel_id = 1; + + let unbond_amounts = [60u128, 80]; + + let (_, signer) = t.ctx.src.get_signer().await; + + let pending_batch = get_pending_batch(&t).await.unwrap(); + let submitted_batch_id = BatchId::from_raw(pending_batch.batch_id.get().get() - 1).unwrap(); + + let Batch::Submitted(submitted_batch) = + get_batch_by_id(&t, submitted_batch_id).await.unwrap() + else { + panic!("expected submitted batch"); + }; + + let fund_amount = submitted_batch.expected_native_unstaked; + + let outcome = t + .ctx + .src + .send_cosmwasm_transaction( + t.union_address.lst_hub.clone(), + ( + to_json_binary(&LstExecuteMsg::ReceiveUnstakedTokens { + batch_id: submitted_batch_id, + }) + .unwrap() + .into(), + vec![ProtoCoin { + denom: "au".to_string(), + amount: fund_amount.to_string(), + }], + ), + signer, + ) + .await + .unwrap() + .unwrap(); + + assert_eq!(outcome.tx_result.code, Code::Ok); + + // Now the batch turns into `Received` since we actually received it + let Batch::Received(received_batch) = + get_batch_by_id(&t, submitted_batch_id).await.unwrap() + else { + panic!("expected received batch"); + }; + + for (amount, staker) in unbond_amounts.into_iter().zip(&shared_data.stakers) { + let proxy_address = calculate_proxy_address( + &t.union_address.zkgm, + alloy::primitives::U256::ZERO, + src_channel_id, + staker.0.as_ref(), + ); + let u_balance_before = t + .ctx + .src + .native_balance(proxy_address.clone(), "au") + .await + .unwrap(); + + withdraw( + &t, + dst_channel_id, + src_channel_id, + staker.0, + staker.1.clone(), + submitted_batch_id, + ) + .await; + + let u_balance_after = t.ctx.src.native_balance(proxy_address, "au").await.unwrap(); + + assert_eq!( + u_balance_after - u_balance_before, + Uint128::new(received_batch.received_native_unstaked) + .multiply_ratio(amount, 140u128) + .u128() + ); + } + + shared_data + }) + .await; +} + +// #[tokio::test] +// async fn test_escher_lst_unhappy_less_money_than_required() { +// let t = init_ctx().await; + +// let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; +// let (cosmos_address, _) = t.ctx.src.get_signer().await; + +// // ensure_channels_opened(t.ctx.channel_count).await; +// // let available_channel = t.ctx.get_available_channel_count().await; +// // assert!(available_channel > 0); +// // let pair = t.ctx.get_channel().await.expect("channel available"); + +// let dst_channel_id = 1; +// let src_channel_id = 1; + +// let vault_on_union = "union1skg5244hpkad603zz77kdekzw6ffgpfrde3ldk8rpdz06n62k4hqct0w4j"; + +// let u_on_eth = hex_literal::hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836"); + +// eth_set_fungible_counterparty( +// &t.ctx.dst, +// dst_channel_id, +// b"au, +// t.union_address.escrow_vault.as_bytes(), +// ) +// .await +// .unwrap(); + +// eth_fund_u( +// &t, +// src_channel_id, +// cosmos_address.to_string(), +// evm_address.into(), +// 5_000, +// 10_000, +// ) +// .await +// .unwrap(); + +// let mut salt_bytes = [0u8; 32]; +// rand::rng().fill_bytes(&mut salt_bytes); + +// let new_u_balance = t +// .ctx +// .dst +// .zkgmerc20_balance_of( +// H160::from(u_on_eth), +// evm_address.into(), +// evm_provider.clone(), +// ) +// .await +// .unwrap(); + +// let new_vault_balance = t +// .ctx +// .src +// .native_balance(Bech32::from_str(vault_on_union).unwrap(), "au") +// .await +// .unwrap(); + +// // both balances are updated +// assert!(new_u_balance > U256::ZERO); +// assert!(new_vault_balance > 0); + +// println!("new_u_balance: {}", new_u_balance); + +// let lst_hub = "union1fdg764zzxwvwyqkx3fuj0236l9ddh5xmutgvj2mv9cduffy82z9sp62ygc"; +// // let lst = "union1jansh23v7teaznyljq6ss4vx6eym8yrz0dsjchap4u7j3etx94vqhmcwn5"; + +// let addr: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); +// let canon = cosmwasm_std::CanonicalAddr::from(addr.data().as_ref()); + +// let zkgm_proxy_canon = instantiate2_address( +// // Checksum of the base contract +// &hex_literal::hex!("ec827349ed4c1fec5a9c3462ff7c979d4c40e7aa43b16ed34469d04ff835f2a1"), +// &canon, +// proxy_account_salt_for_tests( +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// evm_address.as_slice(), +// ) +// .get() +// .as_slice(), +// ) +// .unwrap(); + +// let hrp = Bech32::>::from_str(UNION_ZKGM_ADDRESS) +// .unwrap() +// .hrp() +// .to_string(); + +// // 3. Build a Bech32 address with same HRP +// let zkgm_proxy_calculated = Bech32::>::new( +// hrp, +// FixedBytes::<32>::try_from(zkgm_proxy_canon.as_slice()).unwrap(), +// ); +// println!("ZKGM Proxy: {}", zkgm_proxy_calculated); + +// // let zkgm_proxy = zkgm_proxy_calculated.to_string().as_str(); + +// // let bond_message: Bytes = json!({ +// // "bond": { +// // "mint_to_address": "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// // "min_mint_amount": "3" +// // } +// // }) +// // .to_string() +// // .as_bytes() +// // .into(); + +// // let zkgm_message = json!({ +// // "contract": lst_hub, +// // "msg": bond_message.to_string(), +// // "funds": [{ "denom": "au", "amount": "3" }], +// // "call_action": "call_proxy" +// // }) +// // .to_string(); + +// let zkgm_msg_json = make_zkgm_call_payload( +// lst_hub, +// "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// 1000000u128.into(), +// "au", +// 1000000, +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated.clone(), "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); + +// let addr_str = zkgm_proxy_calculated.to_string(); +// let receiver = addr_str.into_bytes().into(); + +// let instruction_from_evm_to_union = InstructionEvm { +// version: INSTR_VERSION_0, +// opcode: OP_BATCH, +// operand: Batch { +// instructions: vec![ +// Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: evm_address.to_vec().into(), +// receiver, +// base_token: u_on_eth.to_vec().into(), +// // giving 150 but expecting 1000000 so it will fail. +// base_amount: "150".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: SolverMetadata { +// solverAddress: vault_on_union.as_bytes().into(), +// metadata: Default::default(), +// } +// .abi_encode_params() +// .into(), +// quote_token: "au".as_bytes().into(), +// // giving 150 but expecting 1000000 so it will fail. +// quote_amount: "150".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }, +// Instruction { +// version: INSTR_VERSION_0, +// opcode: OP_CALL, +// operand: Call { +// sender: evm_address.to_vec().into(), +// eureka: false, +// contract_address: zkgm_proxy_calculated +// .to_string() +// .as_bytes() +// .to_vec() +// .into(), +// contract_calldata: zkgm_msg_json.as_bytes().to_vec().into(), +// } +// .abi_encode_params() +// .into(), +// }, +// ], +// } +// .abi_encode_params() +// .into(), +// }; + +// let approve_tx_hash = t +// .ctx +// .dst +// .zkgmerc20_approve( +// u_on_eth.into(), +// ETH_ADDRESS_ZKGM.into(), +// U256::from(100000000000u64), +// evm_provider.clone(), +// ) +// .await; + +// assert!( +// approve_tx_hash.is_ok(), +// "Failed to send approve transaction: {:?}, from_account: {:?}", +// approve_tx_hash.err(), +// evm_address +// ); + +// let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); + +// let call = ucs03_zkgm +// .send( +// dst_channel_id, +// 0u64, +// 4294967295000000000u64, +// salt_bytes.into(), +// instruction_from_evm_to_union.clone(), +// ) +// .clear_decoder() +// .with_cloned_provider(); + +// let acked_packet = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.dst, +// ETH_ADDRESS_ZKGM.into(), +// call, +// &t.ctx.src, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// &evm_provider, +// ) +// .await; + +// let acked_packet = acked_packet.unwrap(); +// assert!( +// acked_packet.tag == 0, +// "Packet is acked successfully, but it should not be. Tag: {:?}", +// acked_packet.tag +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated, "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); +// } + +// #[tokio::test] +// async fn test_escher_lst_unhappy_wrong_denom() { +// let t = init_ctx().await; + +// let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; +// let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; +// let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); + +// // ensure_channels_opened(t.ctx.channel_count).await; +// // let available_channel = t.ctx.get_available_channel_count().await; +// // assert!(available_channel > 0); +// // let pair = t.ctx.get_channel().await.expect("channel available"); + +// let dst_channel_id = 1; +// let src_channel_id = 1; + +// let vault_on_union = "union1skg5244hpkad603zz77kdekzw6ffgpfrde3ldk8rpdz06n62k4hqct0w4j"; + +// let u_on_eth = hex_literal::hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836"); + +// let metadata = SolverMetadata { +// solverAddress: u_on_eth.to_vec().into(), +// metadata: Default::default(), +// } +// .abi_encode_params(); + +// let instruction_cosmos = Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: cosmos_address_bytes.clone().into(), +// receiver: evm_address.to_vec().into(), +// base_token: "au".as_bytes().into(), +// base_amount: "100000".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: metadata.into(), +// quote_token: u_on_eth.to_vec().into(), +// quote_amount: "100000".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }; + +// let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; +// println!("registering u counterpart"); +// t.ctx +// .dst +// .u_register_fungible_counterpart( +// H160::from(u_on_eth), +// zkgm_deployer_provider.clone(), +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// b"au.to_vec().into(), +// evm::u::U::FungibleCounterparty { +// beneficiary: vault_on_union.as_bytes().to_vec().into(), +// }, +// ) +// .await +// .unwrap(); +// println!("u counterpart is registered"); + +// let mut salt_bytes = [0u8; 32]; +// rand::rng().fill_bytes(&mut salt_bytes); + +// let cw_msg = ucs03_zkgm::msg::ExecuteMsg::Send { +// channel_id: src_channel_id.try_into().unwrap(), +// timeout_height: 0u64.into(), +// timeout_timestamp: voyager_sdk::primitives::Timestamp::from_secs(u32::MAX.into()), +// salt: salt_bytes.into(), +// instruction: instruction_cosmos.abi_encode_params().into(), +// }; +// let bin_msg: Vec = Encode::::encode(&cw_msg); + +// let funds = vec![Coin { +// denom: "au".into(), +// amount: "100000".into(), +// }]; + +// let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); + +// let ack_packet_data = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.src, +// contract, +// (bin_msg, funds), +// &t.ctx.dst, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// cosmos_provider, +// ) +// .await; + +// assert!( +// ack_packet_data.is_ok(), +// "Failed to send and ack packet: {:?}", +// ack_packet_data.err() +// ); + +// let new_u_balance = t +// .ctx +// .dst +// .zkgmerc20_balance_of( +// H160::from(u_on_eth), +// evm_address.into(), +// evm_provider.clone(), +// ) +// .await +// .unwrap(); + +// let new_vault_balance = t +// .ctx +// .src +// .native_balance(Bech32::from_str(vault_on_union).unwrap(), "au") +// .await +// .unwrap(); + +// // both balances are updated +// assert!(new_u_balance > U256::ZERO); +// assert!(new_vault_balance > 0); + +// println!("new_u_balance: {}", new_u_balance); + +// let lst_hub = "union1fdg764zzxwvwyqkx3fuj0236l9ddh5xmutgvj2mv9cduffy82z9sp62ygc"; +// // let lst = "union1jansh23v7teaznyljq6ss4vx6eym8yrz0dsjchap4u7j3etx94vqhmcwn5"; + +// let addr: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); +// let canon = cosmwasm_std::CanonicalAddr::from(addr.data().as_ref()); + +// let zkgm_proxy_canon = instantiate2_address( +// // Checksum of the base contract +// &hex_literal::hex!("ec827349ed4c1fec5a9c3462ff7c979d4c40e7aa43b16ed34469d04ff835f2a1"), +// &canon, +// proxy_account_salt_for_tests( +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// evm_address.as_slice(), +// ) +// .get() +// .as_slice(), +// ) +// .unwrap(); + +// let hrp = Bech32::>::from_str(UNION_ZKGM_ADDRESS) +// .unwrap() +// .hrp() +// .to_string(); + +// // 3. Build a Bech32 address with same HRP +// let zkgm_proxy_calculated = Bech32::>::new( +// hrp, +// FixedBytes::<32>::try_from(zkgm_proxy_canon.as_slice()).unwrap(), +// ); +// println!("ZKGM Proxy: {}", zkgm_proxy_calculated); + +// // let zkgm_proxy = zkgm_proxy_calculated.to_string().as_str(); + +// // let bond_message: Bytes = json!({ +// // "bond": { +// // "mint_to_address": "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// // "min_mint_amount": "3" +// // } +// // }) +// // .to_string() +// // .as_bytes() +// // .into(); + +// // let zkgm_message = json!({ +// // "contract": lst_hub, +// // "msg": bond_message.to_string(), +// // "funds": [{ "denom": "au", "amount": "3" }], +// // "call_action": "call_proxy" +// // }) +// // .to_string(); + +// let zkgm_msg_json = make_zkgm_call_payload( +// lst_hub, +// "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// 10u128.into(), +// "muan", // wrong denom to make it fail +// 10, +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated.clone(), "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); + +// let addr_str = zkgm_proxy_calculated.to_string(); +// let receiver = addr_str.into_bytes().into(); + +// let instruction_from_evm_to_union = InstructionEvm { +// version: INSTR_VERSION_0, +// opcode: OP_BATCH, +// operand: Batch { +// instructions: vec![ +// Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: evm_address.to_vec().into(), +// receiver, +// base_token: u_on_eth.to_vec().into(), +// base_amount: "150".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: SolverMetadata { +// solverAddress: vault_on_union.as_bytes().into(), +// metadata: Default::default(), +// } +// .abi_encode_params() +// .into(), +// quote_token: "au".as_bytes().into(), +// quote_amount: "150".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }, +// Instruction { +// version: INSTR_VERSION_0, +// opcode: OP_CALL, +// operand: Call { +// sender: evm_address.to_vec().into(), +// eureka: false, +// contract_address: zkgm_proxy_calculated +// .to_string() +// .as_bytes() +// .to_vec() +// .into(), +// contract_calldata: zkgm_msg_json.as_bytes().to_vec().into(), +// } +// .abi_encode_params() +// .into(), +// }, +// ], +// } +// .abi_encode_params() +// .into(), +// }; + +// let approve_tx_hash = t +// .ctx +// .dst +// .zkgmerc20_approve( +// u_on_eth.into(), +// ETH_ADDRESS_ZKGM.into(), +// U256::from(100000000000u64), +// evm_provider.clone(), +// ) +// .await; + +// assert!( +// approve_tx_hash.is_ok(), +// "Failed to send approve transaction: {:?}, from_account: {:?}", +// approve_tx_hash.err(), +// evm_address +// ); + +// let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); + +// let call = ucs03_zkgm +// .send( +// dst_channel_id, +// 0u64, +// 4294967295000000000u64, +// salt_bytes.into(), +// instruction_from_evm_to_union.clone(), +// ) +// .clear_decoder() +// .with_cloned_provider(); + +// let acked_packet = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.dst, +// ETH_ADDRESS_ZKGM.into(), +// call, +// &t.ctx.src, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// &evm_provider, +// ) +// .await; + +// let acked_packet = acked_packet.unwrap(); +// assert!( +// acked_packet.tag == 0, +// "Packet is acked successfully, but it should not be. Tag: {:?}", +// acked_packet.tag +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated, "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); +// } + +// #[tokio::test] +// async fn test_escher_lst_unhappy_under_minimum_ls_amount() { +// let t = init_ctx().await; + +// let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; +// let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; +// let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); + +// // ensure_channels_opened(t.ctx.channel_count).await; +// // let available_channel = t.ctx.get_available_channel_count().await; +// // assert!(available_channel > 0); +// // let pair = ctx.get_channel().await.expect("channel available"); + +// let dst_channel_id = 1; +// let src_channel_id = 1; + +// let vault_on_union = "union1skg5244hpkad603zz77kdekzw6ffgpfrde3ldk8rpdz06n62k4hqct0w4j"; + +// let u_on_eth = hex_literal::hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836"); + +// let metadata = SolverMetadata { +// solverAddress: u_on_eth.to_vec().into(), +// metadata: Default::default(), +// } +// .abi_encode_params(); + +// let instruction_cosmos = Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: cosmos_address_bytes.clone().into(), +// receiver: evm_address.to_vec().into(), +// base_token: "au".as_bytes().into(), +// base_amount: "100000".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: metadata.into(), +// quote_token: u_on_eth.to_vec().into(), +// quote_amount: "100000".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }; + +// let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; +// println!("registering u counterpart"); +// t.ctx +// .dst +// .u_register_fungible_counterpart( +// H160::from(u_on_eth), +// zkgm_deployer_provider.clone(), +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// b"au.to_vec().into(), +// evm::u::U::FungibleCounterparty { +// beneficiary: vault_on_union.as_bytes().to_vec().into(), +// }, +// ) +// .await +// .unwrap(); +// println!("u counterpart is registered"); + +// let mut salt_bytes = [0u8; 32]; +// rand::rng().fill_bytes(&mut salt_bytes); + +// let cw_msg = ucs03_zkgm::msg::ExecuteMsg::Send { +// channel_id: src_channel_id.try_into().unwrap(), +// timeout_height: 0u64.into(), +// timeout_timestamp: voyager_sdk::primitives::Timestamp::from_secs(u32::MAX.into()), +// salt: salt_bytes.into(), +// instruction: instruction_cosmos.abi_encode_params().into(), +// }; +// let bin_msg: Vec = Encode::::encode(&cw_msg); + +// let funds = vec![Coin { +// denom: "au".into(), +// amount: "100000".into(), +// }]; + +// let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); + +// let ack_packet_data = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.src, +// contract, +// (bin_msg, funds), +// &t.ctx.dst, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// cosmos_provider, +// ) +// .await; + +// assert!( +// ack_packet_data.is_ok(), +// "Failed to send and ack packet: {:?}", +// ack_packet_data.err() +// ); + +// let new_u_balance = t +// .ctx +// .dst +// .zkgmerc20_balance_of( +// H160::from(u_on_eth), +// evm_address.into(), +// evm_provider.clone(), +// ) +// .await +// .unwrap(); + +// let new_vault_balance = t +// .ctx +// .src +// .native_balance(Bech32::from_str(vault_on_union).unwrap(), "au") +// .await +// .unwrap(); + +// // both balances are updated +// assert!(new_u_balance > U256::ZERO); +// assert!(new_vault_balance > 0); + +// println!("new_u_balance: {}", new_u_balance); + +// let lst_hub = "union1fdg764zzxwvwyqkx3fuj0236l9ddh5xmutgvj2mv9cduffy82z9sp62ygc"; +// // let lst = "union1jansh23v7teaznyljq6ss4vx6eym8yrz0dsjchap4u7j3etx94vqhmcwn5"; + +// let addr: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); +// let canon = cosmwasm_std::CanonicalAddr::from(addr.data().as_ref()); + +// let zkgm_proxy_canon = instantiate2_address( +// // Checksum of the base contract +// &hex_literal::hex!("ec827349ed4c1fec5a9c3462ff7c979d4c40e7aa43b16ed34469d04ff835f2a1"), +// &canon, +// proxy_account_salt_for_tests( +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// evm_address.as_slice(), +// ) +// .get() +// .as_slice(), +// ) +// .unwrap(); + +// let hrp = Bech32::>::from_str(UNION_ZKGM_ADDRESS) +// .unwrap() +// .hrp() +// .to_string(); + +// // 3. Build a Bech32 address with same HRP +// let zkgm_proxy_calculated = Bech32::>::new( +// hrp, +// FixedBytes::<32>::try_from(zkgm_proxy_canon.as_slice()).unwrap(), +// ); +// println!("ZKGM Proxy: {}", zkgm_proxy_calculated); + +// // let zkgm_proxy = zkgm_proxy_calculated.to_string().as_str(); + +// // let bond_message: Bytes = json!({ +// // "bond": { +// // "mint_to_address": "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// // "min_mint_amount": "3" +// // } +// // }) +// // .to_string() +// // .as_bytes() +// // .into(); + +// // let zkgm_message = json!({ +// // "contract": lst_hub, +// // "msg": bond_message.to_string(), +// // "funds": [{ "denom": "au", "amount": "3" }], +// // "call_action": "call_proxy" +// // }) +// // .to_string(); + +// let zkgm_msg_json = make_zkgm_call_payload( +// lst_hub, +// "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// 0u128.into(), +// "au", +// 0, // under minimum amount to make it fail +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated.clone(), "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); + +// let addr_str = zkgm_proxy_calculated.to_string(); +// let receiver = addr_str.into_bytes().into(); + +// let instruction_from_evm_to_union = InstructionEvm { +// version: INSTR_VERSION_0, +// opcode: OP_BATCH, +// operand: Batch { +// instructions: vec![ +// Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: evm_address.to_vec().into(), +// receiver, +// base_token: u_on_eth.to_vec().into(), +// base_amount: "150".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: SolverMetadata { +// solverAddress: vault_on_union.as_bytes().into(), +// metadata: Default::default(), +// } +// .abi_encode_params() +// .into(), +// quote_token: "au".as_bytes().into(), +// quote_amount: "150".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }, +// Instruction { +// version: INSTR_VERSION_0, +// opcode: OP_CALL, +// operand: Call { +// sender: evm_address.to_vec().into(), +// eureka: false, +// contract_address: zkgm_proxy_calculated +// .to_string() +// .as_bytes() +// .to_vec() +// .into(), +// contract_calldata: zkgm_msg_json.as_bytes().to_vec().into(), +// } +// .abi_encode_params() +// .into(), +// }, +// ], +// } +// .abi_encode_params() +// .into(), +// }; + +// let approve_tx_hash = t +// .ctx +// .dst +// .zkgmerc20_approve( +// u_on_eth.into(), +// ETH_ADDRESS_ZKGM.into(), +// U256::from(100000000000u64), +// evm_provider.clone(), +// ) +// .await; + +// assert!( +// approve_tx_hash.is_ok(), +// "Failed to send approve transaction: {:?}, from_account: {:?}", +// approve_tx_hash.err(), +// evm_address +// ); + +// let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); + +// let call = ucs03_zkgm +// .send( +// dst_channel_id, +// 0u64, +// 4294967295000000000u64, +// salt_bytes.into(), +// instruction_from_evm_to_union.clone(), +// ) +// .clear_decoder() +// .with_cloned_provider(); + +// let acked_packet = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.dst, +// ETH_ADDRESS_ZKGM.into(), +// call, +// &t.ctx.src, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// &evm_provider, +// ) +// .await; + +// let acked_packet = acked_packet.unwrap(); +// assert!( +// acked_packet.tag == 0, +// "Packet is acked successfully, but it should not be. Tag: {:?}", +// acked_packet.tag +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated, "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); +// } + +// #[tokio::test] +// async fn test_escher_lst_unhappy_no_funds() { +// let t = init_ctx().await; + +// let (evm_address, evm_provider) = t.ctx.dst.get_provider().await; +// let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; +// let cosmos_address_bytes = cosmos_address.to_string().into_bytes(); + +// // ensure_channels_opened(t.ctx.channel_count).await; +// // let available_channel = t.ctx.get_available_channel_count().await; +// // assert!(available_channel > 0); +// // let pair = t.ctx.get_channel().await.expect("channel available"); + +// let dst_channel_id = 1; +// let src_channel_id = 1; + +// let vault_on_union = "union1skg5244hpkad603zz77kdekzw6ffgpfrde3ldk8rpdz06n62k4hqct0w4j"; + +// let u_on_eth = hex_literal::hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836"); + +// let metadata = SolverMetadata { +// solverAddress: u_on_eth.to_vec().into(), +// metadata: Default::default(), +// } +// .abi_encode_params(); + +// let instruction_cosmos = Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: cosmos_address_bytes.clone().into(), +// receiver: evm_address.to_vec().into(), +// base_token: "au".as_bytes().into(), +// base_amount: "100000".parse().unwrap(), +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: metadata.into(), +// quote_token: u_on_eth.to_vec().into(), +// quote_amount: "100000".parse().unwrap(), +// } +// .abi_encode_params() +// .into(), +// }; + +// let (_, zkgm_deployer_provider) = t.ctx.dst.get_provider_privileged().await; +// println!("registering u counterpart"); +// t.ctx +// .dst +// .u_register_fungible_counterpart( +// H160::from(u_on_eth), +// zkgm_deployer_provider.clone(), +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// b"au.to_vec().into(), +// evm::u::U::FungibleCounterparty { +// beneficiary: vault_on_union.as_bytes().to_vec().into(), +// }, +// ) +// .await +// .unwrap(); +// println!("u counterpart is registered"); + +// let mut salt_bytes = [0u8; 32]; +// rand::rng().fill_bytes(&mut salt_bytes); + +// let cw_msg = ucs03_zkgm::msg::ExecuteMsg::Send { +// channel_id: src_channel_id.try_into().unwrap(), +// timeout_height: 0u64.into(), +// timeout_timestamp: voyager_sdk::primitives::Timestamp::from_secs(u32::MAX.into()), +// salt: salt_bytes.into(), +// instruction: instruction_cosmos.abi_encode_params().into(), +// }; +// let bin_msg: Vec = Encode::::encode(&cw_msg); + +// let funds = vec![Coin { +// denom: "au".into(), +// amount: "100000".into(), +// }]; + +// let contract: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); + +// let ack_packet_data = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.src, +// contract, +// (bin_msg, funds), +// &t.ctx.dst, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// cosmos_provider, +// ) +// .await; + +// assert!( +// ack_packet_data.is_ok(), +// "Failed to send and ack packet: {:?}", +// ack_packet_data.err() +// ); + +// let new_u_balance = t +// .ctx +// .dst +// .zkgmerc20_balance_of( +// H160::from(u_on_eth), +// evm_address.into(), +// evm_provider.clone(), +// ) +// .await +// .unwrap(); + +// let new_vault_balance = t +// .ctx +// .src +// .native_balance(Bech32::from_str(vault_on_union).unwrap(), "au") +// .await +// .unwrap(); + +// // both balances are updated +// assert!(new_u_balance > U256::ZERO); +// assert!(new_vault_balance > 0); + +// println!("new_u_balance: {}", new_u_balance); + +// let lst_hub = "union1fdg764zzxwvwyqkx3fuj0236l9ddh5xmutgvj2mv9cduffy82z9sp62ygc"; +// // let lst = "union1jansh23v7teaznyljq6ss4vx6eym8yrz0dsjchap4u7j3etx94vqhmcwn5"; + +// let addr: Bech32> = Bech32::from_str(UNION_ZKGM_ADDRESS).unwrap(); +// let canon = cosmwasm_std::CanonicalAddr::from(addr.data().as_ref()); + +// let zkgm_proxy_canon = instantiate2_address( +// // Checksum of the base contract +// &hex_literal::hex!("ec827349ed4c1fec5a9c3462ff7c979d4c40e7aa43b16ed34469d04ff835f2a1"), +// &canon, +// proxy_account_salt_for_tests( +// alloy::primitives::U256::ZERO, +// dst_channel_id, +// evm_address.as_slice(), +// ) +// .get() +// .as_slice(), +// ) +// .unwrap(); + +// let hrp = Bech32::>::from_str(UNION_ZKGM_ADDRESS) +// .unwrap() +// .hrp() +// .to_string(); + +// // 3. Build a Bech32 address with same HRP +// let zkgm_proxy_calculated = Bech32::>::new( +// hrp, +// FixedBytes::<32>::try_from(zkgm_proxy_canon.as_slice()).unwrap(), +// ); +// println!("ZKGM Proxy: {}", zkgm_proxy_calculated); + +// // let zkgm_proxy = zkgm_proxy_calculated.to_string().as_str(); + +// // let bond_message: Bytes = json!({ +// // "bond": { +// // "mint_to_address": "union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2", +// // "min_mint_amount": "3" +// // } +// // }) +// // .to_string() +// // .as_bytes() +// // .into(); + +// // let zkgm_message = json!({ +// // "contract": lst_hub, +// // "msg": bond_message.to_string(), +// // "funds": [{ "denom": "au", "amount": "3" }], +// // "call_action": "call_proxy" +// // }) +// // .to_string(); + +// let bond = LstExecuteMsg::Bond { +// mint_to_address: Addr::unchecked("union1jk9psyhvgkrt2cumz8eytll2244m2nnz4yt2g2"), +// min_mint_amount: 0u128.into(), +// }; + +// let wasm_exec: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { +// contract_addr: lst_hub.to_string(), +// msg: to_json_binary(&bond).unwrap(), +// funds: vec![], // no funds array so it will fail. +// }); + +// let zkgm_msg_json = +// voyager_sdk::serde_json::to_string(&vec![wasm_exec]).expect("vec cosmosmsg json"); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated.clone(), "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); + +// let addr_str = zkgm_proxy_calculated.to_string(); +// let receiver = addr_str.into_bytes().into(); + +// let instruction_from_evm_to_union = InstructionEvm { +// version: INSTR_VERSION_0, +// opcode: OP_BATCH, +// operand: Batch { +// instructions: vec![ +// Instruction { +// version: INSTR_VERSION_2, +// opcode: OP_TOKEN_ORDER, +// operand: TokenOrderV2 { +// sender: evm_address.to_vec().into(), +// receiver, +// base_token: u_on_eth.to_vec().into(), +// base_amount: "150".parse().unwrap(), //giving 150 but expecting 1000000 so it will fail. +// kind: TOKEN_ORDER_KIND_SOLVE, +// metadata: SolverMetadata { +// solverAddress: vault_on_union.as_bytes().into(), +// metadata: Default::default(), +// } +// .abi_encode_params() +// .into(), +// quote_token: "au".as_bytes().into(), +// quote_amount: "150".parse().unwrap(), //giving 150 but expecting 1000000 so it will fail. +// } +// .abi_encode_params() +// .into(), +// }, +// Instruction { +// version: INSTR_VERSION_0, +// opcode: OP_CALL, +// operand: Call { +// sender: evm_address.to_vec().into(), +// eureka: false, +// contract_address: zkgm_proxy_calculated +// .to_string() +// .as_bytes() +// .to_vec() +// .into(), +// contract_calldata: zkgm_msg_json.as_bytes().to_vec().into(), +// } +// .abi_encode_params() +// .into(), +// }, +// ], +// } +// .abi_encode_params() +// .into(), +// }; + +// let approve_tx_hash = t +// .ctx +// .dst +// .zkgmerc20_approve( +// u_on_eth.into(), +// ETH_ADDRESS_ZKGM.into(), +// U256::from(100000000000u64), +// evm_provider.clone(), +// ) +// .await; + +// assert!( +// approve_tx_hash.is_ok(), +// "Failed to send approve transaction: {:?}, from_account: {:?}", +// approve_tx_hash.err(), +// evm_address +// ); + +// let ucs03_zkgm = UCS03Zkgm::new(ETH_ADDRESS_ZKGM.into(), evm_provider.clone()); + +// let call = ucs03_zkgm +// .send( +// dst_channel_id, +// 0u64, +// 4294967295000000000u64, +// salt_bytes.into(), +// instruction_from_evm_to_union.clone(), +// ) +// .clear_decoder() +// .with_cloned_provider(); + +// let acked_packet = t +// .ctx +// .send_and_recv_and_ack_with_retry::( +// &t.ctx.dst, +// ETH_ADDRESS_ZKGM.into(), +// call, +// &t.ctx.src, +// 3, +// Duration::from_secs(20), +// Duration::from_secs(720), +// &evm_provider, +// ) +// .await; + +// let acked_packet = acked_packet.unwrap(); +// assert!( +// acked_packet.tag == 0, +// "Packet is acked successfully, but it should not be. Tag: {:?}", +// acked_packet.tag +// ); + +// let proxy_balance = t +// .ctx +// .src +// .native_balance(zkgm_proxy_calculated, "au") +// .await +// .unwrap(); + +// println!("Proxy balance before: {}", proxy_balance); +// } diff --git a/tools/union-test/tests/lst_common/mod.rs b/tools/union-test/tests/lst_common/mod.rs new file mode 100644 index 0000000000..200a3f4f1f --- /dev/null +++ b/tools/union-test/tests/lst_common/mod.rs @@ -0,0 +1,299 @@ +use std::{future::Future, sync::Arc, time::Duration}; + +use alloy::{network::AnyNetwork, providers::DynProvider}; +use alloy_sol_types::SolValue as _; +use cosmwasm_std::Addr; +use hex_literal::hex; +use protos::cosmos::base::v1beta1::Coin as ProtoCoin; +use rand::RngCore as _; +use serde::Deserialize; +use tokio::sync::{Mutex, OnceCell}; +use tracing::info; +use tracing_subscriber::FmtSubscriber; +use ucs03_zkgm::com::{ + Instruction, SolverMetadata, TokenOrderV2, INSTR_VERSION_2, OP_TOKEN_ORDER, TAG_ACK_SUCCESS, + TOKEN_ORDER_KIND_SOLVE, +}; +use union_test::{ + cosmos::{self}, + cosmos_helpers::{ + calculate_cosmos_contract_address, SALT_ESCROW_VAULT, SALT_EU, SALT_LST_HUB, + SALT_LST_STAKER, SALT_ZKGM, + }, + evm, TestContext, +}; +use unionlabs::{ + encoding::{EncodeAs, Json}, + primitives::H160, +}; +use voyager_sdk::{anyhow, serde_json}; + +pub static CTX: OnceCell<(Mutex, Arc)> = OnceCell::const_new(); + +pub const ETH_ADDRESS_U: H160 = H160::new(hex!("0c8C6f58156D10d18193A8fFdD853e1b9F8D8836")); +pub const ETH_ADDRESS_ZKGM: H160 = H160::new(hex!("05FD55C1AbE31D3ED09A76216cA8F0372f4B2eC5")); + +#[allow(unused)] +pub struct UnionAddressBook { + pub zkgm: Addr, + pub lst_hub: Addr, + pub eu: Addr, + pub escrow_vault: Addr, + pub lst_staker: Addr, +} + +pub struct LstContext { + pub union_address: UnionAddressBook, + pub ctx: TestContext, +} + +#[derive(Deserialize)] +pub struct Config { + evm: evm::Config, + union: cosmos::Config, + needed_channel_count: u32, + voyager_config_file_path: String, + union_deployer_addr: String, +} + +#[derive(Clone)] +pub struct SharedData { + pub stakers: Vec<(alloy::primitives::Address, DynProvider)>, +} + +pub struct Queue { + tests: Vec, + shared_data: SharedData, +} + +pub async fn run_test_in_queue< + Fut: Future, + F: Fn(Arc, SharedData) -> Fut, +>( + key: &str, + test_fn: F, +) { + let ctx = CTX + .get_or_init(|| async { + let subscriber = FmtSubscriber::builder() + .with_max_level(tracing::Level::INFO) + .finish(); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + let cfg: Config = serde_json::from_str(include_str!("../config.json")).unwrap(); + + let src = cosmos::Module::new(cfg.union).await.unwrap(); + let dst = evm::Module::new(cfg.evm).await.unwrap(); + + let ctx = TestContext::new( + src, + dst, + cfg.needed_channel_count as usize, + &cfg.voyager_config_file_path, + ) + .await + .unwrap(); + + ( + Mutex::new(Queue { + tests: { + let mut t = vec!["bond".into(), "unbond".into(), "withdraw".into()]; + t.reverse(); + t + }, + shared_data: SharedData { stakers: vec![] }, + }), + Arc::new(LstContext { + union_address: UnionAddressBook { + zkgm: calculate_cosmos_contract_address( + &cfg.union_deployer_addr, + SALT_ZKGM, + ) + .unwrap(), + lst_hub: calculate_cosmos_contract_address( + &cfg.union_deployer_addr, + SALT_LST_HUB, + ) + .unwrap(), + eu: calculate_cosmos_contract_address(&cfg.union_deployer_addr, SALT_EU) + .unwrap(), + escrow_vault: calculate_cosmos_contract_address( + &cfg.union_deployer_addr, + SALT_ESCROW_VAULT, + ) + .unwrap(), + lst_staker: calculate_cosmos_contract_address( + &cfg.union_deployer_addr, + SALT_LST_STAKER, + ) + .unwrap(), + }, + ctx, + }), + ) + }) + .await; + + loop { + { + let mut lock = ctx.0.lock().await; + if lock.tests.last().unwrap() == key { + lock.shared_data = test_fn(ctx.1.clone(), lock.shared_data.clone()).await; + let _ = lock.tests.pop(); + return; + } + } + tokio::time::sleep(Duration::from_secs(2)).await; + } +} + +pub async fn eth_set_fungible_counterparty( + module: &evm::Module, + channel_id: u32, + base_token: &[u8], + beneficiary: &[u8], +) -> anyhow::Result<()> { + info!("registering fungible counterparty"); + + let (_, privileged_account) = module.get_provider_privileged().await; + module + .u_register_fungible_counterpart( + ETH_ADDRESS_U, + privileged_account.clone(), + alloy::primitives::U256::ZERO, + channel_id, + base_token.to_vec().into(), + evm::u::U::FungibleCounterparty { + beneficiary: beneficiary.to_vec().into(), + }, + ) + .await + .unwrap(); + + Ok(()) +} + +pub async fn eth_fund_u( + t: &LstContext, + src_channel_id: u32, + receiver: H160, + min_amount: u64, + amount: u64, +) -> anyhow::Result<()> { + let (_, evm_provider) = t.ctx.dst.get_provider().await; + let (cosmos_address, cosmos_provider) = t.ctx.src.get_signer().await; + + let u_balance = t + .ctx + .dst + .zkgmerc20_balance_of(ETH_ADDRESS_U, receiver, evm_provider.clone()) + .await + .unwrap(); + + if min_amount > amount { + return Err(anyhow::anyhow!("you seriously wanna fund your contract less than min amount, when min amount is greater?")); + } + + if u_balance > min_amount.into() { + Ok(()) + } else { + info!("the {receiver} is running low on U (u_balance), funding it with {amount}"); + + let metadata = SolverMetadata { + solverAddress: ETH_ADDRESS_U.into_bytes().into(), + metadata: Default::default(), + } + .abi_encode_params(); + + let amount = Into::::into(amount).into(); + + let instruction_cosmos = Instruction { + version: INSTR_VERSION_2, + opcode: OP_TOKEN_ORDER, + operand: TokenOrderV2 { + sender: cosmos_address.to_string().into_bytes().into(), + receiver: receiver.into_bytes().into(), + base_token: "au".as_bytes().into(), + base_amount: amount, + kind: TOKEN_ORDER_KIND_SOLVE, + metadata: metadata.into(), + quote_token: ETH_ADDRESS_U.into_bytes().into(), + quote_amount: amount, + } + .abi_encode_params() + .into(), + }; + + let mut salt_bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut salt_bytes); + + let (_, ack) = t + .ctx + .send_and_recv_and_ack_with_retry::( + &t.ctx.src, + t.union_address.zkgm.clone(), + ( + ucs03_zkgm::msg::ExecuteMsg::Send { + channel_id: src_channel_id.try_into().unwrap(), + timeout_height: 0u64.into(), + timeout_timestamp: voyager_sdk::primitives::Timestamp::from_secs( + u32::MAX.into(), + ), + salt: salt_bytes.into(), + instruction: instruction_cosmos.abi_encode_params().into(), + } + .encode_as::(), + vec![ProtoCoin { + denom: "au".into(), + amount: amount.to_string(), + }], + ), + &t.ctx.dst, + 3, + Duration::from_secs(20), + Duration::from_secs(720), + cosmos_provider, + ) + .await?; + + // make sure the transfer is successful + assert_eq!(ack.tag, TAG_ACK_SUCCESS); + + Ok(()) + } +} + +pub mod evm_helper { + use alloy::primitives::Address; + + use super::*; + + pub fn make_token_order_v2( + escrow_vault_address: &Addr, + sender: &Address, + receiver: &Addr, + amount: alloy::primitives::U256, + ) -> Instruction { + Instruction { + version: INSTR_VERSION_2, + opcode: OP_TOKEN_ORDER, + operand: TokenOrderV2 { + sender: sender.to_vec().into(), + receiver: receiver.as_bytes().to_vec().into(), + base_token: ETH_ADDRESS_U.into_bytes().into(), + base_amount: amount, + quote_token: b"au".into(), + quote_amount: amount, + kind: TOKEN_ORDER_KIND_SOLVE, + metadata: SolverMetadata { + solverAddress: escrow_vault_address.as_bytes().to_vec().into(), + metadata: Default::default(), + } + .abi_encode_params() + .into(), + } + .abi_encode_params() + .into(), + } + } +}