From d8e7a5f93faa6a3ffdb49f99363cc718dc73b859 Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Wed, 13 Apr 2022 12:30:22 +0100 Subject: [PATCH] terra/token_bridge: transfer with payload Also rename terra token-bridge package so it's unique Otherwise cargo can't find it externally, and confuses it with the solana one. --- terra/Cargo.lock | 1167 ++++++++++++++++- terra/Cargo.toml | 11 +- terra/Makefile | 16 +- .../mock-bridge-integration/.cargo/config | 5 + .../mock-bridge-integration/Cargo.toml | 37 + .../mock-bridge-integration/src/contract.rs | 120 ++ .../mock-bridge-integration/src/lib.rs | 6 + .../mock-bridge-integration/src/msg.rs | 31 + .../mock-bridge-integration/src/state.rs | 31 + terra/contracts/token-bridge/Cargo.toml | 2 +- .../token-bridge/_tests/integration.rs | 114 ++ terra/contracts/token-bridge/src/contract.rs | 497 ++++--- terra/contracts/token-bridge/src/lib.rs | 3 + terra/contracts/token-bridge/src/msg.rs | 25 +- terra/contracts/token-bridge/src/state.rs | 33 + .../contracts/token-bridge/src/testing/mod.rs | 1 + .../token-bridge/src/testing/tests.rs | 197 +++ terra/contracts/wormhole/src/lib.rs | 3 + terra/contracts/wormhole/src/state.rs | 73 -- terra/contracts/wormhole/src/testing/mod.rs | 1 + terra/contracts/wormhole/src/testing/tests.rs | 162 +++ terra/test/.gitignore | 1 + terra/test/README.md | 2 +- terra/test/src/__tests__/bridge.ts | 437 +++++- terra/test/src/helpers/client.ts | 20 + terra/test/src/helpers/receipt.ts | 14 + terra/tools/deploy.js | 7 +- terra/tools/deploy_single.js | 5 +- 28 files changed, 2661 insertions(+), 360 deletions(-) create mode 100644 terra/contracts/mock-bridge-integration/.cargo/config create mode 100644 terra/contracts/mock-bridge-integration/Cargo.toml create mode 100644 terra/contracts/mock-bridge-integration/src/contract.rs create mode 100644 terra/contracts/mock-bridge-integration/src/lib.rs create mode 100644 terra/contracts/mock-bridge-integration/src/msg.rs create mode 100644 terra/contracts/mock-bridge-integration/src/state.rs create mode 100644 terra/contracts/token-bridge/_tests/integration.rs create mode 100644 terra/contracts/token-bridge/src/testing/mod.rs create mode 100644 terra/contracts/token-bridge/src/testing/tests.rs create mode 100644 terra/contracts/wormhole/src/testing/mod.rs create mode 100644 terra/contracts/wormhole/src/testing/tests.rs create mode 100644 terra/test/.gitignore create mode 100644 terra/test/src/helpers/receipt.ts diff --git a/terra/Cargo.lock b/terra/Cargo.lock index a7407c864..c35f6f7ca 100644 --- a/terra/Cargo.lock +++ b/terra/Cargo.lock @@ -2,6 +2,42 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.1", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.27.1", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -18,6 +54,12 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.9.0" @@ -40,12 +82,24 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clru" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" + [[package]] name = "const-oid" version = "0.6.2" @@ -54,9 +108,9 @@ checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" [[package]] name = "cosmwasm-crypto" -version = "0.16.7" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b110e31d47bd265e17ec88dd7328fcf40e1ee67a6131c1ab492f77fef8cd83" +checksum = "4ec9bdd1f4da5fc0d085251b0322661c5aaf773ab299e3e205fb18130b7f6ba3" dependencies = [ "digest", "ed25519-zebra", @@ -67,18 +121,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "0.16.7" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0faf9bad5eb0a43a00406e64f8d33407a06bd1826fa976195a69db70e6c18d9d" +checksum = "5ac17a14b4ab09a5d89b5301218067acca33d9311376e5c34c9877f09e562395" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.0.0-beta8" +version = "1.0.0-beta4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d75f6a05667d8613b24171ef2c77a8bf6fb9c14f9e3aaa39aa10e0c6416ed67" +checksum = "e5de9749a4a933c34c59db83a77935a79cd705f93ae2b1cfbe40821f32751f75" dependencies = [ "schemars", "serde_json", @@ -86,14 +140,13 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "0.16.7" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0d4e46ab20939af6366a71783324ae4babdedb111f0dd797d063a2e68718bc" +checksum = "e47306c113f4d964c35a74a87ceb8ccfb5811e9810a9dc427101148b5b9134ca" dependencies = [ "base64", "cosmwasm-crypto", "cosmwasm-derive", - "forward_ref", "schemars", "serde", "serde-json-wasm", @@ -103,23 +156,157 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "0.16.7" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c243d26ba6c49abb5ed69762648a9c664ba263debce425ad10603e7b8aa92ced" +checksum = "2e3472d8e0e7155c5f4d89674ad47adede4b1491ad14f4141610e1522028a6a7" dependencies = [ "cosmwasm-std", "serde", ] [[package]] -name = "cpufeatures" -version = "0.2.2" +name = "cosmwasm-vm" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "8d90f1d30e2d01d815c520dad2738f93188f2e64b3dda3e11609c13eb73109b8" +dependencies = [ + "clru", + "cosmwasm-crypto", + "cosmwasm-std", + "hex", + "loupe", + "parity-wasm", + "schemars", + "serde", + "serde_json", + "sha2", + "thiserror", + "wasmer", + "wasmer-middlewares", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ "libc", ] +[[package]] +name = "cranelift-bforest" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ca3560686e7c9c7ed7e0fe77469f2410ba5d7781b1acaa9adc8d8deea28e3e" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf9bf1ffffb6ce3d2e5ebc83549bd2436426c99b31cc550d521364cbe35d276" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli 0.24.0", + "log", + "regalloc", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc21936a5a6d07e23849ffe83e5c1f6f50305c074f4b2970ca50c13bf55b821" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5b6ffaa87560bebe69a5446449da18090b126037920b0c1c6d5945f72faf6b" + +[[package]] +name = "cranelift-entity" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6b4a8bef04f82e4296782646f733c641d09497df2fabf791323fefaa44c64c" + +[[package]] +name = "cranelift-frontend" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b783b351f966fce33e3c03498cb116d16d97a8f9978164a60920bd0d3a99c" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + [[package]] name = "crunchy" version = "0.1.6" @@ -304,10 +491,45 @@ dependencies = [ ] [[package]] -name = "der" -version = "0.4.5" +name = "darling" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2" dependencies = [ "const-oid", ] @@ -323,9 +545,35 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "dynasm" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab1096ebdaa974cd6a41a743e94dfa00cce9bfbf4690bcc73fdec6a903938ccc" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dynasmrt" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c69d1e16ae47889b47c301c790f48615cd9bfbdf586e3f6d4fde64af3d259" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.0", +] [[package]] name = "ecdsa" @@ -353,6 +601,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "elliptic-curve" version = "0.10.6" @@ -369,6 +623,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enumset" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6216d2c19a6fb5f29d1ada1dc7bc4367a8cbf0fa4af5cf12e07b5bbdde6b5b2c" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6451128aa6655d880755345d085494cf7561a6bee7c8dc821e5d77e6d267ecd4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "ff" version = "0.10.1" @@ -380,16 +661,16 @@ dependencies = [ ] [[package]] -name = "forward_ref" -version = "1.0.0" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -408,15 +689,32 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + [[package]] name = "group" version = "0.10.0" @@ -428,6 +726,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -445,10 +758,27 @@ dependencies = [ ] [[package]] -name = "itoa" +name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "k256" @@ -475,10 +805,139 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.122" +name = "leb128" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" + +[[package]] +name = "libloading" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loupe" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" +dependencies = [ + "indexmap", + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mock-bridge-integration" +version = "0.1.0" +dependencies = [ + "bigint", + "cosmwasm-std", + "cosmwasm-storage", + "cosmwasm-vm", + "cw20", + "cw20-base", + "cw20-wrapped", + "generic-array", + "hex", + "k256", + "lazy_static", + "schemars", + "serde", + "serde_json", + "sha3", + "terraswap", + "thiserror", + "token-bridge-terra", + "wormhole-bridge-terra", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nft-bridge" @@ -499,12 +958,54 @@ dependencies = [ "wormhole-bridge-terra", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" +dependencies = [ + "crc32fast", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + [[package]] name = "pkcs8" version = "0.7.6" @@ -516,21 +1017,93 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.37" +name = "ppv-lite86" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] [[package]] -name = "quote" -version = "1.0.18" +name = "ptr_meta" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", ] [[package]] @@ -548,20 +1121,136 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.6", + "getrandom 0.2.3", ] [[package]] -name = "ryu" -version = "1.0.9" +name = "rand_hc" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regalloc" +version = "0.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +dependencies = [ + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rkyv" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb135b3e5e3311f0a254bfb00333f4bac9ef1d89888b84242a89eb8722b09a07" +dependencies = [ + "memoffset", + "ptr_meta", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8f489f6b6d8551bb15904293c1ad58a6abafa7d8390d15f7ed05a2afcd87d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "schemars" -version = "0.8.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3" +checksum = "271ac0c667b8229adf70f0f957697c96fafd7486ab7481e15dc5e45e3e6a4368" dependencies = [ "dyn-clone", "schemars_derive", @@ -571,9 +1260,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b" +checksum = "6ebda811090b257411540779860bc09bf321bc587f58d2c5864309d1566214e7" dependencies = [ "proc-macro2", "quote", @@ -582,28 +1271,49 @@ dependencies = [ ] [[package]] -name = "serde" -version = "1.0.136" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853" +checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -623,9 +1333,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19" dependencies = [ "itoa", "ryu", @@ -634,9 +1344,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.9" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", "cfg-if", @@ -667,6 +1377,12 @@ dependencies = [ "rand_core 0.6.3", ] +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + [[package]] name = "spki" version = "0.4.1" @@ -676,12 +1392,24 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.4.1" @@ -690,15 +1418,35 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.91" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "target-lexicon" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "terra-cosmwasm" version = "2.2.0" @@ -745,7 +1493,7 @@ dependencies = [ ] [[package]] -name = "token-bridge" +name = "token-bridge-terra" version = "0.1.0" dependencies = [ "bigint", @@ -768,16 +1516,48 @@ dependencies = [ ] [[package]] -name = "typenum" -version = "1.15.0" +name = "tracing" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "uint" -version = "0.9.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" dependencies = [ "byteorder", "crunchy 0.2.2", @@ -793,9 +1573,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wasi" @@ -809,6 +1589,259 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasmer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f52e455a01d0fac439cd7a96ba9b519bdc84e923a5b96034054697ebb17cd75" +dependencies = [ + "cfg-if", + "indexmap", + "loupe", + "more-asserts", + "target-lexicon", + "thiserror", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-singlepass", + "wasmer-derive", + "wasmer-engine", + "wasmer-engine-dylib", + "wasmer-engine-universal", + "wasmer-types", + "wasmer-vm", + "winapi", +] + +[[package]] +name = "wasmer-compiler" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc86dda6f715f03104800be575a38382b35c3962953af9e9d8722dcf0bd2458f" +dependencies = [ + "enumset", + "loupe", + "rkyv", + "serde", + "serde_bytes", + "smallvec", + "target-lexicon", + "thiserror", + "wasmer-types", + "wasmer-vm", + "wasmparser", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a570746cbec434179e2d53357973a34dfdb208043104e8fac3b7b0023015cf6" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.24.0", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "tracing", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-compiler-singlepass" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9429b9f7708c582d855b1787f09c7029ff23fb692550d4a1cc351c8ea84c3014" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "lazy_static", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee7b351bcc1e782997c72dc0b5b328f3ddcad4813b8ce3cac3f25ae5a4ab56b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmer-engine" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8454ead320a4017ba36ddd9ab4fbf7776fceea6ab0b79b5e53664a1682569fc3" +dependencies = [ + "backtrace", + "lazy_static", + "loupe", + "memmap2 0.2.3", + "more-asserts", + "rustc-demangle", + "serde", + "serde_bytes", + "target-lexicon", + "thiserror", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-engine-dylib" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa390d123ebe23d5315c39f6063fcc18319661d03c8000f23d0fe1c011e8135" +dependencies = [ + "cfg-if", + "leb128", + "libloading", + "loupe", + "rkyv", + "serde", + "tempfile", + "tracing", + "wasmer-compiler", + "wasmer-engine", + "wasmer-object", + "wasmer-types", + "wasmer-vm", + "which", +] + +[[package]] +name = "wasmer-engine-universal" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dffe8015f08915eb4939ebc8e521cde8246f272f5197ea60d46214ac5aef285" +dependencies = [ + "cfg-if", + "leb128", + "loupe", + "region", + "rkyv", + "wasmer-compiler", + "wasmer-engine", + "wasmer-types", + "wasmer-vm", + "winapi", +] + +[[package]] +name = "wasmer-middlewares" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95d2b4722d64c850893f7a7eab3ab76181efbafcd366827801d8bcd64bff525f" +dependencies = [ + "loupe", + "wasmer", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-object" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c541c985799fc1444702501c15d41becfb066c92d9673defc1c7417fd8739e15" +dependencies = [ + "object 0.25.3", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91f75d3c31f8b1f8d818ff49624fc974220243cbc07a2252f408192e97c6b51" +dependencies = [ + "indexmap", + "loupe", + "rkyv", + "serde", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469a12346a4831e7dac639b9646d8c9b24c7d2cf0cf458b77f489edb35060c1f" +dependencies = [ + "backtrace", + "cc", + "cfg-if", + "indexmap", + "libc", + "loupe", + "memoffset", + "more-asserts", + "region", + "rkyv", + "serde", + "thiserror", + "wasmer-types", + "winapi", +] + +[[package]] +name = "wasmparser" +version = "0.78.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65" + +[[package]] +name = "which" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "wormhole-bridge-terra" version = "0.1.0" @@ -819,7 +1852,7 @@ dependencies = [ "cw20-base", "cw20-wrapped", "generic-array", - "getrandom 0.2.6", + "getrandom 0.2.3", "hex", "k256", "lazy_static", diff --git a/terra/Cargo.toml b/terra/Cargo.toml index 6a353241f..bb456d9e6 100644 --- a/terra/Cargo.toml +++ b/terra/Cargo.toml @@ -1,5 +1,14 @@ [workspace] -members = ["contracts/cw20-wrapped", "contracts/wormhole", "contracts/token-bridge", "contracts/nft-bridge", "contracts/cw721-wrapped", "packages/cw721", "contracts/cw721-base"] +members = [ + "contracts/cw20-wrapped", + "contracts/wormhole", + "contracts/token-bridge", + "contracts/nft-bridge", + "contracts/cw721-wrapped", + "packages/cw721", + "contracts/cw721-base", + "contracts/mock-bridge-integration", +] [profile.release] opt-level = 3 diff --git a/terra/Makefile b/terra/Makefile index dca889d76..1be0cf77c 100644 --- a/terra/Makefile +++ b/terra/Makefile @@ -1,10 +1,10 @@ bridge_SOURCE=wormhole -token_bridge_SOURCE=token_bridge +token_bridge_SOURCE=token_bridge_terra nft_bridge_SOURCE=nft_bridge SOURCE_FILES=$(shell find . -name "*.rs" -or -name "*.lock" -or -name "*.toml" | grep -v target) -PACKAGES=$(shell find . -name "Cargo.toml" | grep -E 'packages|contracts' | cut -d/ -f3 | sed s/-/_/g) +PACKAGES=$(shell find . -name "Cargo.toml" | grep -E 'packages|contracts' | xargs cat | grep "name *=" | cut -d' ' -f3 | sed s/\"//g | sed s/-/_/g) WASMS=$(patsubst %, artifacts/%.wasm, $(PACKAGES)) -include ../Makefile.help @@ -40,7 +40,7 @@ deploy/token_bridge: token_bridge-code-id-$(NETWORK).txt ## Deploy NFT bridge deploy/nft_bridge: nft_bridge-code-id-$(NETWORK).txt -%-code-id-$(NETWORK).txt: check-network tools/node_modules payer-$(NETWORK).json +%-code-id-$(NETWORK).txt: check-network tools/node_modules payer-$(NETWORK).json artifacts @echo "Deploying artifacts/$($*_SOURCE).wasm on $(NETWORK)" @node tools/deploy_single.js \ --network $(NETWORK) \ @@ -59,9 +59,15 @@ LocalTerra: test/node_modules: test/package-lock.json cd test && npm ci +.PHONY: unit-test +## Run unit tests +unit-test: + cargo test -p wormhole-bridge-terra + cargo test -p token-bridge-terra + .PHONY: test -## Run integration test -test: artifacts test/node_modules LocalTerra +## Run unit and integration tests +test: artifacts test/node_modules LocalTerra unit-test @if pgrep terrad; then echo "Error: terrad already running. Stop it before running tests"; exit 1; fi cd LocalTerra && docker compose up --detach sleep 5 diff --git a/terra/contracts/mock-bridge-integration/.cargo/config b/terra/contracts/mock-bridge-integration/.cargo/config new file mode 100644 index 000000000..2d5cce4ea --- /dev/null +++ b/terra/contracts/mock-bridge-integration/.cargo/config @@ -0,0 +1,5 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +unit-test = "test --lib --features backtraces" +integration-test = "test --test integration" \ No newline at end of file diff --git a/terra/contracts/mock-bridge-integration/Cargo.toml b/terra/contracts/mock-bridge-integration/Cargo.toml new file mode 100644 index 000000000..2aaeef86a --- /dev/null +++ b/terra/contracts/mock-bridge-integration/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "mock-bridge-integration" +version = "0.1.0" +edition = "2018" +description = "Mock Bridge Integration for Transfer w/ Payload" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all init/handle/query exports +library = [] + +[dependencies] +cosmwasm-std = { version = "0.16.0" } +cosmwasm-storage = { version = "0.16.0" } +schemars = "0.8.1" +serde = { version = "1.0.103", default-features = false, features = ["derive"] } +cw20 = "0.8.0" +cw20-base = { version = "0.8.0", features = ["library"] } +cw20-wrapped = { path = "../cw20-wrapped", features = ["library"] } +terraswap = "2.4.0" +thiserror = { version = "1.0.20" } +k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] } +sha3 = { version = "0.9.1", default-features = false } +generic-array = { version = "0.14.4" } +hex = "0.4.2" +lazy_static = "1.4.0" +bigint = "4" + +wormhole-bridge-terra = { path = "../wormhole", features = ["library"] } +token-bridge-terra = { path = "../token-bridge", features = ["library"] } + +[dev-dependencies] +cosmwasm-vm = { version = "0.16.0", default-features = false } +serde_json = "1.0" diff --git a/terra/contracts/mock-bridge-integration/src/contract.rs b/terra/contracts/mock-bridge-integration/src/contract.rs new file mode 100644 index 000000000..768d67e77 --- /dev/null +++ b/terra/contracts/mock-bridge-integration/src/contract.rs @@ -0,0 +1,120 @@ +use cosmwasm_std::{ + entry_point, + to_binary, + Binary, + CosmosMsg, + Deps, + DepsMut, + Env, + MessageInfo, + QueryRequest, + Reply, + Response, + StdError, + StdResult, + SubMsg, + WasmMsg, + WasmQuery, +}; + +use crate::{ + msg::{ + ExecuteMsg, + InstantiateMsg, + MigrateMsg, + QueryMsg, + }, + state::{ + Config, + config, + config_read, + }, +}; + +use token_bridge_terra::{ + msg::{ + ExecuteMsg as TokenBridgeExecuteMsg, + QueryMsg as TokenBridgeQueryMessage, + }, + msg::TransferInfoResponse, +}; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + Ok(Response::new()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> StdResult { + let state = Config { + token_bridge_contract: msg.token_bridge_contract, + }; + config(deps.storage).save(&state)?; + + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { + match msg { + ExecuteMsg::CompleteTransferWithPayload { data } => { + complete_transfer_with_payload(deps, env, info, &data) + }, + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, _msg: Reply) -> StdResult { + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { + Err(StdError::generic_err("not implemented")) +} + +fn complete_transfer_with_payload( + mut deps: DepsMut, + _env: Env, + info: MessageInfo, + data: &Binary, +) -> StdResult { + let cfg = config_read(deps.storage).load()?; + + let messages = vec![SubMsg::reply_on_success( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: cfg.token_bridge_contract, + msg: to_binary(&TokenBridgeExecuteMsg::CompleteTransferWithPayload { + data: data.clone(), relayer: info.sender.to_string() + })?, + funds: vec![], + }), + 1, + ),]; + + let transfer_info = parse_transfer_vaa(deps.as_ref(), data)?; + + Ok(Response::new() + .add_submessages(messages) + .add_attribute("action", "complete_transfer_with_payload") + .add_attribute("transfer_payload", Binary::from(transfer_info.payload).to_base64())) +} + +fn parse_transfer_vaa( + deps: Deps, + data: &Binary, +) -> StdResult { + let cfg = config_read(deps.storage).load()?; + let transfer_info: TransferInfoResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: cfg.token_bridge_contract, + msg: to_binary(&TokenBridgeQueryMessage::TransferInfo { + vaa: data.clone(), + })?, + }))?; + Ok(transfer_info) +} \ No newline at end of file diff --git a/terra/contracts/mock-bridge-integration/src/lib.rs b/terra/contracts/mock-bridge-integration/src/lib.rs new file mode 100644 index 000000000..f67a6e5e3 --- /dev/null +++ b/terra/contracts/mock-bridge-integration/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +extern crate lazy_static; + +pub mod contract; +pub mod msg; +pub mod state; diff --git a/terra/contracts/mock-bridge-integration/src/msg.rs b/terra/contracts/mock-bridge-integration/src/msg.rs new file mode 100644 index 000000000..c902104e2 --- /dev/null +++ b/terra/contracts/mock-bridge-integration/src/msg.rs @@ -0,0 +1,31 @@ +use cosmwasm_std::Binary; +use schemars::JsonSchema; +use serde::{ + Deserialize, + Serialize, +}; + +type HumanAddr = String; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct InstantiateMsg { + pub token_bridge_contract: HumanAddr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + CompleteTransferWithPayload { + data: Binary, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct MigrateMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + WrappedRegistry { chain: u16, address: Binary }, +} \ No newline at end of file diff --git a/terra/contracts/mock-bridge-integration/src/state.rs b/terra/contracts/mock-bridge-integration/src/state.rs new file mode 100644 index 000000000..6e311d057 --- /dev/null +++ b/terra/contracts/mock-bridge-integration/src/state.rs @@ -0,0 +1,31 @@ +use schemars::JsonSchema; +use serde::{ + Deserialize, + Serialize, +}; + +use cosmwasm_std::Storage; +use cosmwasm_storage::{ + singleton, + singleton_read, + ReadonlySingleton, + Singleton, +}; + +type HumanAddr = String; + +pub static CONFIG_KEY: &[u8] = b"config"; + +// Guardian set information +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Config { + pub token_bridge_contract: HumanAddr, +} + +pub fn config(storage: &mut dyn Storage) -> Singleton { + singleton(storage, CONFIG_KEY) +} + +pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton { + singleton_read(storage, CONFIG_KEY) +} \ No newline at end of file diff --git a/terra/contracts/token-bridge/Cargo.toml b/terra/contracts/token-bridge/Cargo.toml index d2e93619c..3dea0a0ea 100644 --- a/terra/contracts/token-bridge/Cargo.toml +++ b/terra/contracts/token-bridge/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "token-bridge" +name = "token-bridge-terra" version = "0.1.0" authors = ["Yuriy Savchenko "] edition = "2018" diff --git a/terra/contracts/token-bridge/_tests/integration.rs b/terra/contracts/token-bridge/_tests/integration.rs new file mode 100644 index 000000000..88d0359ba --- /dev/null +++ b/terra/contracts/token-bridge/_tests/integration.rs @@ -0,0 +1,114 @@ +static WASM: &[u8] = include_bytes!("../../../target/wasm32-unknown-unknown/release/wormhole.wasm"); + +use cosmwasm_std::{ + from_slice, + Coin, + Env, + HumanAddr, + InitResponse, +}; +use cosmwasm_storage::to_length_prefixed; +use cosmwasm_vm::{ + testing::{ + init, + mock_env, + mock_instance, + MockApi, + MockQuerier, + MockStorage, + }, + Api, + Instance, + Storage, +}; + +use wormhole::{ + msg::InitMsg, + state::{ + ConfigInfo, + GuardianAddress, + GuardianSetInfo, + CONFIG_KEY, + }, +}; + +use hex; + +enum TestAddress { + INITIALIZER, +} + +impl TestAddress { + fn value(&self) -> HumanAddr { + match self { + TestAddress::INITIALIZER => HumanAddr::from("initializer"), + } + } +} + +fn mock_env_height(signer: &HumanAddr, height: u64, time: u64) -> Env { + let mut env = mock_env(signer, &[]); + env.block.height = height; + env.block.time = time; + env +} + +fn get_config_info(storage: &S) -> ConfigInfo { + let key = to_length_prefixed(CONFIG_KEY); + let data = storage + .get(&key) + .0 + .expect("error getting data") + .expect("data should exist"); + from_slice(&data).expect("invalid data") +} + +fn do_init( + height: u64, + guardians: &Vec, +) -> Instance { + let mut deps = mock_instance(WASM, &[]); + let init_msg = InitMsg { + initial_guardian_set: GuardianSetInfo { + addresses: guardians.clone(), + expiration_time: 100, + }, + guardian_set_expirity: 50, + wrapped_asset_code_id: 999, + }; + let env = mock_env_height(&TestAddress::INITIALIZER.value(), height, 0); + let owner = deps + .api + .canonical_address(&TestAddress::INITIALIZER.value()) + .0 + .unwrap(); + let res: InitResponse = init(&mut deps, env, init_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // query the store directly + deps.with_storage(|storage| { + assert_eq!( + get_config_info(storage), + ConfigInfo { + guardian_set_index: 0, + guardian_set_expirity: 50, + wrapped_asset_code_id: 999, + owner, + fee: Coin::new(10000, "uluna"), + } + ); + Ok(()) + }) + .unwrap(); + deps +} + +#[test] +fn init_works() { + let guardians = vec![GuardianAddress::from(GuardianAddress { + bytes: hex::decode("beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe") + .expect("Decoding failed") + .into(), + })]; + let _deps = do_init(111, &guardians); +} diff --git a/terra/contracts/token-bridge/src/contract.rs b/terra/contracts/token-bridge/src/contract.rs index ab2ba5bf5..c7d171ff8 100644 --- a/terra/contracts/token-bridge/src/contract.rs +++ b/terra/contracts/token-bridge/src/contract.rs @@ -1,4 +1,54 @@ -use crate::msg::WrappedRegistryResponse; +use cw20::{ + BalanceResponse, + TokenInfoResponse, +}; +use cw20_base::msg::{ + ExecuteMsg as TokenMsg, + QueryMsg as TokenQuery, +}; +use cw20_wrapped::msg::{ + ExecuteMsg as WrappedMsg, + InitHook, + InstantiateMsg as WrappedInit, + QueryMsg as WrappedQuery, + WrappedAssetInfoResponse, +}; +use sha3::{ + Digest, + Keccak256, +}; +use std::{ + cmp::{ + max, + min, + }, + str::FromStr, +}; +use terraswap::asset::{ + Asset, + AssetInfo, +}; + +use wormhole::{ + byte_utils::{ + extend_address_to_32, + extend_string_to_32, + get_string_from_32, + ByteUtils, + }, + error::ContractError, + msg::{ + ExecuteMsg as WormholeExecuteMsg, + QueryMsg as WormholeQueryMsg, + }, + state::{ + vaa_archive_add, + vaa_archive_check, + GovernancePacket, + ParsedVAA, + }, +}; + use cosmwasm_std::{ coin, entry_point, @@ -30,6 +80,8 @@ use crate::{ InstantiateMsg, MigrateMsg, QueryMsg, + TransferInfoResponse, + WrappedRegistryResponse, }, state::{ bridge_contracts, @@ -53,64 +105,10 @@ use crate::{ TokenBridgeMessage, TransferInfo, TransferState, + TransferWithPayloadInfo, UpgradeContract, }, }; -use wormhole::{ - byte_utils::{ - extend_address_to_32, - extend_string_to_32, - get_string_from_32, - ByteUtils, - }, - error::ContractError, -}; - -use cw20_base::msg::{ - ExecuteMsg as TokenMsg, - QueryMsg as TokenQuery, -}; - -use wormhole::msg::{ - ExecuteMsg as WormholeExecuteMsg, - QueryMsg as WormholeQueryMsg, -}; - -use wormhole::state::{ - vaa_archive_add, - vaa_archive_check, - GovernancePacket, - ParsedVAA, -}; - -use cw20::{ - BalanceResponse, - TokenInfoResponse, -}; - -use cw20_wrapped::msg::{ - ExecuteMsg as WrappedMsg, - InitHook, - InstantiateMsg as WrappedInit, - QueryMsg as WrappedQuery, - WrappedAssetInfoResponse, -}; -use terraswap::asset::{ - Asset, - AssetInfo, -}; - -use sha3::{ - Digest, - Keccak256, -}; -use std::{ - cmp::{ - max, - min, - }, - str::FromStr, -}; type HumanAddr = String; @@ -119,6 +117,11 @@ const CHAIN_ID: u16 = 3; const WRAPPED_ASSET_UPDATING: &str = "updating"; +pub enum TransferType { + WithoutPayload, + WithPayload { payload: A }, +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { Ok(Response::new()) @@ -134,7 +137,7 @@ pub fn instantiate( // Save general wormhole info let state = ConfigInfo { gov_chain: msg.gov_chain, - gov_address: msg.gov_address.as_slice().to_vec(), + gov_address: msg.gov_address.into(), wormhole_contract: msg.wormhole_contract, wrapped_asset_code_id: msg.wrapped_asset_code_id, }; @@ -155,15 +158,33 @@ pub fn reply(deps: DepsMut, env: Env, _msg: Reply) -> StdResult { // for why this is necessary. wrapped_transfer_tmp(deps.storage).remove(); - let mut info = TransferInfo::deserialize(&state.message)?; + let token_bridge_message = TokenBridgeMessage::deserialize(&state.message)?; + + let (mut transfer_info, transfer_type) = match token_bridge_message.action { + Action::TRANSFER => { + let info = TransferInfo::deserialize(&token_bridge_message.payload)?; + Ok((info, TransferType::WithoutPayload)) + } + Action::TRANSFER_WITH_PAYLOAD => { + let info = TransferWithPayloadInfo::deserialize(&token_bridge_message.payload)?; + Ok(( + info.transfer_info, + TransferType::WithPayload { + payload: info.payload, + }, + )) + } + _ => Err(StdError::generic_err("Unreachable")), + }?; // Fetch CW20 Balance post-transfer. - let new_balance: BalanceResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: state.token_address.clone(), - msg: to_binary(&TokenQuery::Balance { - address: env.contract.address.to_string(), - })?, - }))?; + let new_balance: BalanceResponse = + deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: state.token_address.clone(), + msg: to_binary(&TokenQuery::Balance { + address: env.contract.address.to_string(), + })?, + }))?; // Actual amount should be the difference in balance of the CW20 account in question to account // for fee tokens. @@ -172,16 +193,26 @@ pub fn reply(deps: DepsMut, env: Env, _msg: Reply) -> StdResult { let real_amount = real_amount / multiplier; // If the fee is too large the user would receive nothing. - if info.fee.1 > real_amount.u128() { + if transfer_info.fee.1 > real_amount.u128() { return Err(StdError::generic_err("fee greater than sent amount")); } // Update Wormhole message to correct amount. - info.amount.1 = real_amount.u128(); + transfer_info.amount.1 = real_amount.u128(); - let token_bridge_message = TokenBridgeMessage { - action: Action::TRANSFER, - payload: info.serialize(), + let token_bridge_message = match transfer_type { + TransferType::WithoutPayload => TokenBridgeMessage { + action: Action::TRANSFER, + payload: transfer_info.serialize(), + }, + TransferType::WithPayload { payload } => TokenBridgeMessage { + action: Action::TRANSFER_WITH_PAYLOAD, + payload: TransferWithPayloadInfo { + transfer_info, + payload, + } + .serialize(), + }, }; // Post Wormhole Message @@ -194,7 +225,7 @@ pub fn reply(deps: DepsMut, env: Env, _msg: Reply) -> StdResult { })?, }); - send_native(deps.storage, &state.token_canonical, info.amount.1.into())?; + send_native(deps.storage, &state.token_canonical, real_amount)?; Ok(Response::default() .add_message(message) .add_attribute("action", "reply_handler")) @@ -214,10 +245,10 @@ pub fn coins_after_tax(deps: DepsMut, coins: Vec) -> StdResult> Ok(res) } -pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult { +fn parse_vaa(deps: Deps, block_time: u64, data: &Binary) -> StdResult { let cfg = config_read(deps.storage).load()?; let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: cfg.wormhole_contract.clone(), + contract_addr: cfg.wormhole_contract, msg: to_binary(&WormholeQueryMsg::VerifyVAA { vaa: data.clone(), block_time, @@ -244,8 +275,29 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S info, asset, recipient_chain, - recipient.as_slice().to_vec(), + recipient.into(), fee, + TransferType::WithoutPayload, + nonce, + ), + ExecuteMsg::InitiateTransferWithPayload { + asset, + recipient_chain, + recipient, + fee, + payload, + nonce, + } => handle_initiate_transfer( + deps, + env, + info, + asset, + recipient_chain, + recipient.into(), + fee, + TransferType::WithPayload { + payload: payload.into(), + }, nonce, ), ExecuteMsg::DepositTokens {} => deposit_tokens(deps, env, info), @@ -254,6 +306,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::CreateAssetMeta { asset_info, nonce } => { handle_create_asset_meta(deps, env, info, asset_info, nonce) } + ExecuteMsg::CompleteTransferWithPayload { data, relayer } => { + handle_complete_transfer_with_payload(deps, env, info, &data, &relayer) + } } } @@ -500,15 +555,16 @@ fn handle_create_asset_meta_native_token( .add_attribute("meta.block_time", env.block.time.seconds().to_string())) } -fn submit_vaa( - mut deps: DepsMut, +fn handle_complete_transfer_with_payload( + deps: DepsMut, env: Env, info: MessageInfo, data: &Binary, + relayer_address: &HumanAddr, ) -> StdResult { let state = config_read(deps.storage).load()?; - let vaa = parse_vaa(deps.branch(), env.block.time.seconds(), data)?; + let vaa = parse_vaa(deps.as_ref(), env.block.time.seconds(), data)?; let data = vaa.payload; if vaa_archive_check(deps.storage, vaa.hash.as_slice()) { @@ -517,21 +573,64 @@ fn submit_vaa( vaa_archive_add(deps.storage, vaa.hash.as_slice())?; // check if vaa is from governance - if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address { + if is_governance_emitter(&state, vaa.emitter_chain, &vaa.emitter_address) { + return ContractError::InvalidVAAAction.std_err(); + } + + let message = TokenBridgeMessage::deserialize(&data)?; + + match message.action { + Action::TRANSFER_WITH_PAYLOAD => handle_complete_transfer( + deps, + env, + info, + vaa.emitter_chain, + vaa.emitter_address, + TransferType::WithPayload { payload: () }, + &message.payload, + relayer_address, + ), + _ => ContractError::InvalidVAAAction.std_err(), + } +} + +fn submit_vaa( + deps: DepsMut, + env: Env, + info: MessageInfo, + data: &Binary, +) -> StdResult { + let state = config_read(deps.storage).load()?; + + let vaa = parse_vaa(deps.as_ref(), env.block.time.seconds(), data)?; + let data = vaa.payload; + + if vaa_archive_check(deps.storage, vaa.hash.as_slice()) { + return ContractError::VaaAlreadyExecuted.std_err(); + } + vaa_archive_add(deps.storage, vaa.hash.as_slice())?; + + // check if vaa is from governance + if is_governance_emitter(&state, vaa.emitter_chain, &vaa.emitter_address) { return handle_governance_payload(deps, env, &data); } let message = TokenBridgeMessage::deserialize(&data)?; match message.action { - Action::TRANSFER => handle_complete_transfer( - deps, - env, - info, - vaa.emitter_chain, - vaa.emitter_address, - &message.payload, - ), + Action::TRANSFER => { + let sender = info.sender.to_string(); + handle_complete_transfer( + deps, + env, + info, + vaa.emitter_chain, + vaa.emitter_address, + TransferType::WithoutPayload, + &message.payload, + &sender, + ) + } Action::ATTEST_META => handle_attest_meta( deps, env, @@ -604,7 +703,9 @@ fn handle_complete_transfer( info: MessageInfo, emitter_chain: u16, emitter_address: Vec, + transfer_type: TransferType<()>, data: &Vec, + relayer_address: &HumanAddr, ) -> StdResult { let transfer_info = TransferInfo::deserialize(&data)?; match transfer_info.token_address.as_slice()[0] { @@ -614,9 +715,20 @@ fn handle_complete_transfer( info, emitter_chain, emitter_address, + transfer_type, data, + relayer_address, + ), + _ => handle_complete_transfer_token( + deps, + env, + info, + emitter_chain, + emitter_address, + transfer_type, + data, + relayer_address, ), - _ => handle_complete_transfer_token(deps, env, info, emitter_chain, emitter_address, data), } } @@ -626,9 +738,17 @@ fn handle_complete_transfer_token( info: MessageInfo, emitter_chain: u16, emitter_address: Vec, + transfer_type: TransferType<()>, data: &Vec, + relayer_address: &HumanAddr, ) -> StdResult { - let transfer_info = TransferInfo::deserialize(&data)?; + let transfer_info = match transfer_type { + TransferType::WithoutPayload => TransferInfo::deserialize(&data)?, + TransferType::WithPayload { payload: _ } => { + TransferWithPayloadInfo::deserialize(&data)?.transfer_info + } + }; + let expected_contract = bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?; @@ -645,6 +765,15 @@ fn handle_complete_transfer_token( let token_chain = transfer_info.token_chain; let target_address = (&transfer_info.recipient.as_slice()).get_address(0); + let recipient = deps.api.addr_humanize(&target_address)?; + + if let TransferType::WithPayload { payload: _ } = transfer_type { + if recipient != info.sender { + return Err(StdError::generic_err( + "transfers with payload can only be redeemed by the recipient", + )); + } + }; let (not_supported_amount, mut amount) = transfer_info.amount; let (not_supported_fee, mut fee) = transfer_info.fee; @@ -666,11 +795,6 @@ fn handle_complete_transfer_token( return if let Some(contract_addr) = contract_addr { // Asset already deployed, just mint - let recipient = deps - .api - .addr_humanize(&target_address) - .or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?; - let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.clone(), msg: to_binary(&WrappedMsg::Mint { @@ -683,7 +807,7 @@ fn handle_complete_transfer_token( messages.push(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.clone(), msg: to_binary(&WrappedMsg::Mint { - recipient: info.sender.to_string(), + recipient: relayer_address.to_string(), amount: Uint128::from(fee), })?, funds: vec![], @@ -695,14 +819,15 @@ fn handle_complete_transfer_token( .add_attribute("action", "complete_transfer_wrapped") .add_attribute("contract", contract_addr) .add_attribute("recipient", recipient) - .add_attribute("amount", amount.to_string())) + .add_attribute("amount", amount.to_string()) + .add_attribute("relayer", relayer_address) + .add_attribute("fee", fee.to_string())) } else { Err(StdError::generic_err("Wrapped asset not deployed. To deploy, invoke CreateWrapped with the associated AssetMeta")) }; } else { let token_address = transfer_info.token_address.as_slice().get_address(0); - let recipient = deps.api.addr_humanize(&target_address)?; let contract_addr = deps.api.addr_humanize(&token_address)?; // note -- here the amount is the amount the recipient will receive; @@ -734,7 +859,7 @@ fn handle_complete_transfer_token( messages.push(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.to_string(), msg: to_binary(&TokenMsg::Transfer { - recipient: info.sender.to_string(), + recipient: relayer_address.to_string(), amount: Uint128::from(fee), })?, funds: vec![], @@ -746,7 +871,9 @@ fn handle_complete_transfer_token( .add_attribute("action", "complete_transfer_native") .add_attribute("recipient", recipient) .add_attribute("contract", contract_addr) - .add_attribute("amount", amount.to_string())) + .add_attribute("amount", amount.to_string()) + .add_attribute("relayer", relayer_address) + .add_attribute("fee", fee.to_string())) } } @@ -756,9 +883,16 @@ fn handle_complete_transfer_token_native( info: MessageInfo, emitter_chain: u16, emitter_address: Vec, + transfer_type: TransferType<()>, data: &Vec, + relayer_address: &HumanAddr, ) -> StdResult { - let transfer_info = TransferInfo::deserialize(&data)?; + let transfer_info = match transfer_type { + TransferType::WithoutPayload => TransferInfo::deserialize(&data)?, + TransferType::WithPayload { payload: () } => { + TransferWithPayloadInfo::deserialize(&data)?.transfer_info + } + }; let expected_contract = bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?; @@ -775,6 +909,15 @@ fn handle_complete_transfer_token_native( } let target_address = (&transfer_info.recipient.as_slice()).get_address(0); + let recipient = deps.api.addr_humanize(&target_address)?; + + if let TransferType::WithPayload { payload: _ } = transfer_type { + if recipient != info.sender { + return Err(StdError::generic_err( + "transfers with payload can only be redeemed by the recipient", + )); + } + }; let (not_supported_amount, mut amount) = transfer_info.amount; let (not_supported_fee, fee) = transfer_info.fee; @@ -797,7 +940,6 @@ fn handle_complete_transfer_token_native( // note -- here the amount is the amount the recipient will receive; // amount + fee is the total sent - let recipient = deps.api.addr_humanize(&target_address)?; let token_address = (&*token_address).get_address(0); receive_native(deps.storage, &token_address, Uint128::new(amount + fee))?; @@ -808,7 +950,7 @@ fn handle_complete_transfer_token_native( if fee != 0 { messages.push(CosmosMsg::Bank(BankMsg::Send { - to_address: info.sender.to_string(), + to_address: relayer_address.to_string(), amount: coins_after_tax(deps, vec![coin(fee, &denom)])?, })); } @@ -818,7 +960,9 @@ fn handle_complete_transfer_token_native( .add_attribute("action", "complete_transfer_terra_native") .add_attribute("recipient", recipient) .add_attribute("denom", denom) - .add_attribute("amount", amount.to_string())) + .add_attribute("amount", amount.to_string()) + .add_attribute("relayer", relayer_address) + .add_attribute("fee", fee.to_string())) } fn handle_initiate_transfer( @@ -829,6 +973,7 @@ fn handle_initiate_transfer( recipient_chain: u16, recipient: Vec, fee: Uint128, + transfer_type: TransferType>, nonce: u32, ) -> StdResult { match asset.info { @@ -841,6 +986,7 @@ fn handle_initiate_transfer( recipient_chain, recipient, fee, + transfer_type, nonce, ), AssetInfo::NativeToken { ref denom } => handle_initiate_transfer_native_token( @@ -852,6 +998,7 @@ fn handle_initiate_transfer( recipient_chain, recipient, fee, + transfer_type, nonce, ), } @@ -866,6 +1013,7 @@ fn handle_initiate_transfer_token( recipient_chain: u16, recipient: Vec, mut fee: Uint128, + transfer_type: TransferType>, nonce: u32, ) -> StdResult { if recipient_chain == CHAIN_ID { @@ -907,7 +1055,7 @@ fn handle_initiate_transfer_token( let wrapped_token_info: WrappedAssetInfoResponse = deps.querier.custom_query(&request)?; asset_chain = wrapped_token_info.asset_chain; - asset_address = wrapped_token_info.asset_address.as_slice().to_vec(); + asset_address = wrapped_token_info.asset_address.into(); let transfer_info = TransferInfo { token_chain: asset_chain, @@ -918,9 +1066,19 @@ fn handle_initiate_transfer_token( fee: (0, fee.u128()), }; - let token_bridge_message = TokenBridgeMessage { - action: Action::TRANSFER, - payload: transfer_info.serialize(), + let token_bridge_message: TokenBridgeMessage = match transfer_type { + TransferType::WithoutPayload => TokenBridgeMessage { + action: Action::TRANSFER, + payload: transfer_info.serialize(), + }, + TransferType::WithPayload { payload } => TokenBridgeMessage { + action: Action::TRANSFER_WITH_PAYLOAD, + payload: TransferWithPayloadInfo { + transfer_info, + payload, + } + .serialize(), + }, }; messages.push(CosmosMsg::Wasm(WasmMsg::Execute { @@ -1006,13 +1164,29 @@ fn handle_initiate_transfer_token( // that there's no execution in progress. The reply handler takes // care of clearing out this temporary storage when done. assert!(wrapped_transfer_tmp(deps.storage).load().is_err()); + + let token_bridge_message: TokenBridgeMessage = match transfer_type { + TransferType::WithoutPayload => TokenBridgeMessage { + action: Action::TRANSFER, + payload: transfer_info.serialize(), + }, + TransferType::WithPayload { payload } => TokenBridgeMessage { + action: Action::TRANSFER_WITH_PAYLOAD, + payload: TransferWithPayloadInfo { + transfer_info, + payload, + } + .serialize(), + }, + }; + // Wrap up state to be captured by the submessage reply. wrapped_transfer_tmp(deps.storage).save(&TransferState { previous_balance: balance.balance.to_string(), account: info.sender.to_string(), token_address: asset, token_canonical: asset_canonical.clone(), - message: transfer_info.serialize(), + message: token_bridge_message.serialize(), multiplier: Uint128::new(multiplier).to_string(), nonce, })?; @@ -1056,6 +1230,7 @@ fn handle_initiate_transfer_native_token( recipient_chain: u16, recipient: Vec, fee: Uint128, + transfer_type: TransferType>, nonce: u32, ) -> StdResult { if recipient_chain == CHAIN_ID { @@ -1097,9 +1272,19 @@ fn handle_initiate_transfer_native_token( fee: (0, fee.u128()), }; - let token_bridge_message = TokenBridgeMessage { - action: Action::TRANSFER, - payload: transfer_info.serialize(), + let token_bridge_message: TokenBridgeMessage = match transfer_type { + TransferType::WithoutPayload => TokenBridgeMessage { + action: Action::TRANSFER, + payload: transfer_info.serialize(), + }, + TransferType::WithPayload { payload } => TokenBridgeMessage { + action: Action::TRANSFER_WITH_PAYLOAD, + payload: TransferWithPayloadInfo { + transfer_info, + payload, + } + .serialize(), + }, }; let sender = deps.api.addr_canonicalize(&info.sender.as_str())?; @@ -1128,11 +1313,12 @@ fn handle_initiate_transfer_native_token( } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::WrappedRegistry { chain, address } => { to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?) } + QueryMsg::TransferInfo { vaa } => to_binary(&query_transfer_info(deps, env, &vaa)?), } } @@ -1149,9 +1335,41 @@ pub fn query_wrapped_registry( } } -fn build_asset_id(chain: u16, address: &[u8]) -> Vec { - let mut asset_id: Vec = vec![]; - asset_id.extend_from_slice(&chain.to_be_bytes()); +fn query_transfer_info(deps: Deps, env: Env, vaa: &Binary) -> StdResult { + let cfg = config_read(deps.storage).load()?; + + let parsed = parse_vaa(deps, env.block.time.seconds(), vaa)?; + let data = parsed.payload; + + // check if vaa is from governance + if is_governance_emitter(&cfg, parsed.emitter_chain, &parsed.emitter_address) { + return ContractError::InvalidVAAAction.std_err(); + } + + let message = TokenBridgeMessage::deserialize(&data)?; + match message.action { + Action::ATTEST_META => ContractError::InvalidVAAAction.std_err(), + _ => { + let info = TransferWithPayloadInfo::deserialize(&message.payload)?; + let core = info.transfer_info; + + Ok(TransferInfoResponse { + amount: core.amount.1.into(), + token_address: core.token_address, + token_chain: core.token_chain, + recipient: core.recipient, + recipient_chain: core.recipient_chain, + fee: core.fee.1.into(), + payload: info.payload, + }) + } + } +} + +pub fn build_asset_id(chain: u16, address: &[u8]) -> Vec { + let chain = &chain.to_be_bytes(); + let mut asset_id = Vec::with_capacity(chain.len() + address.len()); + asset_id.extend_from_slice(chain); asset_id.extend_from_slice(address); let mut hasher = Keccak256::new(); @@ -1160,52 +1378,15 @@ fn build_asset_id(chain: u16, address: &[u8]) -> Vec { } // Produce a 20 byte asset "address" from a native terra denom. -fn build_native_id(denom: &str) -> Vec { - let mut asset_address: Vec = denom.clone().as_bytes().to_vec(); - asset_address.reverse(); - asset_address.extend(vec![0u8; 20 - denom.len()]); - asset_address.reverse(); - assert_eq!(asset_address.len(), 20); +pub fn build_native_id(denom: &str) -> Vec { + let n = denom.len(); + assert!(n < 20); + let mut asset_address = Vec::with_capacity(20); + asset_address.resize(20 - n, 0u8); + asset_address.extend_from_slice(denom.as_bytes()); asset_address } -#[cfg(test)] -mod tests { - use cosmwasm_std::{ - Binary, - StdResult, - }; - - #[test] - fn test_me() -> StdResult<()> { - let x = vec![ - 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 96u8, 180u8, 94u8, 195u8, 0u8, 0u8, - 0u8, 1u8, 0u8, 3u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 38u8, - 229u8, 4u8, 215u8, 149u8, 163u8, 42u8, 54u8, 156u8, 236u8, 173u8, 168u8, 72u8, 220u8, - 100u8, 90u8, 154u8, 159u8, 160u8, 215u8, 0u8, 91u8, 48u8, 44u8, 48u8, 44u8, 51u8, 44u8, - 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, - 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 55u8, 44u8, 52u8, - 54u8, 44u8, 50u8, 53u8, 53u8, 44u8, 53u8, 48u8, 44u8, 50u8, 52u8, 51u8, 44u8, 49u8, - 48u8, 54u8, 44u8, 49u8, 50u8, 50u8, 44u8, 49u8, 49u8, 48u8, 44u8, 49u8, 50u8, 53u8, - 44u8, 56u8, 56u8, 44u8, 55u8, 51u8, 44u8, 49u8, 56u8, 57u8, 44u8, 50u8, 48u8, 55u8, - 44u8, 49u8, 48u8, 52u8, 44u8, 56u8, 51u8, 44u8, 49u8, 49u8, 57u8, 44u8, 49u8, 50u8, - 55u8, 44u8, 49u8, 57u8, 50u8, 44u8, 49u8, 52u8, 55u8, 44u8, 56u8, 57u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 51u8, 44u8, 50u8, 51u8, 50u8, 44u8, 48u8, 44u8, 51u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, - 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 51u8, 44u8, 49u8, 49u8, - 54u8, 44u8, 52u8, 56u8, 44u8, 49u8, 49u8, 54u8, 44u8, 49u8, 52u8, 57u8, 44u8, 49u8, - 48u8, 56u8, 44u8, 49u8, 49u8, 51u8, 44u8, 56u8, 44u8, 48u8, 44u8, 50u8, 51u8, 50u8, - 44u8, 52u8, 57u8, 44u8, 49u8, 53u8, 50u8, 44u8, 49u8, 44u8, 50u8, 56u8, 44u8, 50u8, - 48u8, 51u8, 44u8, 50u8, 49u8, 50u8, 44u8, 50u8, 50u8, 49u8, 44u8, 50u8, 52u8, 49u8, - 44u8, 56u8, 53u8, 44u8, 49u8, 48u8, 57u8, 93u8, - ]; - let b = Binary::from(x.clone()); - let y = b.as_slice().to_vec(); - assert_eq!(x, y); - Ok(()) - } +fn is_governance_emitter(cfg: &ConfigInfo, emitter_chain: u16, emitter_address: &Vec) -> bool { + cfg.gov_chain == emitter_chain && cfg.gov_address == emitter_address.clone() } diff --git a/terra/contracts/token-bridge/src/lib.rs b/terra/contracts/token-bridge/src/lib.rs index f67a6e5e3..e4ccd9f8c 100644 --- a/terra/contracts/token-bridge/src/lib.rs +++ b/terra/contracts/token-bridge/src/lib.rs @@ -4,3 +4,6 @@ extern crate lazy_static; pub mod contract; pub mod msg; pub mod state; + +#[cfg(test)] +mod testing; diff --git a/terra/contracts/token-bridge/src/msg.rs b/terra/contracts/token-bridge/src/msg.rs index 4c60d4729..c08471aa5 100644 --- a/terra/contracts/token-bridge/src/msg.rs +++ b/terra/contracts/token-bridge/src/msg.rs @@ -44,6 +44,15 @@ pub enum ExecuteMsg { nonce: u32, }, + InitiateTransferWithPayload { + asset: Asset, + recipient_chain: u16, + recipient: Binary, + fee: Uint128, + payload: Binary, + nonce: u32, + }, + SubmitVaa { data: Binary, }, @@ -52,6 +61,11 @@ pub enum ExecuteMsg { asset_info: AssetInfo, nonce: u32, }, + + CompleteTransferWithPayload { + data: Binary, + relayer: HumanAddr, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -62,6 +76,7 @@ pub struct MigrateMsg {} #[serde(rename_all = "snake_case")] pub enum QueryMsg { WrappedRegistry { chain: u16, address: Binary }, + TransferInfo { vaa: Binary }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -72,6 +87,12 @@ pub struct WrappedRegistryResponse { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub enum WormholeQueryMsg { - VerifyVAA { vaa: Binary, block_time: u64 }, +pub struct TransferInfoResponse { + pub amount: Uint128, + pub token_address: Vec, + pub token_chain: u16, + pub recipient: Vec, + pub recipient_chain: u16, + pub fee: Uint128, + pub payload: Vec, } diff --git a/terra/contracts/token-bridge/src/state.rs b/terra/contracts/token-bridge/src/state.rs index 1142cea19..63e8b374a 100644 --- a/terra/contracts/token-bridge/src/state.rs +++ b/terra/contracts/token-bridge/src/state.rs @@ -147,6 +147,7 @@ pub struct Action; impl Action { pub const TRANSFER: u8 = 1; pub const ATTEST_META: u8 = 2; + pub const TRANSFER_WITH_PAYLOAD: u8 = 3; } // 0 u8 action @@ -225,6 +226,38 @@ impl TransferInfo { } } +// 0 u256 amount +// 32 [u8; 32] token_address +// 64 u16 token_chain +// 66 [u8; 32] recipient +// 98 u16 recipient_chain +// 100 u256 fee +// 132 [u8] payload + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TransferWithPayloadInfo { + pub transfer_info: TransferInfo, + pub payload: Vec, +} + +impl TransferWithPayloadInfo { + pub fn deserialize(data: &Vec) -> StdResult { + let transfer_info = TransferInfo::deserialize(data)?; + let payload = TransferWithPayloadInfo::get_payload(data); + + Ok(TransferWithPayloadInfo { + transfer_info, + payload, + }) + } + pub fn serialize(&self) -> Vec { + [self.transfer_info.serialize(), self.payload.clone()].concat() + } + pub fn get_payload(data: &Vec) -> Vec { + return data[132..].to_vec(); + } +} + // 0 [32]uint8 TokenAddress // 32 uint16 TokenChain // 34 uint8 Decimals diff --git a/terra/contracts/token-bridge/src/testing/mod.rs b/terra/contracts/token-bridge/src/testing/mod.rs new file mode 100644 index 000000000..14f00389d --- /dev/null +++ b/terra/contracts/token-bridge/src/testing/mod.rs @@ -0,0 +1 @@ +mod tests; diff --git a/terra/contracts/token-bridge/src/testing/tests.rs b/terra/contracts/token-bridge/src/testing/tests.rs new file mode 100644 index 000000000..84d4372ca --- /dev/null +++ b/terra/contracts/token-bridge/src/testing/tests.rs @@ -0,0 +1,197 @@ +use cosmwasm_std::{ + Binary, + StdResult, +}; + +use wormhole::state::ParsedVAA; + +use crate::{ + contract::{ + build_asset_id, + build_native_id, + }, + state::{ + Action, + TokenBridgeMessage, + TransferInfo, + TransferWithPayloadInfo, + }, +}; + +#[test] +fn binary_check() -> StdResult<()> { + let x = vec![ + 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 96u8, 180u8, 94u8, 195u8, 0u8, 0u8, 0u8, + 1u8, 0u8, 3u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 38u8, 229u8, + 4u8, 215u8, 149u8, 163u8, 42u8, 54u8, 156u8, 236u8, 173u8, 168u8, 72u8, 220u8, 100u8, 90u8, + 154u8, 159u8, 160u8, 215u8, 0u8, 91u8, 48u8, 44u8, 48u8, 44u8, 51u8, 44u8, 48u8, 44u8, + 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, + 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 55u8, 44u8, 52u8, 54u8, 44u8, 50u8, 53u8, + 53u8, 44u8, 53u8, 48u8, 44u8, 50u8, 52u8, 51u8, 44u8, 49u8, 48u8, 54u8, 44u8, 49u8, 50u8, + 50u8, 44u8, 49u8, 49u8, 48u8, 44u8, 49u8, 50u8, 53u8, 44u8, 56u8, 56u8, 44u8, 55u8, 51u8, + 44u8, 49u8, 56u8, 57u8, 44u8, 50u8, 48u8, 55u8, 44u8, 49u8, 48u8, 52u8, 44u8, 56u8, 51u8, + 44u8, 49u8, 49u8, 57u8, 44u8, 49u8, 50u8, 55u8, 44u8, 49u8, 57u8, 50u8, 44u8, 49u8, 52u8, + 55u8, 44u8, 56u8, 57u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, + 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, + 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, + 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, + 44u8, 48u8, 44u8, 48u8, 44u8, 51u8, 44u8, 50u8, 51u8, 50u8, 44u8, 48u8, 44u8, 51u8, 44u8, + 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, + 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 51u8, 44u8, 49u8, 49u8, 54u8, + 44u8, 52u8, 56u8, 44u8, 49u8, 49u8, 54u8, 44u8, 49u8, 52u8, 57u8, 44u8, 49u8, 48u8, 56u8, + 44u8, 49u8, 49u8, 51u8, 44u8, 56u8, 44u8, 48u8, 44u8, 50u8, 51u8, 50u8, 44u8, 52u8, 57u8, + 44u8, 49u8, 53u8, 50u8, 44u8, 49u8, 44u8, 50u8, 56u8, 44u8, 50u8, 48u8, 51u8, 44u8, 50u8, + 49u8, 50u8, 44u8, 50u8, 50u8, 49u8, 44u8, 50u8, 52u8, 49u8, 44u8, 56u8, 53u8, 44u8, 49u8, + 48u8, 57u8, 93u8, + ]; + let b = Binary::from(x.clone()); + let y: Vec = b.into(); + assert_eq!(x, y); + Ok(()) +} + +#[test] +fn build_native_and_asset_ids() -> StdResult<()> { + let denom = "uusd"; + let native_id = build_native_id(denom); + + let expected_native_id = vec![ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 117u8, + 117u8, 115u8, 100u8, + ]; + assert_eq!(&native_id, &expected_native_id, "native_id != expected"); + + // weth + let chain = 2u16; + let token_address = "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; + let token_address = hex::decode(token_address).unwrap(); + let asset_id = build_asset_id(chain, token_address.as_slice()); + + let expected_asset_id = vec![ + 171u8, 106u8, 233u8, 80u8, 14u8, 139u8, 124u8, 78u8, 181u8, 77u8, 142u8, 76u8, 109u8, 81u8, + 55u8, 100u8, 139u8, 159u8, 42u8, 85u8, 172u8, 234u8, 0u8, 114u8, 11u8, 82u8, 40u8, 40u8, + 50u8, 73u8, 211u8, 135u8, + ]; + assert_eq!(&asset_id, &expected_asset_id, "asset_id != expected"); + Ok(()) +} + +#[test] +fn deserialize_transfer_vaa() -> StdResult<()> { + let signed_vaa = "\ + 010000000001003f3179d5bb17b6f2ecc13741ca3f78d922043e99e09975e390\ + 4332d2418bb3f16d7ac93ca8401f8bed1cf9827bc806ecf7c5a283340f033bf4\ + 72724abf1d274f00000000000000000000010000000000000000000000000000\ + 00000000000000000000000000000000ffff0000000000000000000100000000\ + 00000000000000000000000000000000000000000000000005f5e10001000000\ + 0000000000000000000000000000000000000000000000007575736400030000\ + 00000000000000000000f7f7dde848e7450a029cd0a9bd9bdae4b5147db30003\ + 00000000000000000000000000000000000000000000000000000000000f4240"; + let signed_vaa = hex::decode(signed_vaa).unwrap(); + + let parsed = ParsedVAA::deserialize(signed_vaa.as_slice())?; + let message = TokenBridgeMessage::deserialize(&parsed.payload)?; + assert_eq!( + message.action, + Action::TRANSFER, + "message.action != expected" + ); + + let info = TransferInfo::deserialize(&message.payload)?; + + let amount = (0u128, 100_000_000u128); + assert_eq!(info.amount, amount, "info.amount != expected"); + + let token_address = "0100000000000000000000000000000000000000000000000000000075757364"; + let token_address = hex::decode(token_address).unwrap(); + assert_eq!( + info.token_address, token_address, + "info.token_address != expected" + ); + + let token_chain = 3u16; + assert_eq!( + info.token_chain, token_chain, + "info.token_chain != expected" + ); + + let recipient = "000000000000000000000000f7f7dde848e7450a029cd0a9bd9bdae4b5147db3"; + let recipient = hex::decode(recipient).unwrap(); + assert_eq!(info.recipient, recipient, "info.recipient != expected"); + + let recipient_chain = 3u16; + assert_eq!( + info.recipient_chain, recipient_chain, + "info.recipient_chain != expected" + ); + + let fee = (0u128, 1_000_000u128); + assert_eq!(info.fee, fee, "info.fee != expected"); + + Ok(()) +} + +#[test] +fn deserialize_transfer_with_payload_vaa() -> StdResult<()> { + let signed_vaa = "\ + 010000000001002b0e392ebe370e718b91dcafbba21094efd8e7f1f12e28bd90\ + a178b4dfbbc708675152a3cd2edd20e8e018600026b73b6c6cbf02622903409e\ + 8b48ab7fa30ef001000000010000000100010000000000000000000000000000\ + 00000000000000000000000000000000ffff0000000000000002000300000000\ + 00000000000000000000000000000000000000000000000005f5e10001000000\ + 0000000000000000000000000000000000000000000000007575736400030000\ + 000000000000000000008cec800d24df11e556e708461c98122df4a2c3b10003\ + 00000000000000000000000000000000000000000000000000000000000f4240\ + 416c6c20796f75722062617365206172652062656c6f6e6720746f207573"; + let signed_vaa = hex::decode(signed_vaa).unwrap(); + + let parsed = ParsedVAA::deserialize(signed_vaa.as_slice())?; + let message = TokenBridgeMessage::deserialize(&parsed.payload)?; + assert_eq!( + message.action, + Action::TRANSFER_WITH_PAYLOAD, + "message.action != expected" + ); + + let info_with_payload = TransferWithPayloadInfo::deserialize(&message.payload)?; + let info = info_with_payload.transfer_info; + + let amount = (0u128, 100_000_000u128); + assert_eq!(info.amount, amount, "info.amount != expected"); + + let token_address = "0100000000000000000000000000000000000000000000000000000075757364"; + let token_address = hex::decode(token_address).unwrap(); + assert_eq!( + info.token_address, token_address, + "info.token_address != expected" + ); + + let token_chain = 3u16; + assert_eq!( + info.token_chain, token_chain, + "info.token_chain != expected" + ); + + let recipient = "0000000000000000000000008cec800d24df11e556e708461c98122df4a2c3b1"; + let recipient = hex::decode(recipient).unwrap(); + assert_eq!(info.recipient, recipient, "info.recipient != expected"); + + let recipient_chain = 3u16; + assert_eq!( + info.recipient_chain, recipient_chain, + "info.recipient_chain != expected" + ); + + let fee = (0u128, 1_000_000u128); + assert_eq!(info.fee, fee, "info.fee != expected"); + + let transfer_payload = "All your base are belong to us"; + let transfer_payload = transfer_payload.as_bytes(); + assert_eq!( + info_with_payload.payload.as_slice(), + transfer_payload, + "info.payload != expected" + ); + + Ok(()) +} diff --git a/terra/contracts/wormhole/src/lib.rs b/terra/contracts/wormhole/src/lib.rs index 610d16ed2..4c573a1af 100644 --- a/terra/contracts/wormhole/src/lib.rs +++ b/terra/contracts/wormhole/src/lib.rs @@ -5,3 +5,6 @@ pub mod msg; pub mod state; pub use crate::error::ContractError; + +#[cfg(test)] +mod testing; diff --git a/terra/contracts/wormhole/src/state.rs b/terra/contracts/wormhole/src/state.rs index 49cc6d9d4..0f09f0a99 100644 --- a/terra/contracts/wormhole/src/state.rs +++ b/terra/contracts/wormhole/src/state.rs @@ -386,76 +386,3 @@ impl TransferFee { } } -#[cfg(test)] -mod tests { - use super::*; - - fn build_guardian_set(length: usize) -> GuardianSetInfo { - let mut addresses: Vec = Vec::with_capacity(length); - for _ in 0..length { - addresses.push(GuardianAddress { - bytes: vec![].into(), - }); - } - - GuardianSetInfo { - addresses, - expiration_time: 0, - } - } - - #[test] - fn quardian_set_quorum() { - assert_eq!(build_guardian_set(1).quorum(), 1); - assert_eq!(build_guardian_set(2).quorum(), 2); - assert_eq!(build_guardian_set(3).quorum(), 3); - assert_eq!(build_guardian_set(4).quorum(), 3); - assert_eq!(build_guardian_set(5).quorum(), 4); - assert_eq!(build_guardian_set(6).quorum(), 5); - assert_eq!(build_guardian_set(7).quorum(), 5); - assert_eq!(build_guardian_set(8).quorum(), 6); - assert_eq!(build_guardian_set(9).quorum(), 7); - assert_eq!(build_guardian_set(10).quorum(), 7); - assert_eq!(build_guardian_set(11).quorum(), 8); - assert_eq!(build_guardian_set(12).quorum(), 9); - assert_eq!(build_guardian_set(20).quorum(), 14); - assert_eq!(build_guardian_set(25).quorum(), 17); - assert_eq!(build_guardian_set(100).quorum(), 67); - } - - #[test] - fn test_deserialize() { - let x = hex::decode("080000000901007bfa71192f886ab6819fa4862e34b4d178962958d9b2e3d9437338c9e5fde1443b809d2886eaa69e0f0158ea517675d96243c9209c3fe1d94d5b19866654c6980000000b150000000500020001020304000000000000000000000000000000000000000000000000000000000000000000000a0261626364").unwrap(); - - let body = &x[ParsedVAA::HEADER_LEN + ParsedVAA::SIGNATURE_LEN..]; - let mut hasher = Keccak256::new(); - hasher.update(body); - let hash = hasher.finalize(); - - // Rehash the hash - let mut hasher = Keccak256::new(); - hasher.update(hash); - let hash = hasher.finalize().to_vec(); - - let v = ParsedVAA::deserialize(x.as_slice()).unwrap(); - assert_eq!( - v, - ParsedVAA { - version: 8, - guardian_set_index: 9, - timestamp: 2837, - nonce: 5, - len_signers: 1, - emitter_chain: 2, - emitter_address: vec![ - 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 - ], - sequence: 10, - consistency_level: 2, - payload: vec![97, 98, 99, 100], - hash, - } - ); - } -} diff --git a/terra/contracts/wormhole/src/testing/mod.rs b/terra/contracts/wormhole/src/testing/mod.rs new file mode 100644 index 000000000..b90ccf403 --- /dev/null +++ b/terra/contracts/wormhole/src/testing/mod.rs @@ -0,0 +1 @@ +mod tests; \ No newline at end of file diff --git a/terra/contracts/wormhole/src/testing/tests.rs b/terra/contracts/wormhole/src/testing/tests.rs new file mode 100644 index 000000000..60e1513f3 --- /dev/null +++ b/terra/contracts/wormhole/src/testing/tests.rs @@ -0,0 +1,162 @@ +use cosmwasm_std::StdResult; + +use crate::state::{GuardianAddress, GuardianSetInfo, ParsedVAA}; + +#[test] +fn quardian_set_quorum() { + let num_guardians_trials: Vec = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20, 25, 100, + ]; + + let expected_quorums: Vec = vec![ + 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 14, 17, 67, + ]; + + let make_guardian_set = |n: usize| -> GuardianSetInfo { + let mut addresses = Vec::with_capacity(n); + for _ in 0..n { + addresses.push( + GuardianAddress { + bytes: Vec::new().into(), + } + ); + } + GuardianSetInfo { + addresses, + expiration_time: 0, + } + }; + + for (i, &num_guardians) in num_guardians_trials.iter().enumerate() { + let quorum = make_guardian_set(num_guardians).quorum(); + assert_eq!(quorum, expected_quorums[i], "quorum != expected"); + } +} + +#[test] +fn deserialize_round_1() -> StdResult<()> { + let signed_vaa = "\ + 080000000901007bfa71192f886ab6819fa4862e34b4d178962958d9b2e3d943\ + 7338c9e5fde1443b809d2886eaa69e0f0158ea517675d96243c9209c3fe1d94d\ + 5b19866654c6980000000b150000000500020001020304000000000000000000\ + 000000000000000000000000000000000000000000000000000a0261626364"; + let signed_vaa = hex::decode(signed_vaa).unwrap(); + + let parsed = ParsedVAA::deserialize(signed_vaa.as_slice())?; + + let version = 8u8; + assert_eq!(parsed.version, version, "parsed.version != expected"); + + let guardian_set_index = 9u32; + assert_eq!(parsed.guardian_set_index, guardian_set_index, "parsed.guardian_set_index != expected"); + + let timestamp = 2837u32; + assert_eq!(parsed.timestamp, timestamp, "parsed.timestamp != expected"); + + let nonce = 5u32; + assert_eq!(parsed.nonce, nonce, "parsed.nonce != expected"); + + let len_signers = 1u8; + assert_eq!(parsed.len_signers, len_signers, "parsed.len_signers != expected"); + + let emitter_chain = 2u16; + assert_eq!(parsed.emitter_chain, emitter_chain, "parsed.emitter_chain != expected"); + + let emitter_address = "0001020304000000000000000000000000000000000000000000000000000000"; + let emitter_address = hex::decode(emitter_address).unwrap(); + assert_eq!(parsed.emitter_address, emitter_address, "parsed.emitter_address != expected"); + + let sequence = 10u64; + assert_eq!(parsed.sequence, sequence, "parsed.sequence != expected"); + + let consistency_level = 2u8; + assert_eq!(parsed.consistency_level, consistency_level, "parsed.consistency_level != expected"); + + let payload = vec![97u8, 98u8, 99u8, 100u8]; + assert_eq!(parsed.payload, payload, "parsed.payload != expected"); + + let hash = vec![ + 164u8, 44u8, 82u8, 103u8, 33u8, 170u8, 183u8, 178u8, + 188u8, 204u8, 35u8, 53u8, 78u8, 148u8, 160u8, 153u8, + 122u8, 252u8, 84u8, 211u8, 26u8, 204u8, 128u8, 215u8, + 37u8, 232u8, 222u8, 186u8, 222u8, 186u8, 98u8, 94u8, + ]; + assert_eq!(parsed.hash, hash, "parsed.hash != expected"); + + Ok(()) +} + +#[test] +fn deserialize_round_2() -> StdResult<()> { + let signed_vaa = "\ + 010000000001003f3179d5bb17b6f2ecc13741ca3f78d922043e99e09975e390\ + 4332d2418bb3f16d7ac93ca8401f8bed1cf9827bc806ecf7c5a283340f033bf4\ + 72724abf1d274f00000000000000000000010000000000000000000000000000\ + 00000000000000000000000000000000ffff0000000000000000000100000000\ + 00000000000000000000000000000000000000000000000005f5e10001000000\ + 0000000000000000000000000000000000000000000000007575736400030000\ + 00000000000000000000f7f7dde848e7450a029cd0a9bd9bdae4b5147db30003\ + 00000000000000000000000000000000000000000000000000000000000f4240"; + let signed_vaa = hex::decode(signed_vaa).unwrap(); + + let parsed = ParsedVAA::deserialize(signed_vaa.as_slice())?; + + let version = 1u8; + assert_eq!(parsed.version, version, "parsed.version != expected"); + + let guardian_set_index = 0u32; + assert_eq!(parsed.guardian_set_index, guardian_set_index, "parsed.guardian_set_index != expected"); + + let timestamp = 0u32; + assert_eq!(parsed.timestamp, timestamp, "parsed.timestamp != expected"); + + let nonce = 0u32; + assert_eq!(parsed.nonce, nonce, "parsed.nonce != expected"); + + let len_signers = 1u8; + assert_eq!(parsed.len_signers, len_signers, "parsed.len_signers != expected"); + + let emitter_chain = 1u16; + assert_eq!(parsed.emitter_chain, emitter_chain, "parsed.emitter_chain != expected"); + + let emitter_address = "000000000000000000000000000000000000000000000000000000000000ffff"; + let emitter_address = hex::decode(emitter_address).unwrap(); + assert_eq!(parsed.emitter_address, emitter_address, "parsed.emitter_address != expected"); + + let sequence = 0u64; + assert_eq!(parsed.sequence, sequence, "parsed.sequence != expected"); + + let consistency_level = 0u8; + assert_eq!(parsed.consistency_level, consistency_level, "parsed.consistency_level != expected"); + + let payload = vec![ + 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 5u8, 245u8, 225u8, + 0u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 117u8, 117u8, 115u8, + 100u8, 0u8, 3u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 247u8, + 247u8, 221u8, 232u8, 72u8, 231u8, 69u8, 10u8, 2u8, + 156u8, 208u8, 169u8, 189u8, 155u8, 218u8, 228u8, 181u8, + 20u8, 125u8, 179u8, 0u8, 3u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 15u8, 66u8, 64u8, + ]; + assert_eq!(parsed.payload, payload, "parsed.payload != expected"); + + let hash = vec![ + 114u8, 108u8, 111u8, 78u8, 204u8, 83u8, 150u8, 170u8, + 240u8, 15u8, 193u8, 176u8, 165u8, 87u8, 174u8, 230u8, + 94u8, 222u8, 106u8, 206u8, 179u8, 203u8, 193u8, 187u8, + 1u8, 148u8, 17u8, 40u8, 248u8, 214u8, 147u8, 68u8, + ]; + assert_eq!(parsed.hash, hash, "parsed.hash != expected"); + + Ok(()) +} \ No newline at end of file diff --git a/terra/test/.gitignore b/terra/test/.gitignore new file mode 100644 index 000000000..a65b41774 --- /dev/null +++ b/terra/test/.gitignore @@ -0,0 +1 @@ +lib diff --git a/terra/test/README.md b/terra/test/README.md index 9c8d8b4d8..bc0e30bb8 100644 --- a/terra/test/README.md +++ b/terra/test/README.md @@ -30,4 +30,4 @@ npm run test These tests are built using Jest and is meant to be structured very similarly to the [ethereum unit tests](../../ethereum), which requires running a local node via ganache before _truffle_ can run any of the testing scripts in the [test directory](../../ethereum/test). -**Currently the only test that exists is for the token bridge's transfer.** \ No newline at end of file +**Currently the only test that exists is for the token bridge's transfer and transfer with payload.** diff --git a/terra/test/src/__tests__/bridge.ts b/terra/test/src/__tests__/bridge.ts index 06d1e8079..a0f663908 100644 --- a/terra/test/src/__tests__/bridge.ts +++ b/terra/test/src/__tests__/bridge.ts @@ -1,15 +1,19 @@ import { describe, expect, jest, test } from "@jest/globals"; import { Bech32, toHex } from "@cosmjs/encoding"; +import { + getNativeBalance, + makeProviderAndWallet, + transactWithoutMemo, +} from "../helpers/client"; +import { storeCode, deploy } from "../instantiate"; import { Int, MsgExecuteContract } from "@terra-money/terra.js"; - -import { makeProviderAndWallet, transactWithoutMemo } from "../helpers/client"; import { makeGovernanceVaaPayload, makeTransferVaaPayload, signAndEncodeVaa, TEST_SIGNER_PKS, } from "../helpers/vaa"; -import { storeCode, deploy } from "../instantiate"; +import { computeGasPaid, parseEventsFromLog } from "../helpers/receipt"; jest.setTimeout(60000); @@ -24,7 +28,9 @@ const CONSISTENCY_LEVEL = 0; const WASM_WORMHOLE = "../artifacts/wormhole.wasm"; const WASM_WRAPPED_ASSET = "../artifacts/cw20_wrapped.wasm"; -const WASM_TOKEN_BRIDGE = "../artifacts/token_bridge.wasm"; +const WASM_TOKEN_BRIDGE = "../artifacts/token_bridge_terra.wasm"; +const WASM_MOCK_BRIDGE_INTEGRATION = + "../artifacts/mock_bridge_integration.wasm"; // global map of contract addresses for all tests const contracts = new Map(); @@ -42,10 +48,17 @@ const contracts = new Map(); > should deposit and log transfers correctly > should deposit and log fee token transfers correctly > should transfer out locked assets for a valid transfer vm + > should deposit and log transfer with payload correctly + > should transfer out locked assets for a valid transfer with payload vm > should mint bridged assets wrappers on transfer from another chain and handle fees correctly + > should handle additional data on token bridge transfer with payload in single transaction when feeRecipient == transferRecipient + > should not allow a redemption from msg.sender other than 'to' on token bridge transfer with payload + > should allow a redemption from msg.sender == 'to' on token bridge transfer with payload and check that sender recieves fee > should burn bridged assets wrappers on transfer to another chain > should handle ETH deposits correctly (uusd) > should handle ETH withdrawals and fees correctly (uusd) + > should handle ETH deposits with payload correctly (uusd) + > should handle ETH withdrawals with payload correctly (uusd) > should revert on transfer out of a total of > max(uint64) tokens */ @@ -92,8 +105,18 @@ describe("Bridge Tests", () => { wrapped_asset_code_id: wrappedAssetCodeId, }); + // mock bridge integration + const mockBridgeIntegration = await deploy( + client, + wallet, + WASM_MOCK_BRIDGE_INTEGRATION, + { + token_bridge_contract: tokenBridge, + } + ); contracts.set("wormhole", wormhole); contracts.set("tokenBridge", tokenBridge); + contracts.set("mockBridgeIntegration", mockBridgeIntegration); done(); } catch (e) { console.error(e); @@ -200,14 +223,11 @@ describe("Bridge Tests", () => { ); // check balances - let balanceBefore = new Int(0); - { - const [balance] = await client.bank.balance(tokenBridge); - const coin = balance.get(denom); - if (coin !== undefined) { - balanceBefore = new Int(coin.amount); - } - } + const balanceBefore = await getNativeBalance( + client, + tokenBridge, + denom + ); // execute outbound transfer const receipt = await transactWithoutMemo(client, wallet, [ @@ -216,17 +236,8 @@ describe("Bridge Tests", () => { ]); console.info("receipt", receipt.txhash); - let balanceAfter: Int; - { - const [balance] = await client.bank.balance(tokenBridge); - const coin = balance.get(denom); - expect(!coin).toBeFalsy(); - - balanceAfter = new Int(coin!.amount); - } - expect( - balanceBefore.add(new Int(amount)).eq(balanceAfter) - ).toBeTruthy(); + const balanceAfter = await getNativeBalance(client, tokenBridge, denom); + expect(balanceBefore.add(amount).eq(balanceAfter)).toBeTruthy(); done(); } catch (e) { @@ -247,18 +258,7 @@ describe("Bridge Tests", () => { const relayerFee = "1000000"; // one dolla const walletAddress = wallet.key.accAddress; - const recipient = "terra17lmam6zguazs5q5u6z5mmx76uj63gldnse2pdp"; - - // check balances - let balanceBefore = new Int(0); - { - const [balance] = await client.bank.balance(recipient); - const coin = balance.get(denom); - if (coin !== undefined) { - balanceBefore = new Int(coin.amount); - } - } - + const recipient = "terra17lmam6zguazs5q5u6z5mmx76uj63gldnse2pdp"; // test2 const encodedTo = nativeToHex(recipient); console.log("encodedTo", encodedTo); const ustAddress = @@ -292,28 +292,74 @@ describe("Bridge Tests", () => { ); console.info("signedVaa", signedVaa); + // check balances + const walletBalanceBefore = await getNativeBalance( + client, + walletAddress, + denom + ); + const recipientBalanceBefore = await getNativeBalance( + client, + recipient, + denom + ); + const bridgeBalanceBefore = await getNativeBalance( + client, + tokenBridge, + denom + ); + const submitVaa = new MsgExecuteContract(walletAddress, tokenBridge, { submit_vaa: { data: Buffer.from(signedVaa, "hex").toString("base64"), }, }); - // execute inbound transfer with signed vaa + // execute outbound transfer with signed vaa const receipt = await transactWithoutMemo(client, wallet, [submitVaa]); console.info("receipt", receipt.txhash); - let balanceAfter: Int; - { - const [balance] = await client.bank.balance(recipient); - const coin = balance.get(denom); - expect(!coin).toBeFalsy(); + // check wallet (relayer) balance change + const walletBalanceAfter = await getNativeBalance( + client, + walletAddress, + denom + ); + const gasPaid = computeGasPaid(receipt); + const walletExpectedChange = new Int(relayerFee).sub(gasPaid); - balanceAfter = new Int(coin!.amount); - } - const expectedAmount = (new Int(amount)).sub(relayerFee); + // due to rounding, we should expect the balances to reconcile + // within 1 unit (equivalent to 1e-6 uusd). Best-case scenario + // we end up with slightly more balance than expected + const reconciled = walletBalanceAfter + .minus(walletExpectedChange) + .minus(walletBalanceBefore); expect( - //balanceBefore.add(new Int(expectedAmount)).eq(balanceAfter) - balanceBefore.add(expectedAmount).eq(balanceAfter) + reconciled.greaterThanOrEqualTo("0") && + reconciled.lessThanOrEqualTo("1") + ).toBeTruthy(); + + const recipientBalanceAfter = await getNativeBalance( + client, + recipient, + denom + ); + const recipientExpectedChange = new Int(amount).sub(relayerFee); + expect( + recipientBalanceBefore + .add(recipientExpectedChange) + .eq(recipientBalanceAfter) + ).toBeTruthy(); + + // cehck bridge balance change + const bridgeExpectedChange = new Int(amount); + const bridgeBalanceAfter = await getNativeBalance( + client, + tokenBridge, + denom + ); + expect( + bridgeBalanceBefore.sub(bridgeExpectedChange).eq(bridgeBalanceAfter) ).toBeTruthy(); done(); @@ -323,6 +369,305 @@ describe("Bridge Tests", () => { } })(); }); + // transfer with payload tests + test("Initiate Transfer With Payload (native denom)", (done) => { + (async () => { + try { + const [client, wallet] = await makeProviderAndWallet(); + + const tokenBridge = contracts.get("tokenBridge")!; + + // transfer uusd + const denom = "uusd"; + const recipientAddress = + "0000000000000000000000004206942069420694206942069420694206942069"; + const amount = "100000000"; // one benjamin + const relayerFee = "1000000"; // one dolla + const myPayload = "ABC"; + + const walletAddress = wallet.key.accAddress; + + // need to deposit before initiating transfer + const deposit = new MsgExecuteContract( + wallet.key.accAddress, + tokenBridge, + { + deposit_tokens: {}, + }, + { [denom]: amount } + ); + + const initiateTransferWithPayload = new MsgExecuteContract( + walletAddress, + tokenBridge as string, + { + initiate_transfer_with_payload: { + asset: { + amount, + info: { + native_token: { + denom, + }, + }, + }, + recipient_chain: 2, + recipient: Buffer.from(recipientAddress, "hex").toString( + "base64" + ), + fee: relayerFee, + payload: Buffer.from(myPayload, "hex").toString("base64"), + nonce: 69, + }, + } + ); + + // check balances + const balanceBefore = await getNativeBalance( + client, + tokenBridge, + denom + ); + + // execute outbound transfer with payload + const receipt = await transactWithoutMemo(client, wallet, [ + deposit, + initiateTransferWithPayload, + ]); + console.info("receipt txHash", receipt.txhash); + + const balanceAfter = await getNativeBalance(client, tokenBridge, denom); + expect(balanceBefore.add(amount).eq(balanceAfter)).toBeTruthy(); + + done(); + } catch (e) { + console.error(e); + done("Failed to Initiate Transfer With Payload (native denom)"); + } + })(); + }); + test("Complete Transfer With Payload (native denom)", (done) => { + (async () => { + try { + const [client, wallet] = await makeProviderAndWallet(); + + const tokenBridge = contracts.get("tokenBridge")!; + const mockBridgeIntegration = contracts.get("mockBridgeIntegration")!; + + const denom = "uusd"; + const amount = "100000000"; // one benjamin + const relayerFee = "1000000"; // one dolla + + const walletAddress = wallet.key.accAddress; + + const encodedTo = nativeToHex(mockBridgeIntegration); + console.log("encodedTo", encodedTo); + const ustAddress = + "0100000000000000000000000000000000000000000000000000000075757364"; + const additionalPayload = "All your base are belong to us"; + + const vaaPayload = makeTransferVaaPayload( + 3, + amount, + ustAddress, + encodedTo, + 3, + relayerFee, + additionalPayload + ); + console.info("vaaPayload", vaaPayload); + + const timestamp = 1; + const nonce = 1; + const sequence = 2; + + const signedVaa = signAndEncodeVaa( + timestamp, + nonce, + FOREIGN_CHAIN, + FOREIGN_TOKEN_BRIDGE, + sequence, + vaaPayload, + TEST_SIGNER_PKS, + GUARDIAN_SET_INDEX, + CONSISTENCY_LEVEL + ); + console.info("signedVaa", signedVaa); + + // check balances before execute + const walletBalanceBefore = await getNativeBalance( + client, + walletAddress, + denom + ); + const contractBalanceBefore = await getNativeBalance( + client, + mockBridgeIntegration, + denom + ); + const bridgeBalanceBefore = await getNativeBalance( + client, + tokenBridge, + denom + ); + + const submitVaa = new MsgExecuteContract( + walletAddress, + mockBridgeIntegration, + { + complete_transfer_with_payload: { + data: Buffer.from(signedVaa, "hex").toString("base64"), + }, + } + ); + + // execute outbound transfer with signed vaa + const receipt = await transactWithoutMemo(client, wallet, [submitVaa]); + console.info("receipt txHash", receipt.txhash); + + // check wallet (relayer) balance change + const walletBalanceAfter = await getNativeBalance( + client, + walletAddress, + denom + ); + const gasPaid = computeGasPaid(receipt); + const walletExpectedChange = new Int(relayerFee).sub(gasPaid); + + // due to rounding, we should expect the balances to reconcile + // within 1 unit (equivalent to 1e-6 uusd). Best-case scenario + // we end up with slightly more balance than expected + const reconciled = walletBalanceAfter + .minus(walletExpectedChange) + .minus(walletBalanceBefore); + expect( + reconciled.greaterThanOrEqualTo("0") && + reconciled.lessThanOrEqualTo("1") + ).toBeTruthy(); + + // check contract balance change + const contractBalanceAfter = await getNativeBalance( + client, + mockBridgeIntegration, + denom + ); + const contractExpectedChange = new Int(amount).sub(relayerFee); + expect( + contractBalanceBefore + .add(contractExpectedChange) + .eq(contractBalanceAfter) + ).toBeTruthy(); + + // cehck bridge balance change + const bridgeExpectedChange = new Int(amount); + const bridgeBalanceAfter = await getNativeBalance( + client, + tokenBridge, + denom + ); + expect( + bridgeBalanceBefore.sub(bridgeExpectedChange).eq(bridgeBalanceAfter) + ).toBeTruthy(); + + // verify payload + const events = parseEventsFromLog(receipt); + const response: any[] = events.find((event) => { + return event.type == "wasm"; + }).attributes; + + const transferPayloadResponse = response.find((item) => { + return item.key == "transfer_payload"; + }); + expect( + Buffer.from(transferPayloadResponse.value, "base64").toString() + ).toEqual(additionalPayload); + + done(); + } catch (e) { + console.error(e); + done("Failed to Complete Transfer With Payload (native denom)"); + } + })(); + }); + test("Throw on Complete Transfer With Payload If Someone Else Redeems VAA", (done) => { + (async () => { + try { + const [client, wallet] = await makeProviderAndWallet(); + + const tokenBridge = contracts.get("tokenBridge")!; + const mockBridgeIntegration = contracts.get("mockBridgeIntegration")!; + + const denom = "uusd"; + const amount = "100000000"; // one benjamin + const relayerFee = "1000000"; // one dolla + + const walletAddress = wallet.key.accAddress; + + const encodedTo = nativeToHex(mockBridgeIntegration); + console.log("encodedTo", encodedTo); + const ustAddress = + "0100000000000000000000000000000000000000000000000000000075757364"; + const additionalPayload = "All your base are belong to us"; + + const vaaPayload = makeTransferVaaPayload( + 3, + amount, + ustAddress, + encodedTo, + 3, + relayerFee, + additionalPayload + ); + console.info("vaaPayload", vaaPayload); + + const timestamp = 1; + const nonce = 1; + const sequence = 3; + + const signedVaa = signAndEncodeVaa( + timestamp, + nonce, + FOREIGN_CHAIN, + FOREIGN_TOKEN_BRIDGE, + sequence, + vaaPayload, + TEST_SIGNER_PKS, + GUARDIAN_SET_INDEX, + CONSISTENCY_LEVEL + ); + console.info("signedVaa", signedVaa); + + let expectedErrorFound = false; + try { + const submitVaa = new MsgExecuteContract(walletAddress, tokenBridge, { + complete_transfer_with_payload: { + data: Buffer.from(signedVaa, "hex").toString("base64"), + relayer: walletAddress, + }, + }); + + // execute outbound transfer with signed vaa + const receipt = await transactWithoutMemo(client, wallet, [ + submitVaa, + ]); + console.info("receipt txHash", receipt.txhash); + } catch (e) { + const errorMsg: string = e.response.data.message; + expectedErrorFound = errorMsg.includes( + "transfers with payload can only be redeemed by the recipient" + ); + } + + expect(expectedErrorFound).toBeTruthy(); + + done(); + } catch (e) { + console.error(e); + done( + "Failed to Throw on Complete Transfer With Payload If Someone Else Redeems VAA" + ); + } + })(); + }); }); function nativeToHex(address: string) { diff --git a/terra/test/src/helpers/client.ts b/terra/test/src/helpers/client.ts index 760cbb354..d634a71a9 100644 --- a/terra/test/src/helpers/client.ts +++ b/terra/test/src/helpers/client.ts @@ -1,16 +1,23 @@ import { BlockTxBroadcastResult, + Int, LCDClient, MnemonicKey, Msg, Wallet, } from "@terra-money/terra.js"; +export const GAS_PRICE = 0.2; // uusd + export async function makeProviderAndWallet(): Promise<[LCDClient, Wallet]> { // provider const client = new LCDClient({ URL: "http://localhost:1317", chainID: "localterra", + gasAdjustment: "2", + gasPrices: { + uusd: GAS_PRICE, + }, }); // wallet @@ -48,3 +55,16 @@ export async function transactWithoutMemo( ): Promise { return transact(client, wallet, msgs, ""); } + +export async function getNativeBalance( + client: LCDClient, + address: string, + denom: string +): Promise { + const [balance] = await client.bank.balance(address); + const coin = balance.get(denom); + if (coin === undefined) { + return new Int(0); + } + return new Int(coin.amount); +} diff --git a/terra/test/src/helpers/receipt.ts b/terra/test/src/helpers/receipt.ts new file mode 100644 index 000000000..3dc7ce745 --- /dev/null +++ b/terra/test/src/helpers/receipt.ts @@ -0,0 +1,14 @@ +import { BlockTxBroadcastResult, Coin, Int } from "@terra-money/terra.js"; + +import { GAS_PRICE } from "./client"; + +export function parseEventsFromLog(receipt: BlockTxBroadcastResult): any[] { + return JSON.parse(receipt.raw_log)[0].events; +} + +export function computeGasPaid(receipt: BlockTxBroadcastResult): Int { + const gasPrice = new Coin("uusd", GAS_PRICE).amount; + // LocalTerra seems to spend all the gas_wanted + // instead of spending gas_used... + return new Int(gasPrice.mul(receipt.gas_wanted).ceil()); +} diff --git a/terra/tools/deploy.js b/terra/tools/deploy.js index 70497647c..0706e2733 100644 --- a/terra/tools/deploy.js +++ b/terra/tools/deploy.js @@ -17,12 +17,13 @@ import { zeroPad } from "ethers/lib/utils.js"; */ const artifacts = [ "wormhole.wasm", - "token_bridge.wasm", + "token_bridge_terra.wasm", "cw20_wrapped.wasm", "cw20_base.wasm", "nft_bridge.wasm", "cw721_wrapped.wasm", "cw721_base.wasm", + "mock_bridge_integration.wasm", ]; /* Check that the artifact folder contains all the wasm files we expect and nothing else */ @@ -166,7 +167,7 @@ addresses["wormhole.wasm"] = await instantiate("wormhole.wasm", { }, }); -addresses["token_bridge.wasm"] = await instantiate("token_bridge.wasm", { +addresses["token_bridge_terra.wasm"] = await instantiate("token_bridge_terra.wasm", { gov_chain: govChain, gov_address: Buffer.from(govAddress, "hex").toString("base64"), wormhole_contract: addresses["wormhole.wasm"], @@ -239,7 +240,7 @@ await mint_cw721( /* Registrations: tell the bridge contracts to know about each other */ const contract_registrations = { - "token_bridge.wasm": [ + "token_bridge_terra.wasm": [ // Solana process.env.REGISTER_SOL_TOKEN_BRIDGE_VAA, // Ethereum diff --git a/terra/tools/deploy_single.js b/terra/tools/deploy_single.js index c79ab5539..03417dba2 100644 --- a/terra/tools/deploy_single.js +++ b/terra/tools/deploy_single.js @@ -15,7 +15,7 @@ export const TERRA_GAS_PRICES_URL = "https://fcd.terra.dev/v1/txs/gas_prices"; const argv = yargs(hideBin(process.argv)) .option('network', { description: 'Which network to deploy to', - choices: ['mainnet', 'testnet', 'localterra'], + choices: ['mainnet', 'testnet', 'devnet'], required: true }) .option('artifact', { @@ -50,8 +50,7 @@ const terra_host = } : { URL: "http://localhost:1317", - chainID: "columbus-5", - name: "localterra", + chainID: "localterra", }; const lcd = new LCDClient(terra_host);