diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6babba45a..8f464c954 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,6 +25,10 @@ jobs: run: cargo make build-contracts - name: Test contracts run: cargo make test-contracts + - name: Build Aurora Eth Connector contract + run: cargo make build-aurora-eth-connector + - name: List Aurora Eth Connector contract + run: ls -la engine-tests-connector/etc/aurora-eth-connector/bin/aurora-eth-connector-test.wasm - name: Build mainnet test WASM run: cargo make --profile mainnet build-test - name: List mainnet WASM directory and root directory diff --git a/Cargo.lock b/Cargo.lock index 9a04f3a1e..7ff1b477d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,7 +288,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -299,13 +299,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -327,7 +327,7 @@ dependencies = [ [[package]] name = "aurora-engine" -version = "2.10.0" +version = "2.10.1" dependencies = [ "aurora-engine-modexp", "aurora-engine-precompiles", @@ -388,6 +388,18 @@ dependencies = [ "sha3", ] +[[package]] +name = "aurora-engine-standalone-nep141-legacy" +version = "0.1.0" +dependencies = [ + "aurora-engine", + "aurora-engine-modexp", + "aurora-engine-sdk", + "aurora-engine-types", + "serde", + "serde_json", +] + [[package]] name = "aurora-engine-test-doubles" version = "1.0.0" @@ -408,6 +420,7 @@ dependencies = [ "aurora-engine-modexp", "aurora-engine-precompiles", "aurora-engine-sdk", + "aurora-engine-standalone-nep141-legacy", "aurora-engine-test-doubles", "aurora-engine-transactions", "aurora-engine-types", @@ -428,6 +441,7 @@ dependencies = [ "near-crypto 0.17.0", "near-primitives 0.17.0", "near-primitives-core 0.17.0", + "near-sdk", "near-vm-errors 0.17.0", "near-vm-logic 0.17.0", "near-vm-runner", @@ -441,6 +455,24 @@ dependencies = [ "walrus", ] +[[package]] +name = "aurora-engine-tests-connector" +version = "1.0.0" +dependencies = [ + "anyhow", + "aurora-engine", + "aurora-engine-types", + "byte-slice-cast", + "ethabi", + "hex 0.4.3", + "near-sdk", + "near-units", + "rlp", + "serde", + "tokio", + "workspaces", +] + [[package]] name = "aurora-engine-transactions" version = "1.0.0" @@ -669,7 +701,7 @@ dependencies = [ "async-lock", "async-task", "atomic-waker", - "fastrand", + "fastrand 1.9.0", "futures-lite", "log", ] @@ -787,7 +819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", - "regex-automata 0.3.3", + "regex-automata 0.3.4", "serde", ] @@ -978,9 +1010,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.12" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -989,9 +1021,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.12" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -1008,7 +1040,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1322,9 +1354,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.63+curl-8.1.2" +version = "0.4.65+curl-8.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" +checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986" dependencies = [ "cc", "libc", @@ -1369,7 +1401,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1380,7 +1412,16 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.26", + "syn 2.0.28", +] + +[[package]] +name = "deranged" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01" +dependencies = [ + "serde", ] [[package]] @@ -1391,7 +1432,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1527,9 +1568,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" @@ -1548,6 +1589,7 @@ dependencies = [ "aurora-engine-modexp", "aurora-engine-precompiles", "aurora-engine-sdk", + "aurora-engine-standalone-nep141-legacy", "aurora-engine-transactions", "aurora-engine-types", "evm-core", @@ -1588,7 +1630,7 @@ checksum = "8560b409800a72d2d7860f8e5f4e0b0bd22bea6a352ea2a9ce30ccdef7f16d2f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1609,7 +1651,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1626,9 +1668,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -1687,7 +1729,7 @@ dependencies = [ "ethereum-types", "hash-db", "hash256-std-hasher", - "parity-scale-codec 3.6.3", + "parity-scale-codec 3.6.4", "rlp", "scale-info", "serde", @@ -1729,7 +1771,7 @@ dependencies = [ "evm-gasometer", "evm-runtime", "log", - "parity-scale-codec 3.6.3", + "parity-scale-codec 3.6.4", "primitive-types 0.12.1", "rlp", "scale-info", @@ -1742,7 +1784,7 @@ name = "evm-core" version = "0.39.0" source = "git+https://github.com/aurora-is-near/sputnikvm.git?tag=v0.38.0-aurora#c5cdf0c9bf8da9f6baeb1e5b4a52a2a3405e123f" dependencies = [ - "parity-scale-codec 3.6.3", + "parity-scale-codec 3.6.4", "primitive-types 0.12.1", "scale-info", "serde", @@ -1808,6 +1850,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "filetime" version = "0.2.21" @@ -1981,7 +2029,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -1998,7 +2046,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -2421,7 +2469,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.6.3", + "parity-scale-codec 3.6.4", ] [[package]] @@ -2698,9 +2746,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -3230,7 +3278,7 @@ dependencies = [ "smart-default", "strum", "thiserror", - "time 0.3.23", + "time 0.3.24", "tracing", ] @@ -3321,7 +3369,7 @@ checksum = "84c1eda300e2e78f4f945ae58117d49e806899f4a51ee2faa09eda5ebc2e6571" dependencies = [ "quote", "serde", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -3355,7 +3403,7 @@ dependencies = [ "fs2", "near-rpc-error-core 0.17.0", "serde", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -3698,9 +3746,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -3777,7 +3825,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -3868,15 +3916,15 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.3" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ "arrayvec 0.7.4", "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.6.3", + "parity-scale-codec-derive 3.6.4", "serde", ] @@ -3894,9 +3942,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.3" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -4020,7 +4068,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -4342,9 +4390,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -4529,7 +4577,7 @@ checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.3", + "regex-automata 0.3.4", "regex-syntax 0.7.4", ] @@ -4544,9 +4592,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" dependencies = [ "aho-corasick", "memchr", @@ -4802,7 +4850,7 @@ dependencies = [ "bitvec 1.0.1", "cfg-if 1.0.0", "derive_more", - "parity-scale-codec 3.6.3", + "parity-scale-codec 3.6.4", "scale-info-derive", ] @@ -4853,9 +4901,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "seahash" @@ -4903,9 +4951,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -4916,9 +4964,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4932,22 +4980,22 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -4963,9 +5011,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -4974,13 +5022,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -4997,9 +5045,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +checksum = "21e47d95bc83ed33b2ecf84f4187ad1ab9685d18ff28db000c99deac8ce180e3" dependencies = [ "base64 0.21.2", "chrono", @@ -5008,26 +5056,26 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.23", + "time 0.3.24", ] [[package]] name = "serde_with_macros" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +checksum = "ea3cee93715c2e266b9338b7544da68a9f24e227722ba482bd1c024367c77c65" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] name = "serde_yaml" -version = "0.9.23" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6075b41c7e3b079e5f246eb6094a44850d3a4c25a67c581c80796c80134012" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ "indexmap 2.0.0", "itoa", @@ -5087,9 +5135,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -5255,9 +5303,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -5295,21 +5343,20 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.9" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" +checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if 1.0.0", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.37.23", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -5350,22 +5397,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -5391,10 +5438,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -5409,9 +5457,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -5487,7 +5535,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -5695,7 +5743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ "crossbeam-channel", - "time 0.3.23", + "time 0.3.24", "tracing-subscriber", ] @@ -5707,7 +5755,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -5983,7 +6031,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -6017,7 +6065,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6568,9 +6616,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" dependencies = [ "memchr", ] @@ -6658,7 +6706,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fea7dc915..b31bb6c4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ aurora-engine-sdk = { path = "engine-sdk", default-features = false } aurora-engine-transactions = { path = "engine-transactions", default-features = false } aurora-engine-types = { path = "engine-types", default-features = false } aurora-engine-modexp = { path = "engine-modexp", default-features = false } +aurora-engine-standalone-nep141-legacy = { path = "engine-standalone-nep141-legacy", default-features = false } aurora-engine-test-doubles = { path = "engine-test-doubles" } aurora-engine-workspace = { path = "engine-workspace" } engine-standalone-storage = { path = "engine-standalone-storage" } @@ -74,9 +75,11 @@ members = [ "engine-modexp", "engine-precompiles", "engine-sdk", + "engine-standalone-nep141-legacy", "engine-standalone-storage", "engine-standalone-tracing", "engine-tests", + "engine-tests-connector", "engine-transactions", "engine-types", "engine-workspace", diff --git a/Makefile.toml b/Makefile.toml index b6b17c875..8a36af309 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -347,9 +347,31 @@ args = [ "--ignored", ] +[tasks.build-aurora-eth-connector] +category = "Build" +script = ''' +AURORA_ETH_CONNECTOR_DIR="engine-tests-connector/etc/aurora-eth-connector/" +AURORA_ETH_CONNECTOR_ETC="engine-tests-connector/etc/" +AURORA_ETH_CONNECTOR_REPO_NAME="aurora-eth-connector" +AURORA_ETH_CONNECTOR_REPO="https://github.com/aurora-is-near/"$AURORA_ETH_CONNECTOR_REPO_NAME +if [ -d $AURORA_ETH_CONNECTOR_DIR ]; then + cd $AURORA_ETH_CONNECTOR_DIR + echo "Pull Aurora Eth-Connector repo" + git pull origin master +else + mkdir $AURORA_ETH_CONNECTOR_ETC || true + cd $AURORA_ETH_CONNECTOR_ETC + echo "Clone Aurora Eth-Connector repo: "$AURORA_ETH_CONNECTOR_REPO + git clone --depth 1 $AURORA_ETH_CONNECTOR_REPO + cd $AURORA_ETH_CONNECTOR_REPO_NAME +fi +cargo make --profile mainnet build-test +''' + [tasks.test-flow] category = "Test" dependencies = [ + "build-aurora-eth-connector", "build-test", "test-contracts", "test-workspace", diff --git a/doc/eth-connector.md b/doc/eth-connector.md index c519c647f..8e0897a69 100644 --- a/doc/eth-connector.md +++ b/doc/eth-connector.md @@ -29,7 +29,6 @@ For more details see: [NEP-141](https://nomicon.io/Standards/Tokens/FungibleToke * ft_total_supply (view) * ft_total_eth_supply_on_near (view) -* ft_total_eth_supply_on_aurora (view) * ft_balance_of (view) * ft_balance_of_eth (view) * ft_transfer (mutable, payable) diff --git a/engine-precompiles/src/lib.rs b/engine-precompiles/src/lib.rs index 49f475a8a..2045b9e81 100644 --- a/engine-precompiles/src/lib.rs +++ b/engine-precompiles/src/lib.rs @@ -358,7 +358,10 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> Precompiles<'a, I, E, mut generic_precompiles: BTreeMap>, ctx: PrecompileConstructorContext<'a, I, E, H, M>, ) -> Self { + #[cfg(feature = "error_refund")] let near_exit = ExitToNear::new(ctx.current_account_id.clone(), ctx.io); + #[cfg(not(feature = "error_refund"))] + let near_exit = ExitToNear::new(ctx.io); let ethereum_exit = ExitToEthereum::new(ctx.current_account_id.clone(), ctx.io); let cross_contract_call = CrossContractCall::new(ctx.current_account_id, ctx.io); let predecessor_account_id = PredecessorAccount::new(ctx.env); diff --git a/engine-precompiles/src/native.rs b/engine-precompiles/src/native.rs index 272a76cc3..f1fa694ea 100644 --- a/engine-precompiles/src/native.rs +++ b/engine-precompiles/src/native.rs @@ -1,4 +1,5 @@ use super::{EvmPrecompileResult, Precompile}; +use crate::prelude::types::EthGas; use crate::prelude::{ format, parameters::{PromiseArgs, PromiseCreateArgs, WithdrawCallArgs}, @@ -12,9 +13,8 @@ use crate::prelude::{ parameters::{PromiseWithCallbackArgs, RefundCallArgs}, types, }; - -use crate::prelude::types::EthGas; use crate::PrecompileOutput; +use aurora_engine_types::storage::EthConnectorStorageId; use aurora_engine_types::{account_id::AccountId, types::NEP141Wei}; use evm::backend::Log; use evm::{Context, ExitError}; @@ -196,6 +196,7 @@ pub mod events { //TransferEthToNear pub struct ExitToNear { + #[cfg(feature = "error_refund")] current_account_id: AccountId, io: I, } @@ -211,6 +212,12 @@ pub mod exit_to_near { } impl ExitToNear { + #[cfg(not(feature = "error_refund"))] + pub const fn new(io: I) -> Self { + Self { io } + } + + #[cfg(feature = "error_refund")] pub const fn new(current_account_id: AccountId, io: I) -> Self { Self { current_account_id, @@ -235,6 +242,21 @@ fn get_nep141_from_erc20(erc20_token: &[u8], io: &I) -> Result(io: &I) -> Result { + io.read_storage(&construct_contract_key( + EthConnectorStorageId::EthConnectorAccount, + )) + .ok_or(ExitError::Other(Cow::Borrowed("ERR_KEY_NOT_FOUND"))) + .and_then(|x| { + x.to_value() + .map_err(|_| ExitError::Other(Cow::Borrowed("ERR_DESERIALIZE"))) + }) +} + +fn construct_contract_key(suffix: EthConnectorStorageId) -> Vec { + bytes_to_key(KeyPrefix::EthConnector, &[u8::from(suffix)]) +} + fn validate_amount(amount: U256) -> Result<(), ExitError> { if amount > U256::from(u128::MAX) { return Err(ExitError::Other(Cow::from("ERR_INVALID_AMOUNT"))); @@ -299,9 +321,8 @@ impl Precompile for ExitToNear { let (refund_address, mut input) = parse_input(input)?; #[cfg(not(feature = "error_refund"))] let mut input = parse_input(input)?; - let current_account_id = self.current_account_id.clone(); #[cfg(feature = "error_refund")] - let refund_on_error_target = current_account_id.clone(); + let refund_on_error_target = self.current_account_id.clone(); let (nep141_address, args, exit_event) = match flag { 0x0 => { @@ -310,13 +331,15 @@ impl Precompile for ExitToNear { // Input slice format: // recipient_account_id (bytes) - the NEAR recipient account which will receive NEP-141 ETH tokens + let eth_connector_contract_account = get_eth_connector_contract_account(&self.io)?; + if let Ok(dest_account) = AccountId::try_from(input) { ( - current_account_id, + eth_connector_contract_account, // There is no way to inject json, given the encoding of both arguments // as decimal and valid account id respectively. format!( - r#"{{"receiver_id": "{}", "amount": "{}", "memo": null}}"#, + r#"{{"receiver_id": "{}", "amount": "{}", "memo": null}}"#, dest_account, context.apparent_value.as_u128() ), @@ -379,7 +402,9 @@ impl Precompile for ExitToNear { ))); } } - _ => return Err(ExitError::Other(Cow::from("ERR_INVALID_FLAG"))), + _ => { + return Err(ExitError::Other(Cow::from("ERR_INVALID_FLAG"))); + } }; #[cfg(feature = "error_refund")] diff --git a/engine-standalone-nep141-legacy/Cargo.toml b/engine-standalone-nep141-legacy/Cargo.toml new file mode 100644 index 000000000..1e83b2001 --- /dev/null +++ b/engine-standalone-nep141-legacy/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "aurora-engine-standalone-nep141-legacy" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +description = "Aurora engine standalone NEP-141 legacy library. Provides the legacy NEP-141 code used by the standalone engine." +homepage.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true +autobenches = false + +[lib] +crate-type = ["lib"] + +[dependencies] +aurora-engine = { workspace = true, features = ["std"] } +aurora-engine-modexp = { workspace = true, features = ["std"] } +aurora-engine-types = { workspace = true, features = ["std"] } +aurora-engine-sdk = { workspace = true, features = ["std"] } +serde.workspace = true +serde_json.workspace = true + +[features] diff --git a/engine-standalone-nep141-legacy/src/admin_controlled.rs b/engine-standalone-nep141-legacy/src/admin_controlled.rs new file mode 100644 index 000000000..1197bd778 --- /dev/null +++ b/engine-standalone-nep141-legacy/src/admin_controlled.rs @@ -0,0 +1,137 @@ +use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; + +pub type PausedMask = u8; + +pub const ERR_PAUSED: &str = "ERR_PAUSED"; + +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)] +pub struct PauseEthConnectorCallArgs { + pub paused_mask: PausedMask, +} + +pub trait AdminControlled { + /// Return the current mask representing all paused events. + fn get_paused(&self) -> PausedMask; + + /// Update mask with all paused events. + /// Implementor is responsible for guaranteeing that this function can only be + /// called by owner of the contract. + fn set_paused(&mut self, paused: PausedMask); + + /// Return if the contract is paused for the current flag and user + fn is_paused(&self, flag: PausedMask, is_owner: bool) -> bool { + (self.get_paused() & flag) != 0 && !is_owner + } + + /// Asserts the passed paused flag is not set. Returns `PausedError` if paused. + fn assert_not_paused(&self, flag: PausedMask, is_owner: bool) -> Result<(), PausedError> { + if self.is_paused(flag, is_owner) { + Err(PausedError) + } else { + Ok(()) + } + } +} + +pub struct PausedError; + +impl AsRef<[u8]> for PausedError { + fn as_ref(&self) -> &[u8] { + ERR_PAUSED.as_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct MockAdminControlled { + mask: PausedMask, + } + + impl MockAdminControlled { + pub fn new() -> Self { + Self { mask: 0 } + } + } + + impl AdminControlled for MockAdminControlled { + fn get_paused(&self) -> PausedMask { + self.mask + } + + fn set_paused(&mut self, paused: PausedMask) { + self.mask = paused + } + } + + #[test] + fn test_setting_paused_mask_with_1_bit_marks_it_as_paused() { + let is_owner = false; + let mask = 1u8; + let mut admin_controlled = MockAdminControlled::new(); + + assert!(!admin_controlled.is_paused(mask, is_owner)); + admin_controlled.set_paused(mask); + assert!(admin_controlled.is_paused(mask, is_owner)); + } + + #[test] + fn test_setting_paused_mask_with_0_bit_marks_it_as_not_paused() { + let is_owner = false; + let mask = 1u8; + let mut admin_controlled = MockAdminControlled::new(); + admin_controlled.set_paused(mask); + + assert!(admin_controlled.is_paused(mask, is_owner)); + admin_controlled.set_paused(0u8); + assert!(!admin_controlled.is_paused(mask, is_owner)); + } + + #[test] + fn test_setting_paused_mask_with_1_bit_fails_to_assert_not_paused() { + let is_owner = false; + let mask = 1u8; + let admin_controlled = MockAdminControlled::new(); + + let result = admin_controlled.assert_not_paused(mask, is_owner); + assert!(result.is_ok(), "asserting as paused failed"); + } + + #[test] + fn test_setting_paused_mask_with_0_bit_asserts_not_paused() { + let is_owner = false; + let mask = 1u8; + let mut admin_controlled = MockAdminControlled::new(); + + admin_controlled.set_paused(mask); + let error = admin_controlled + .assert_not_paused(mask, is_owner) + .expect_err("asserting as not paused failed"); + + let expected_error_message = b"ERR_PAUSED"; + let actual_error_message = error.as_ref(); + assert_eq!(expected_error_message, actual_error_message); + } + + #[test] + fn test_paused_mask_has_no_effect_on_owner() { + let is_owner = true; + let mask = 1u8; + let mut admin_controlled = MockAdminControlled::new(); + + admin_controlled.set_paused(mask); + assert!(!admin_controlled.is_paused(mask, is_owner)); + } + + #[test] + fn test_asserting_paused_mask_has_no_effect_on_owner() { + let is_owner = true; + let mask = 1u8; + let mut admin_controlled = MockAdminControlled::new(); + + admin_controlled.set_paused(mask); + let result = admin_controlled.assert_not_paused(mask, is_owner); + assert!(result.is_ok(), "asserting as not paused failed"); + } +} diff --git a/engine/src/fungible_token.rs b/engine-standalone-nep141-legacy/src/fungible_token.rs similarity index 93% rename from engine/src/fungible_token.rs rename to engine-standalone-nep141-legacy/src/fungible_token.rs index 7570e1318..faacb032c 100644 --- a/engine/src/fungible_token.rs +++ b/engine-standalone-nep141-legacy/src/fungible_token.rs @@ -1,17 +1,22 @@ -use crate::connector::ZERO_ATTACHED_BALANCE; -use crate::engine; -use crate::parameters::{NEP141FtOnTransferArgs, ResolveTransferCallArgs, StorageBalance}; -use crate::prelude::account_id::AccountId; -use crate::prelude::Wei; -use crate::prelude::{ - sdk, storage, vec, Address, Balance, BorshDeserialize, BorshSerialize, NearGas, PromiseAction, - PromiseBatchAction, PromiseCreateArgs, PromiseResult, PromiseWithCallbackArgs, - StorageBalanceBounds, StorageUsage, String, ToString, Vec, +use crate::legacy_connector::ZERO_ATTACHED_BALANCE; +use aurora_engine::{ + engine, + parameters::{NEP141FtOnTransferArgs, ResolveTransferCallArgs, StorageBalance}, }; +use aurora_engine_sdk as sdk; use aurora_engine_sdk::io::{StorageIntermediate, IO}; -use aurora_engine_types::borsh; -pub use aurora_engine_types::parameters::connector::FungibleTokenMetadata; -use aurora_engine_types::types::{NEP141Wei, Yocto, ZERO_NEP141_WEI, ZERO_YOCTO}; +use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; +use aurora_engine_types::{ + account_id::AccountId, + parameters::{PromiseAction, PromiseBatchAction, PromiseCreateArgs, PromiseWithCallbackArgs}, + storage, + types::{ + Address, Balance, NEP141Wei, NearGas, PromiseResult, StorageBalanceBounds, StorageUsage, + Wei, Yocto, ZERO_NEP141_WEI, ZERO_YOCTO, + }, + vec, String, ToString, Vec, +}; +use serde::{Deserialize, Serialize}; /// Gas for `resolve_transfer`: 5 `TGas` const GAS_FOR_RESOLVE_TRANSFER: NearGas = NearGas::new(5_000_000_000_000); @@ -56,6 +61,25 @@ pub struct FungibleTokenOps { io: I, } +/// Fungible token Reference hash type. +/// Used for `FungibleTokenMetadata` +#[derive(Debug, BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct FungibleReferenceHash([u8; 32]); + +impl FungibleReferenceHash { + /// Encode to base64-encoded string + #[must_use] + pub fn encode(&self) -> String { + aurora_engine_sdk::base64::encode(self) + } +} + +impl AsRef<[u8]> for FungibleReferenceHash { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + impl FungibleTokenOps { pub fn new(io: I) -> Self { FungibleToken::default().ops(io) @@ -488,7 +512,7 @@ impl FungibleTokenOps { storage::bytes_to_key( storage::KeyPrefix::EthConnector, &[u8::from( - crate::prelude::EthConnectorStorageId::StatisticsAuroraAccountsCounter, + aurora_engine_types::storage:: EthConnectorStorageId::StatisticsAuroraAccountsCounter, )], ) } @@ -500,14 +524,14 @@ impl FungibleTokenOps { .read_u64(&key) .unwrap_or(0) .checked_add(1) - .expect(crate::errors::ERR_ACCOUNTS_COUNTER_OVERFLOW); + .expect(aurora_engine::errors::ERR_ACCOUNTS_COUNTER_OVERFLOW); self.io.write_storage(&key, &accounts_counter.to_le_bytes()); } } pub mod error { - use crate::errors; - use crate::prelude::types::balance::error::BalanceOverflowError; + use aurora_engine::errors; + use aurora_engine_types::types::balance::error::BalanceOverflowError; const TOTAL_SUPPLY_OVERFLOW: &[u8; 25] = errors::ERR_TOTAL_SUPPLY_OVERFLOW; const BALANCE_OVERFLOW: &[u8; 20] = errors::ERR_BALANCE_OVERFLOW; diff --git a/engine-standalone-nep141-legacy/src/legacy_connector.rs b/engine-standalone-nep141-legacy/src/legacy_connector.rs new file mode 100644 index 000000000..1af46af41 --- /dev/null +++ b/engine-standalone-nep141-legacy/src/legacy_connector.rs @@ -0,0 +1,900 @@ +use crate::admin_controlled::{AdminControlled, PauseEthConnectorCallArgs, PausedMask}; +use crate::fungible_token::{self, FungibleToken, FungibleTokenOps}; +use aurora_engine::connector::proof_key; +use aurora_engine::{ + deposit_event::{DepositedEvent, FtTransferMessageData, TokenMessageData}, + engine::Engine, + parameters::{ + BalanceOfCallArgs, BalanceOfEthCallArgs, FinishDepositCallArgs, InitCallArgs, + NEP141FtOnTransferArgs, ResolveTransferCallArgs, SetContractDataCallArgs, + StorageBalanceOfCallArgs, StorageDepositCallArgs, StorageWithdrawCallArgs, + TransferCallArgs, TransferCallCallArgs, WithdrawResult, + }, + proof::Proof, +}; +use aurora_engine_modexp::ModExpAlgorithm; +use aurora_engine_sdk as sdk; +use aurora_engine_sdk::{ + env::Env, + io::{StorageIntermediate, IO}, +}; +use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; +use aurora_engine_types::parameters::connector::FungibleTokenMetadata; +use aurora_engine_types::{ + account_id::AccountId, + format, + parameters::{ + PromiseBatchAction, PromiseCreateArgs, PromiseWithCallbackArgs, WithdrawCallArgs, + }, + storage::{EthConnectorStorageId, KeyPrefix}, + str, + types::{ + address::error::AddressError, Address, NEP141Wei, NearGas, PromiseResult, Wei, Yocto, + ERR_FAILED_PARSE, ZERO_NEP141_WEI, ZERO_WEI, + }, + ToString, Vec, U256, +}; + +pub const ERR_NOT_ENOUGH_BALANCE_FOR_FEE: &str = "ERR_NOT_ENOUGH_BALANCE_FOR_FEE"; +/// Indicate zero attached balance for promise call +pub const ZERO_ATTACHED_BALANCE: Yocto = Yocto::new(0); +/// NEAR Gas for calling `fininsh_deposit` promise. Used in the `deposit` logic. +pub const GAS_FOR_FINISH_DEPOSIT: NearGas = NearGas::new(50_000_000_000_000); +/// NEAR Gas for calling `verify_log_entry` promise. Used in the `deposit` logic. +// Note: Is 40Tgas always enough? +const GAS_FOR_VERIFY_LOG_ENTRY: NearGas = NearGas::new(40_000_000_000_000); + +/// Admin control flow flag indicates that all control flow unpause (unblocked). +pub const UNPAUSE_ALL: PausedMask = 0; +/// Admin control flow flag indicates that the deposit is paused. +pub const PAUSE_DEPOSIT: PausedMask = 1 << 0; +/// Admin control flow flag indicates that withdrawal is paused. +pub const PAUSE_WITHDRAW: PausedMask = 1 << 1; + +/// Eth-connector contract data. It's stored in the storage. +/// Contains: +/// * connector specific data +/// * Fungible token data +/// * paused_mask - admin control flow data +/// * io - I/O trait handler +pub struct EthConnectorContract { + contract: EthConnector, + ft: FungibleTokenOps, + paused_mask: PausedMask, + io: I, +} + +/// Connector specific data. It always should contain `prover account` - +#[derive(BorshSerialize, BorshDeserialize)] +pub struct EthConnector { + /// It used in the Deposit flow, to verify log entry form incoming proof. + pub prover_account: AccountId, + /// It is Eth address, used in the Deposit and Withdraw logic. + pub eth_custodian_address: Address, +} + +impl EthConnectorContract { + /// Init Eth-connector contract instance. + /// Load contract data from storage and init I/O handler. + /// Used as single point of contract access for various contract actions + pub fn init_instance(io: I) -> Result { + Ok(Self { + contract: get_contract_data(&io, &EthConnectorStorageId::Contract)?, + ft: get_contract_data::(&io, &EthConnectorStorageId::FungibleToken)? + .ops(io), + paused_mask: get_contract_data(&io, &EthConnectorStorageId::PausedMask) + .unwrap_or(UNPAUSE_ALL), + io, + }) + } + + /// Create contract data - init eth-connector contract specific data. + /// Used only once for first time initialization. + /// Initialized contract data stored in the storage. + pub fn create_contract( + mut io: I, + owner_id: AccountId, + args: InitCallArgs, + ) -> Result<(), error::InitContractError> { + // Check is it already initialized + let contract_key_exists = + io.storage_has_key(&construct_contract_key(&EthConnectorStorageId::Contract)); + if contract_key_exists { + return Err(error::InitContractError::AlreadyInitialized); + } + + sdk::log!("[init contract]"); + + let contract_data = set_contract_data( + &mut io, + SetContractDataCallArgs { + prover_account: args.prover_account, + eth_custodian_address: args.eth_custodian_address, + metadata: args.metadata, + }, + ) + .map_err(error::InitContractError::InvalidCustodianAddress)?; + + let mut ft = FungibleTokenOps::new(io); + // Register FT account for current contract + ft.internal_register_account(&owner_id); + + let paused_mask = UNPAUSE_ALL; + io.write_borsh( + &construct_contract_key(&EthConnectorStorageId::PausedMask), + &paused_mask, + ); + + Self { + contract: contract_data, + ft, + paused_mask, + io, + } + .save_ft_contract(); + + Ok(()) + } + + /// Deposit all types of tokens + pub fn deposit( + &self, + raw_proof: Vec, + current_account_id: AccountId, + predecessor_account_id: AccountId, + ) -> Result { + // Check is current account owner + let is_owner = current_account_id == predecessor_account_id; + // Check is current flow paused. If it's owner account just skip it. + self.assert_not_paused(PAUSE_DEPOSIT, is_owner) + .map_err(|_| error::DepositError::Paused)?; + + sdk::log!("[Deposit tokens]"); + + // Get incoming deposit arguments + let proof: Proof = + Proof::try_from_slice(&raw_proof).map_err(|_| error::DepositError::ProofParseFailed)?; + // Fetch event data from Proof + let event = DepositedEvent::from_log_entry_data(&proof.log_entry_data) + .map_err(error::DepositError::EventParseFailed)?; + + sdk::log!( + "Deposit started: from {} to recipient {:?} with amount: {:?} and fee {:?}", + event.sender.encode(), + event.token_message_data.recipient(), + event.amount, + event.fee + ); + + sdk::log!( + "Event's address {}, custodian address {}", + event.eth_custodian_address.encode(), + self.contract.eth_custodian_address.encode(), + ); + + if event.eth_custodian_address != self.contract.eth_custodian_address { + return Err(error::DepositError::CustodianAddressMismatch); + } + + if NEP141Wei::new(event.fee.as_u128()) >= event.amount { + return Err(error::DepositError::InsufficientAmountForFee); + } + + // Verify proof data with cross-contract call to prover account + sdk::log!( + "Deposit verify_log_entry for prover: {}", + self.contract.prover_account, + ); + + // Do not skip bridge call. This is only used for development and diagnostics. + let skip_bridge_call = false.try_to_vec().unwrap(); + let mut proof_to_verify = raw_proof; + proof_to_verify.extend(skip_bridge_call); + + let verify_call = PromiseCreateArgs { + target_account_id: self.contract.prover_account.clone(), + method: "verify_log_entry".to_string(), + args: proof_to_verify, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: GAS_FOR_VERIFY_LOG_ENTRY, + }; + + // Finalize deposit + let data = match event.token_message_data { + // Deposit to NEAR accounts + TokenMessageData::Near(account_id) => FinishDepositCallArgs { + new_owner_id: account_id, + amount: event.amount, + proof_key: proof_key(&proof), + relayer_id: predecessor_account_id, + fee: event.fee, + msg: None, + } + .try_to_vec() + .unwrap(), + // Deposit to Eth accounts + // fee is being minted in the `ft_on_transfer` callback method + TokenMessageData::Eth { + receiver_id, + message, + } => { + // Transfer to self and then transfer ETH in `ft_on_transfer` + // address - is NEAR account + let transfer_data = TransferCallCallArgs { + receiver_id, + amount: event.amount, + memo: None, + msg: message.encode(), + } + .try_to_vec() + .unwrap(); + + // Send to self - current account id + FinishDepositCallArgs { + new_owner_id: current_account_id.clone(), + amount: event.amount, + proof_key: proof_key(&proof), + relayer_id: predecessor_account_id, + fee: event.fee, + msg: Some(transfer_data), + } + .try_to_vec() + .unwrap() + } + }; + + let finish_call = PromiseCreateArgs { + target_account_id: current_account_id, + method: "finish_deposit".to_string(), + args: data, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: GAS_FOR_FINISH_DEPOSIT, + }; + Ok(PromiseWithCallbackArgs { + base: verify_call, + callback: finish_call, + }) + } + + /// Finish deposit (private method) + /// NOTE: we should `record_proof` only after `mint` operation. The reason + /// is that in this case we only calculate the amount to be credited but + /// do not save it, however, if an error occurs during the calculation, + /// this will happen before `record_proof`. After that contract will save. + pub fn finish_deposit( + &mut self, + predecessor_account_id: AccountId, + current_account_id: AccountId, + data: FinishDepositCallArgs, + prepaid_gas: NearGas, + ) -> Result, error::FinishDepositError> { + sdk::log!("Finish deposit with the amount: {}", data.amount); + + // Mint tokens to recipient minus fee + if let Some(msg) = data.msg { + // Mint - calculate new balances + self.mint_eth_on_near(data.new_owner_id, data.amount)?; + // Store proof only after `mint` calculations + self.record_proof(&data.proof_key)?; + // Save new contract data + self.save_ft_contract(); + let transfer_call_args = TransferCallCallArgs::try_from_slice(&msg).unwrap(); + let promise = self.ft_transfer_call( + predecessor_account_id, + current_account_id, + transfer_call_args, + prepaid_gas, + )?; + Ok(Some(promise)) + } else { + // Mint - calculate new balances + self.mint_eth_on_near( + data.new_owner_id.clone(), + data.amount - NEP141Wei::new(data.fee.as_u128()), + )?; + self.mint_eth_on_near(data.relayer_id, NEP141Wei::new(data.fee.as_u128()))?; + // Store proof only after `mint` calculations + self.record_proof(&data.proof_key)?; + // Save new contract data + self.save_ft_contract(); + Ok(None) + } + } + + /// Internal ETH withdraw ETH logic + #[allow(dead_code)] + pub(crate) fn internal_remove_eth( + &mut self, + amount: Wei, + ) -> Result<(), fungible_token::error::WithdrawError> { + self.burn_eth_on_aurora(amount)?; + self.save_ft_contract(); + Ok(()) + } + + /// Record used proof as hash key + fn record_proof(&mut self, key: &str) -> Result<(), error::ProofUsed> { + sdk::log!("Record proof: {}", key); + + if self.is_used_event(key) { + return Err(error::ProofUsed); + } + + self.save_used_event(key); + Ok(()) + } + + /// Mint nETH tokens + fn mint_eth_on_near( + &mut self, + owner_id: AccountId, + amount: NEP141Wei, + ) -> Result<(), fungible_token::error::DepositError> { + sdk::log!("Mint {} nETH tokens for: {}", amount, owner_id); + + if self.ft.get_account_eth_balance(&owner_id).is_none() { + self.ft.accounts_insert(&owner_id, ZERO_NEP141_WEI); + } + self.ft.internal_deposit_eth_to_near(&owner_id, amount) + } + + /// Mint ETH tokens + fn mint_eth_on_aurora( + &mut self, + owner_id: Address, + amount: Wei, + ) -> Result<(), fungible_token::error::DepositError> { + sdk::log!("Mint {} ETH tokens for: {}", amount, owner_id.encode()); + self.ft.internal_deposit_eth_to_aurora(owner_id, amount) + } + + /// Burn ETH tokens + fn burn_eth_on_aurora( + &mut self, + amount: Wei, + ) -> Result<(), fungible_token::error::WithdrawError> { + self.ft.internal_withdraw_eth_from_aurora(amount) + } + + /// Withdraw nETH from NEAR accounts + /// NOTE: it should be without any log data + pub fn withdraw_eth_from_near( + &mut self, + current_account_id: &AccountId, + predecessor_account_id: &AccountId, + args: &WithdrawCallArgs, + ) -> Result { + // Check is current account id is owner + let is_owner = current_account_id == predecessor_account_id; + // Check is current flow paused. If it's owner just skip asserrion. + self.assert_not_paused(PAUSE_WITHDRAW, is_owner) + .map_err(|_| error::WithdrawError::Paused)?; + + // Burn tokens to recipient + self.ft + .internal_withdraw_eth_from_near(predecessor_account_id, args.amount)?; + // Save new contract data + self.save_ft_contract(); + + Ok(WithdrawResult { + recipient_id: args.recipient_address, + amount: args.amount, + eth_custodian_address: self.contract.eth_custodian_address, + }) + } + + /// Returns total ETH supply on NEAR (nETH as NEP-141 token) + pub fn ft_total_eth_supply_on_near(&mut self) { + let total_supply = self.ft.ft_total_eth_supply_on_near(); + sdk::log!("Total ETH supply on NEAR: {}", total_supply); + self.io + .return_output(format!("\"{}\"", total_supply).as_bytes()); + } + + /// Returns total ETH supply on Aurora (ETH in Aurora EVM) + pub fn ft_total_eth_supply_on_aurora(&mut self) { + let total_supply = self.ft.ft_total_eth_supply_on_aurora(); + sdk::log!("Total ETH supply on Aurora: {}", total_supply); + self.io + .return_output(format!("\"{}\"", total_supply).as_bytes()); + } + + /// Return balance of nETH (ETH on Near) + pub fn ft_balance_of(&mut self, args: BalanceOfCallArgs) { + let balance = self.ft.ft_balance_of(&args.account_id); + sdk::log!("Balance of nETH [{}]: {}", args.account_id, balance); + + self.io.return_output(format!("\"{}\"", balance).as_bytes()); + } + + /// Return balance of ETH (ETH in Aurora EVM) + pub fn ft_balance_of_eth_on_aurora( + &mut self, + args: BalanceOfEthCallArgs, + ) -> Result<(), aurora_engine_types::types::balance::error::BalanceOverflowError> { + let balance = self + .ft + .internal_unwrap_balance_of_eth_on_aurora(&args.address); + sdk::log!("Balance of ETH [{}]: {}", args.address.encode(), balance); + self.io.return_output(format!("\"{}\"", balance).as_bytes()); + Ok(()) + } + + /// Transfer between NEAR accounts + pub fn ft_transfer( + &mut self, + predecessor_account_id: &AccountId, + args: &TransferCallArgs, + ) -> Result<(), fungible_token::error::TransferError> { + self.ft.internal_transfer_eth_on_near( + predecessor_account_id, + &args.receiver_id, + args.amount, + &args.memo, + )?; + self.save_ft_contract(); + sdk::log!( + "Transfer amount {} to {} success with memo: {:?}", + args.amount, + args.receiver_id, + args.memo + ); + Ok(()) + } + + /// FT resolve transfer logic + pub fn ft_resolve_transfer( + &mut self, + args: &ResolveTransferCallArgs, + promise_result: PromiseResult, + ) { + let amount = self.ft.ft_resolve_transfer( + promise_result, + &args.sender_id, + &args.receiver_id, + args.amount, + ); + sdk::log!( + "Resolve transfer from {} to {} success", + args.sender_id, + args.receiver_id + ); + // `ft_resolve_transfer` can change `total_supply` so we should save the contract + self.save_ft_contract(); + self.io.return_output(format!("\"{}\"", amount).as_bytes()); + } + + /// FT transfer call from sender account (invoker account) to receiver + /// We starting early checking for message data to avoid `ft_on_transfer` call panics + /// But we don't check relayer exists. If relayer doesn't exist we simply not mint/burn the amount of the fee + /// We allow empty messages for cases when `receiver_id =! current_account_id` + pub fn ft_transfer_call( + &mut self, + predecessor_account_id: AccountId, + current_account_id: AccountId, + args: TransferCallCallArgs, + prepaid_gas: NearGas, + ) -> Result { + sdk::log!( + "Transfer call to {} amount {}", + args.receiver_id, + args.amount, + ); + + // Verify message data before `ft_on_transfer` call to avoid verification panics + // It's allowed empty message if `receiver_id != current_account_id` + if args.receiver_id == current_account_id { + let message_data = FtTransferMessageData::parse_on_transfer_message(&args.msg) + .map_err(error::FtTransferCallError::MessageParseFailed)?; + // Check is transfer amount > fee + if message_data.fee.as_u128() >= args.amount.as_u128() { + return Err(error::FtTransferCallError::InsufficientAmountForFee); + } + + // Additional check overflow before process `ft_on_transfer` + // But don't check overflow for relayer + // Note: It can't overflow because the total supply doesn't change during transfer. + let amount_for_check = self + .ft + .internal_unwrap_balance_of_eth_on_aurora(&message_data.recipient); + if amount_for_check + .checked_add(Wei::from(args.amount)) + .is_none() + { + return Err(error::FtTransferCallError::Transfer( + fungible_token::error::TransferError::BalanceOverflow, + )); + } + if self + .ft + .total_eth_supply_on_aurora + .checked_add(Wei::from(args.amount)) + .is_none() + { + return Err(error::FtTransferCallError::Transfer( + fungible_token::error::TransferError::TotalSupplyOverflow, + )); + } + } + + self.ft + .ft_transfer_call( + predecessor_account_id, + args.receiver_id, + args.amount, + &args.memo, + args.msg, + current_account_id, + prepaid_gas, + ) + .map_err(Into::into) + } + + /// FT storage deposit logic + pub fn storage_deposit( + &mut self, + predecessor_account_id: AccountId, + amount: Yocto, + args: StorageDepositCallArgs, + ) -> Result, fungible_token::error::StorageFundingError> { + let account_id = args + .account_id + .unwrap_or_else(|| predecessor_account_id.clone()); + let (res, maybe_promise) = self.ft.storage_deposit( + predecessor_account_id, + &account_id, + amount, + args.registration_only, + )?; + self.save_ft_contract(); + self.io.return_output(&res.to_json_bytes()); + Ok(maybe_promise) + } + + /// FT storage unregister + pub fn storage_unregister( + &mut self, + account_id: AccountId, + force: Option, + ) -> Result, fungible_token::error::StorageFundingError> { + let promise = match self.ft.internal_storage_unregister(account_id, force) { + Ok((_, p)) => { + self.io.return_output(b"true"); + Some(p) + } + Err(fungible_token::error::StorageFundingError::NotRegistered) => { + self.io.return_output(b"false"); + None + } + Err(other) => return Err(other), + }; + Ok(promise) + } + + /// FT storage withdraw + pub fn storage_withdraw( + &mut self, + account_id: &AccountId, + args: &StorageWithdrawCallArgs, + ) -> Result<(), fungible_token::error::StorageFundingError> { + let res = self.ft.storage_withdraw(account_id, args.amount)?; + self.save_ft_contract(); + self.io.return_output(&res.to_json_bytes()); + Ok(()) + } + + /// Get balance of storage + pub fn storage_balance_of(&mut self, args: StorageBalanceOfCallArgs) { + self.io + .return_output(&self.ft.storage_balance_of(&args.account_id).to_json_bytes()); + } + + /// ft_on_transfer callback function + pub fn ft_on_transfer( + &mut self, + engine: &Engine<'_, I, E, M>, + args: &NEP141FtOnTransferArgs, + ) -> Result<(), error::FtTransferCallError> { + sdk::log!("Call ft_on_transfer"); + // Parse message with specific rules + let message_data = FtTransferMessageData::parse_on_transfer_message(&args.msg) + .map_err(error::FtTransferCallError::MessageParseFailed)?; + + // Special case when predecessor_account_id is current_account_id + let fee = Wei::from(message_data.fee); + // Mint fee to relayer + let relayer = engine.get_relayer(message_data.relayer.as_bytes()); + + let mint_amount = if relayer.is_some() && fee > ZERO_WEI { + self.mint_eth_on_aurora(relayer.unwrap(), fee)?; + args.amount.as_u128() - message_data.fee.as_u128() + } else { + args.amount.as_u128() + }; + + self.mint_eth_on_aurora(message_data.recipient, Wei::new(U256::from(mint_amount)))?; + self.save_ft_contract(); + self.io.return_output(b"\"0\""); + + Ok(()) + } + + /// Get accounts counter for statistics. + /// It represents total unique accounts (all-time, including accounts which now have zero balance). + pub fn get_accounts_counter(&mut self) { + self.io + .return_output(&self.ft.get_accounts_counter().to_le_bytes()); + } + + pub fn get_bridge_prover(&self) -> &AccountId { + &self.contract.prover_account + } + + /// Save eth-connector fungible token contract data + fn save_ft_contract(&mut self) { + self.io.write_borsh( + &construct_contract_key(&EthConnectorStorageId::FungibleToken), + &self.ft.data(), + ); + } + + /// Generate key for used events from Proof + fn used_event_key(&self, key: &str) -> Vec { + let mut v = construct_contract_key(&EthConnectorStorageId::UsedEvent).to_vec(); + v.extend_from_slice(key.as_bytes()); + v + } + + /// Save already used event proof as hash key + fn save_used_event(&mut self, key: &str) { + self.io.write_borsh(&self.used_event_key(key), &0u8); + } + + /// Check is event of proof already used + fn is_used_event(&self, key: &str) -> bool { + self.io.storage_has_key(&self.used_event_key(key)) + } + + /// Checks whether the provided proof was already used + pub fn is_used_proof(&self, proof: Proof) -> bool { + self.is_used_event(&proof_key(&proof)) + } + + /// Get Eth connector paused flags + pub fn get_paused_flags(&self) -> PausedMask { + self.get_paused() + } + + /// Set Eth connector paused flags + pub fn set_paused_flags(&mut self, args: &PauseEthConnectorCallArgs) { + self.set_paused(args.paused_mask); + } +} + +impl AdminControlled for EthConnectorContract { + /// Get current admin paused status + fn get_paused(&self) -> PausedMask { + self.paused_mask + } + + /// Set admin paused status + fn set_paused(&mut self, paused_mask: PausedMask) { + self.paused_mask = paused_mask; + self.io.write_borsh( + &construct_contract_key(&EthConnectorStorageId::PausedMask), + &self.paused_mask, + ); + } +} + +fn construct_contract_key(suffix: &EthConnectorStorageId) -> Vec { + aurora_engine_types::storage::bytes_to_key(KeyPrefix::EthConnector, &[u8::from(*suffix)]) +} + +fn get_contract_data( + io: &I, + suffix: &EthConnectorStorageId, +) -> Result { + io.read_storage(&construct_contract_key(suffix)) + .ok_or(error::StorageReadError::KeyNotFound) + .and_then(|x| { + x.to_value() + .map_err(|_| error::StorageReadError::BorshDeserialize) + }) +} + +/// Sets the contract data and returns it back +pub fn set_contract_data( + io: &mut I, + args: SetContractDataCallArgs, +) -> Result { + // Get initial contract arguments + let contract_data = EthConnector { + prover_account: args.prover_account, + eth_custodian_address: Address::decode(&args.eth_custodian_address)?, + }; + // Save eth-connector specific data + io.write_borsh( + &construct_contract_key(&EthConnectorStorageId::Contract), + &contract_data, + ); + + io.write_borsh( + &construct_contract_key(&EthConnectorStorageId::FungibleTokenMetadata), + &args.metadata, + ); + + Ok(contract_data) +} + +/// Return metadata +pub fn get_metadata(io: &I) -> Option { + io.read_storage(&construct_contract_key( + &EthConnectorStorageId::FungibleTokenMetadata, + )) + .and_then(|data| data.to_value().ok()) +} + +pub mod error { + use aurora_engine_types::types::address::error::AddressError; + use aurora_engine_types::types::balance::error::BalanceOverflowError; + + use crate::fungible_token; + use aurora_engine::deposit_event; + use aurora_engine::deposit_event::error::ParseOnTransferMessageError; + use aurora_engine::errors; + + const PROOF_EXIST: &[u8; 15] = errors::ERR_PROOF_EXIST; + + #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] + pub enum StorageReadError { + KeyNotFound, + BorshDeserialize, + } + + impl AsRef<[u8]> for StorageReadError { + fn as_ref(&self) -> &[u8] { + match self { + Self::KeyNotFound => errors::ERR_CONNECTOR_STORAGE_KEY_NOT_FOUND, + Self::BorshDeserialize => errors::ERR_FAILED_DESERIALIZE_CONNECTOR_DATA, + } + } + } + + #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] + pub enum DepositError { + Paused, + ProofParseFailed, + EventParseFailed(deposit_event::error::ParseError), + CustodianAddressMismatch, + InsufficientAmountForFee, + InvalidAddress(AddressError), + } + + impl AsRef<[u8]> for DepositError { + fn as_ref(&self) -> &[u8] { + match self { + Self::Paused => crate::admin_controlled::ERR_PAUSED.as_bytes(), + Self::ProofParseFailed => super::ERR_FAILED_PARSE.as_bytes(), + Self::EventParseFailed(e) => e.as_ref(), + Self::CustodianAddressMismatch => errors::ERR_WRONG_EVENT_ADDRESS, + Self::InsufficientAmountForFee => super::ERR_NOT_ENOUGH_BALANCE_FOR_FEE.as_bytes(), + Self::InvalidAddress(e) => e.as_ref(), + } + } + } + + #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] + pub enum FinishDepositError { + TransferCall(FtTransferCallError), + ProofUsed, + } + + impl From for FinishDepositError { + fn from(_: ProofUsed) -> Self { + Self::ProofUsed + } + } + + impl From for FinishDepositError { + fn from(e: FtTransferCallError) -> Self { + Self::TransferCall(e) + } + } + + impl From for FinishDepositError { + fn from(e: fungible_token::error::DepositError) -> Self { + Self::TransferCall(FtTransferCallError::Transfer(e.into())) + } + } + + impl AsRef<[u8]> for FinishDepositError { + fn as_ref(&self) -> &[u8] { + match self { + Self::ProofUsed => PROOF_EXIST, + Self::TransferCall(e) => e.as_ref(), + } + } + } + + #[derive(Debug)] + pub enum WithdrawError { + Paused, + FT(fungible_token::error::WithdrawError), + } + + impl From for WithdrawError { + fn from(e: fungible_token::error::WithdrawError) -> Self { + Self::FT(e) + } + } + + impl AsRef<[u8]> for WithdrawError { + fn as_ref(&self) -> &[u8] { + match self { + Self::Paused => crate::admin_controlled::ERR_PAUSED.as_bytes(), + Self::FT(e) => e.as_ref(), + } + } + } + + #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] + pub enum FtTransferCallError { + BalanceOverflow(BalanceOverflowError), + MessageParseFailed(ParseOnTransferMessageError), + InsufficientAmountForFee, + Transfer(fungible_token::error::TransferError), + } + + impl From for FtTransferCallError { + fn from(e: fungible_token::error::TransferError) -> Self { + Self::Transfer(e) + } + } + + impl From for FtTransferCallError { + fn from(e: fungible_token::error::DepositError) -> Self { + Self::Transfer(e.into()) + } + } + + impl From for FtTransferCallError { + fn from(e: ParseOnTransferMessageError) -> Self { + Self::MessageParseFailed(e) + } + } + + impl AsRef<[u8]> for FtTransferCallError { + fn as_ref(&self) -> &[u8] { + match self { + Self::MessageParseFailed(e) => e.as_ref(), + Self::InsufficientAmountForFee => super::ERR_NOT_ENOUGH_BALANCE_FOR_FEE.as_bytes(), + Self::Transfer(e) => e.as_ref(), + Self::BalanceOverflow(e) => e.as_ref(), + } + } + } + + #[derive(Debug)] + pub enum InitContractError { + AlreadyInitialized, + InvalidCustodianAddress(AddressError), + } + + impl AsRef<[u8]> for InitContractError { + fn as_ref(&self) -> &[u8] { + match self { + Self::AlreadyInitialized => errors::ERR_CONTRACT_INITIALIZED, + Self::InvalidCustodianAddress(e) => e.as_ref(), + } + } + } + + pub struct ProofUsed; + + impl AsRef<[u8]> for ProofUsed { + fn as_ref(&self) -> &[u8] { + PROOF_EXIST + } + } +} diff --git a/engine-standalone-nep141-legacy/src/lib.rs b/engine-standalone-nep141-legacy/src/lib.rs new file mode 100644 index 000000000..84de936ae --- /dev/null +++ b/engine-standalone-nep141-legacy/src/lib.rs @@ -0,0 +1,3 @@ +pub mod admin_controlled; +pub mod fungible_token; +pub mod legacy_connector; diff --git a/engine-standalone-storage/Cargo.toml b/engine-standalone-storage/Cargo.toml index cacf5e388..6241f2cee 100644 --- a/engine-standalone-storage/Cargo.toml +++ b/engine-standalone-storage/Cargo.toml @@ -15,11 +15,12 @@ crate-type = ["lib"] [dependencies] aurora-engine = { workspace = true, features = ["std"] } -aurora-engine-types = { workspace = true, features = ["std"] } aurora-engine-modexp = { workspace = true, features = ["std"] } aurora-engine-precompiles = { workspace = true, features = ["std"] } aurora-engine-sdk = { workspace = true, features = ["std"] } +aurora-engine-standalone-nep141-legacy.workspace = true aurora-engine-transactions = { workspace = true, features = ["std"] } +aurora-engine-types = { workspace = true, features = ["std"] } evm-core.workspace = true hex = { workspace = true, features = ["std"] } rocksdb.workspace = true diff --git a/engine-standalone-storage/src/relayer_db/mod.rs b/engine-standalone-storage/src/relayer_db/mod.rs index 5e29a0488..355c0c6f7 100644 --- a/engine-standalone-storage/src/relayer_db/mod.rs +++ b/engine-standalone-storage/src/relayer_db/mod.rs @@ -124,7 +124,7 @@ where if tx_succeeded { println!( "WARN: Transaction with NEAR hash {near_tx_hash:?} expected to succeed, but failed with error message {e:?}", - ); + ); } continue; } @@ -133,7 +133,7 @@ where println!( "WARN: Transaction with NEAR hash {near_tx_hash:?} expected to succeed, but failed with error message {:?}", result.status - ); + ); continue; } // if result.status.is_fail() && !tx_succeeded then this is consistent; we @@ -201,8 +201,9 @@ mod test { use super::FallibleIterator; use crate::relayer_db::types::ConnectionParams; use crate::sync::types::{TransactionKind, TransactionMessage}; - use aurora_engine::fungible_token::FungibleTokenMetadata; - use aurora_engine::{connector, parameters, state}; + use aurora_engine::{parameters, state}; + use aurora_engine_standalone_nep141_legacy::legacy_connector; + use aurora_engine_types::parameters::connector::FungibleTokenMetadata; use aurora_engine_types::H256; #[allow(clippy::doc_markdown)] @@ -238,9 +239,9 @@ mod test { let result = storage.with_engine_access(block_height, 0, &[], |io| { let mut local_io = io; state::set_state(&mut local_io, &engine_state).unwrap(); - connector::EthConnectorContract::create_contract( + legacy_connector::EthConnectorContract::create_contract( io, - &engine_state.owner_id, + engine_state.owner_id.clone(), parameters::InitCallArgs { prover_account: "prover.bridge.near".parse().unwrap(), eth_custodian_address: "6bfad42cfc4efc96f529d786d643ff4a8b89fa52" diff --git a/engine-standalone-storage/src/sync/mod.rs b/engine-standalone-storage/src/sync/mod.rs index 0f32b33ba..3700259b3 100644 --- a/engine-standalone-storage/src/sync/mod.rs +++ b/engine-standalone-storage/src/sync/mod.rs @@ -2,9 +2,10 @@ use aurora_engine::parameters::SubmitArgs; use aurora_engine::pausables::{ EnginePrecompilesPauser, PausedPrecompilesManager, PrecompileFlags, }; -use aurora_engine::{connector, engine, parameters::SubmitResult, state, xcc}; +use aurora_engine::{engine, parameters::SubmitResult, state, xcc}; use aurora_engine_modexp::ModExpAlgorithm; use aurora_engine_sdk::env::{self, Env, DEFAULT_PREPAID_GAS}; +use aurora_engine_standalone_nep141_legacy::legacy_connector; use aurora_engine_types::{ account_id::AccountId, parameters::PromiseWithCallbackArgs, @@ -209,6 +210,9 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( relayer_address: Address, promise_data: &[Option>], ) -> Result, error::Error> { + let is_disabled_legacy_nep141 = + aurora_engine::connector::EthConnectorContract::init_instance(io)? + .is_disabled_legacy_nep141(); let result = match transaction { TransactionKind::Call(args) => { // We can ignore promises in the standalone engine (see above) @@ -247,7 +251,7 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( engine::Engine::new(relayer_address, env.current_account_id(), io, &env)?; if env.predecessor_account_id == env.current_account_id { - connector::EthConnectorContract::init_instance(io)? + legacy_connector::EthConnectorContract::init_instance(io)? .ft_on_transfer(&engine, args)?; } else { engine.receive_erc20_tokens( @@ -261,8 +265,9 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( None } + TransactionKind::FtTransferCall(_) if is_disabled_legacy_nep141 => None, TransactionKind::FtTransferCall(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; let promise_args = connector.ft_transfer_call( env.predecessor_account_id.clone(), env.current_account_id.clone(), @@ -273,22 +278,25 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( Some(TransactionExecutionResult::Promise(promise_args)) } + TransactionKind::ResolveTransfer(_, _) if is_disabled_legacy_nep141 => None, TransactionKind::ResolveTransfer(args, promise_result) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; connector.ft_resolve_transfer(args, promise_result.clone()); None } + TransactionKind::FtTransfer(_) if is_disabled_legacy_nep141 => None, TransactionKind::FtTransfer(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; connector.ft_transfer(&env.predecessor_account_id, args)?; None } + TransactionKind::Withdraw(_) if is_disabled_legacy_nep141 => None, TransactionKind::Withdraw(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; connector.withdraw_eth_from_near( &env.current_account_id, &env.predecessor_account_id, @@ -298,8 +306,9 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( None } + TransactionKind::Deposit(_) if is_disabled_legacy_nep141 => None, TransactionKind::Deposit(raw_proof) => { - let connector_contract = connector::EthConnectorContract::init_instance(io)?; + let connector_contract = legacy_connector::EthConnectorContract::init_instance(io)?; let promise_args = connector_contract.deposit( raw_proof.clone(), env.current_account_id(), @@ -309,8 +318,9 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( Some(TransactionExecutionResult::Promise(promise_args)) } + TransactionKind::FinishDeposit(_) if is_disabled_legacy_nep141 => None, TransactionKind::FinishDeposit(finish_args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; let maybe_promise_args = connector.finish_deposit( env.predecessor_account_id(), env.current_account_id(), @@ -321,8 +331,9 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( maybe_promise_args.map(TransactionExecutionResult::Promise) } + TransactionKind::StorageDeposit(_) if is_disabled_legacy_nep141 => None, TransactionKind::StorageDeposit(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; let _promise = connector.storage_deposit( env.predecessor_account_id, Yocto::new(env.attached_deposit), @@ -332,22 +343,25 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( None } + TransactionKind::StorageUnregister(_) if is_disabled_legacy_nep141 => None, TransactionKind::StorageUnregister(force) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; let _promise = connector.storage_unregister(env.predecessor_account_id, *force)?; None } + TransactionKind::StorageWithdraw(_) if is_disabled_legacy_nep141 => None, TransactionKind::StorageWithdraw(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; connector.storage_withdraw(&env.predecessor_account_id, args)?; None } + TransactionKind::SetPausedFlags(_) if is_disabled_legacy_nep141 => None, TransactionKind::SetPausedFlags(args) => { - let mut connector = connector::EthConnectorContract::init_instance(io)?; + let mut connector = legacy_connector::EthConnectorContract::init_instance(io)?; connector.set_paused_flags(args); None @@ -377,22 +391,41 @@ fn non_submit_execute<'db, M: ModExpAlgorithm + 'static>( result? } + TransactionKind::SetConnectorData(_) if is_disabled_legacy_nep141 => None, TransactionKind::SetConnectorData(args) => { let mut connector_io = io; - connector::set_contract_data(&mut connector_io, args.clone())?; + legacy_connector::set_contract_data(&mut connector_io, args.clone())?; None } + TransactionKind::NewConnector(_) if is_disabled_legacy_nep141 => None, TransactionKind::NewConnector(args) => { - connector::EthConnectorContract::create_contract( + legacy_connector::EthConnectorContract::create_contract( io, - &env.current_account_id, + env.current_account_id, args.clone(), )?; None } + + TransactionKind::SetEthConnectorContractAccount(args) => { + use aurora_engine::admin_controlled::AdminControlled; + + let mut connector = aurora_engine::connector::EthConnectorContract::init_instance(io)?; + connector.set_eth_connector_contract_account(&args.account); + + None + } + + TransactionKind::DisableLegacyNEP141 => { + let mut connector = aurora_engine::connector::EthConnectorContract::init_instance(io)?; + connector.disable_legacy_nep141(); + + None + } + TransactionKind::NewEngine(args) => { state::set_state(&mut io, &args.clone().into())?; @@ -517,22 +550,24 @@ pub enum TransactionExecutionResult { } pub mod error { - use aurora_engine::{connector, engine, fungible_token, state, xcc}; + use aurora_engine::{engine, state, xcc}; + use aurora_engine_standalone_nep141_legacy::{fungible_token, legacy_connector}; #[derive(Debug)] pub enum Error { EngineState(state::EngineStateError), Engine(engine::EngineError), DeployErc20(engine::DeployErc20Error), - FtOnTransfer(connector::error::FtTransferCallError), - Deposit(connector::error::DepositError), - FinishDeposit(connector::error::FinishDepositError), + FtOnTransfer(legacy_connector::error::FtTransferCallError), + Deposit(legacy_connector::error::DepositError), + FinishDeposit(legacy_connector::error::FinishDepositError), FtTransfer(fungible_token::error::TransferError), - FtWithdraw(connector::error::WithdrawError), + FtWithdraw(legacy_connector::error::WithdrawError), FtStorageFunding(fungible_token::error::StorageFundingError), InvalidAddress(aurora_engine_types::types::address::error::AddressError), - ConnectorInit(connector::error::InitContractError), - ConnectorStorage(connector::error::StorageReadError), + ConnectorInit(legacy_connector::error::InitContractError), + LegacyConnectorStorage(legacy_connector::error::StorageReadError), + ConnectorStorage(aurora_engine::connector::error::StorageReadError), FundXccError(xcc::FundXccError), } @@ -554,20 +589,20 @@ pub mod error { } } - impl From for Error { - fn from(e: connector::error::FtTransferCallError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::FtTransferCallError) -> Self { Self::FtOnTransfer(e) } } - impl From for Error { - fn from(e: connector::error::DepositError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::DepositError) -> Self { Self::Deposit(e) } } - impl From for Error { - fn from(e: connector::error::FinishDepositError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::FinishDepositError) -> Self { Self::FinishDeposit(e) } } @@ -578,8 +613,8 @@ pub mod error { } } - impl From for Error { - fn from(e: connector::error::WithdrawError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::WithdrawError) -> Self { Self::FtWithdraw(e) } } @@ -596,14 +631,20 @@ pub mod error { } } - impl From for Error { - fn from(e: connector::error::InitContractError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::InitContractError) -> Self { Self::ConnectorInit(e) } } - impl From for Error { - fn from(e: connector::error::StorageReadError) -> Self { + impl From for Error { + fn from(e: legacy_connector::error::StorageReadError) -> Self { + Self::LegacyConnectorStorage(e) + } + } + + impl From for Error { + fn from(e: aurora_engine::connector::error::StorageReadError) -> Self { Self::ConnectorStorage(e) } } diff --git a/engine-standalone-storage/src/sync/types.rs b/engine-standalone-storage/src/sync/types.rs index 1c702aa8b..1db0e3e3a 100644 --- a/engine-standalone-storage/src/sync/types.rs +++ b/engine-standalone-storage/src/sync/types.rs @@ -1,6 +1,7 @@ use crate::Storage; use aurora_engine::parameters; use aurora_engine::xcc::{AddressVersionUpdateArgs, FundXccArgs}; +use aurora_engine_standalone_nep141_legacy::admin_controlled::PauseEthConnectorCallArgs; use aurora_engine_transactions::{EthTransactionKind, NormalizedEthTransaction}; use aurora_engine_types::account_id::AccountId; use aurora_engine_types::types::Address; @@ -114,7 +115,7 @@ pub enum TransactionKind { /// Admin only method; used to change upgrade delay blocks SetUpgradeDelayBlocks(parameters::SetUpgradeDelayBlocksArgs), /// Admin only method - SetPausedFlags(parameters::PauseEthConnectorCallArgs), + SetPausedFlags(PauseEthConnectorCallArgs), /// Ad entry mapping from address to relayer NEAR account RegisterRelayer(Address), /// Called if exist precompiles fail @@ -123,6 +124,8 @@ pub enum TransactionKind { SetConnectorData(parameters::SetContractDataCallArgs), /// Initialize eth-connector NewConnector(parameters::InitCallArgs), + SetEthConnectorContractAccount(parameters::SetEthConnectorContractAccountArgs), + DisableLegacyNEP141, /// Initialize Engine NewEngine(parameters::NewCallArgs), /// Update xcc-router bytecode @@ -352,6 +355,10 @@ impl TransactionKind { Self::RegisterRelayer(_) => Self::no_evm_execution("register_relayer"), Self::SetConnectorData(_) => Self::no_evm_execution("set_connector_data"), Self::NewConnector(_) => Self::no_evm_execution("new_connector"), + Self::SetEthConnectorContractAccount(_) => { + Self::no_evm_execution("set_eth_connector_contract_account") + } + Self::DisableLegacyNEP141 => Self::no_evm_execution("disable_legacy_nep141"), Self::NewEngine(_) => Self::no_evm_execution("new_engine"), Self::FactoryUpdate(_) => Self::no_evm_execution("factory_update"), Self::FactoryUpdateAddressVersion(_) => { @@ -522,7 +529,7 @@ enum BorshableTransactionKind<'a> { StorageDeposit(Cow<'a, parameters::StorageDepositCallArgs>), StorageUnregister(Option), StorageWithdraw(Cow<'a, parameters::StorageWithdrawCallArgs>), - SetPausedFlags(Cow<'a, parameters::PauseEthConnectorCallArgs>), + SetPausedFlags(Cow<'a, PauseEthConnectorCallArgs>), RegisterRelayer(Cow<'a, Address>), RefundOnError(Cow<'a, Option>), SetConnectorData(Cow<'a, parameters::SetContractDataCallArgs>), @@ -534,6 +541,8 @@ enum BorshableTransactionKind<'a> { PausePrecompiles(Cow<'a, parameters::PausePrecompilesCallArgs>), ResumePrecompiles(Cow<'a, parameters::PausePrecompilesCallArgs>), Unknown, + SetEthConnectorContractAccount(Cow<'a, parameters::SetEthConnectorContractAccountArgs>), + DisableLegacyNEP141, SetOwner(Cow<'a, parameters::SetOwnerArgs>), SubmitWithArgs(Cow<'a, parameters::SubmitArgs>), FundXccSubAccound(Cow<'a, FundXccArgs>), @@ -584,6 +593,10 @@ impl<'a> From<&'a TransactionKind> for BorshableTransactionKind<'a> { TransactionKind::Unknown => Self::Unknown, TransactionKind::PausePrecompiles(x) => Self::PausePrecompiles(Cow::Borrowed(x)), TransactionKind::ResumePrecompiles(x) => Self::ResumePrecompiles(Cow::Borrowed(x)), + TransactionKind::SetEthConnectorContractAccount(x) => { + Self::SetEthConnectorContractAccount(Cow::Borrowed(x)) + } + TransactionKind::DisableLegacyNEP141 => Self::DisableLegacyNEP141, TransactionKind::SetOwner(x) => Self::SetOwner(Cow::Borrowed(x)), TransactionKind::FundXccSubAccound(x) => Self::FundXccSubAccound(Cow::Borrowed(x)), TransactionKind::SetUpgradeDelayBlocks(x) => { @@ -652,6 +665,10 @@ impl<'a> TryFrom> for TransactionKind { BorshableTransactionKind::ResumePrecompiles(x) => { Ok(Self::ResumePrecompiles(x.into_owned())) } + BorshableTransactionKind::SetEthConnectorContractAccount(x) => { + Ok(Self::SetEthConnectorContractAccount(x.into_owned())) + } + BorshableTransactionKind::DisableLegacyNEP141 => Ok(Self::DisableLegacyNEP141), BorshableTransactionKind::SetOwner(x) => Ok(Self::SetOwner(x.into_owned())), BorshableTransactionKind::FundXccSubAccound(x) => { Ok(Self::FundXccSubAccound(x.into_owned())) diff --git a/engine-tests-connector/Cargo.toml b/engine-tests-connector/Cargo.toml new file mode 100644 index 000000000..04a8b161b --- /dev/null +++ b/engine-tests-connector/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "aurora-engine-tests-connector" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +description = "ETH connector tests" +homepage.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true +autobenches = false + +[dev-dependencies] +aurora-engine = { workspace = true, features = ["std", "tracing", "impl-serde", "borsh-compat"] } +aurora-engine-types = { workspace = true, features = ["std", "impl-serde", "borsh-compat"] } + +anyhow.workspace = true +byte-slice-cast.workspace = true +near-sdk.workspace = true +near-units.workspace = true +tokio = { workspace = true, features = ["macros"] } +hex.workspace = true +ethabi.workspace = true +rlp.workspace = true +serde = { workspace = true, features = ["derive"] } +workspaces = "0.7.0" + +[features] +mainnet-test = [] +testnet-test = [] diff --git a/engine-tests/src/tests/eth_connector.rs b/engine-tests-connector/src/connector.rs similarity index 53% rename from engine-tests/src/tests/eth_connector.rs rename to engine-tests-connector/src/connector.rs index e873be8a2..9e44af357 100644 --- a/engine-tests/src/tests/eth_connector.rs +++ b/engine-tests-connector/src/connector.rs @@ -1,373 +1,442 @@ -use crate::prelude::{Fee, NEP141Wei, H256, U256}; -use crate::utils::address_from_hex; -use crate::utils::workspace::create_sub_account; -use aurora_engine::connector::{PAUSE_DEPOSIT, PAUSE_WITHDRAW, UNPAUSE_ALL}; -use aurora_engine_types::parameters::connector::Proof; -use aurora_engine_types::parameters::WithdrawCallArgs; -use aurora_engine_types::types::Address; -use aurora_engine_workspace::types::ExecutionOutcome; -use aurora_engine_workspace::{parse_near, EngineContract, EngineContractBuilder}; -use serde_json::json; -use std::fmt::Debug; - -const CONTRACT_ACC: &str = "eth_connector.root"; -const PROOF_DATA_NEAR: &str = r#"{"log_index":0,"log_entry_data":[248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,107,17,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,10,160,177,33,112,26,26,176,12,12,163,2,249,133,245,12,51,201,55,50,148,156,122,67,27,26,101,178,36,153,54,100,53,137,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,197,65,5,202,188,134,5,164,246,19,133,35,57,28,114,241,186,81,123,163,166,161,24,32,157,168,170,13,108,58,61,46,160,6,199,163,13,91,119,225,39,168,255,213,10,107,252,143,246,138,241,108,139,59,35,187,185,162,223,53,108,222,73,181,109,160,27,154,49,63,26,170,15,177,97,255,6,204,84,221,234,197,159,172,114,47,148,126,32,199,241,127,101,120,182,51,52,100,185,1,0,0,0,8,0,0,0,0,0,0,0,32,0,0,0,0,0,2,0,8,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,8,32,0,32,0,0,128,0,2,0,0,0,1,0,32,0,0,0,2,0,0,0,0,32,0,0,0,0,0,4,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,128,64,0,0,0,0,1,32,0,0,0,0,0,0,96,32,0,64,0,0,0,128,1,0,0,0,0,1,0,0,0,8,0,0,0,18,32,0,0,64,145,1,8,0,4,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,16,0,128,0,0,0,0,0,0,128,0,2,0,0,0,0,0,0,0,0,0,0,2,0,80,0,0,0,0,0,0,0,0,1,128,0,8,0,0,0,0,4,0,0,0,128,2,0,32,0,128,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,16,0,8,0,0,0,0,0,0,0,0,0,0,128,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,25,1,227,23,131,157,85,14,131,122,18,0,131,75,91,132,132,96,174,58,224,140,115,112,105,100,101,114,49,48,1,2,8,230,160,188,212,199,183,154,22,223,85,103,215,24,122,240,235,79,129,44,93,184,88,161,218,79,5,44,226,106,100,50,40,163,97,136,155,158,202,3,149,91,200,78],"proof":[[248,113,160,46,156,31,85,241,226,241,13,5,56,73,146,176,67,195,109,6,189,172,104,44,103,44,88,32,15,181,152,136,29,121,252,160,191,48,87,174,71,151,208,114,164,150,51,200,171,90,90,106,46,200,79,77,222,145,95,89,141,137,138,149,67,73,8,87,128,128,128,128,128,128,160,175,9,219,77,174,13,247,133,55,172,92,185,202,7,160,10,204,112,44,133,36,96,30,234,235,134,30,209,205,166,212,255,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,107,17,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; -const PROOF_DATA_ETH: &str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,40,1,130,121,129,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,23,160,227,118,223,171,207,47,75,187,79,185,74,198,88,140,54,97,161,196,35,70,121,178,154,141,172,91,193,252,86,64,228,227,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,109,150,79,199,61,172,73,162,195,49,105,169,235,252,47,207,92,249,136,136,160,232,74,213,122,210,55,65,43,78,225,85,247,174,212,229,211,176,186,250,113,21,129,16,181,52,172,217,167,148,242,153,45,160,15,198,229,127,6,235,198,161,226,121,173,106,62,0,90,25,158,11,242,44,178,3,137,22,245,126,227,91,74,156,24,115,160,65,253,74,43,97,155,196,93,59,43,202,12,155,49,115,95,124,247,230,15,1,171,150,10,56,115,247,86,81,8,39,11,185,1,0,128,32,9,2,0,0,0,0,0,0,32,16,128,32,0,0,128,2,0,0,64,51,0,0,0,129,0,32,66,32,0,14,0,144,0,0,0,2,13,34,0,128,64,200,128,4,32,16,0,64,0,0,34,0,32,0,40,0,8,0,0,32,176,0,196,1,0,0,10,1,16,8,16,0,0,72,48,0,0,36,0,17,4,128,10,68,0,16,0,1,32,0,128,0,32,0,12,64,162,8,98,2,0,32,0,0,16,136,1,16,40,0,0,0,0,4,0,0,44,32,0,0,192,49,0,8,12,64,96,129,0,2,0,0,128,0,12,64,10,8,1,132,0,32,0,1,4,33,0,4,128,140,128,0,2,66,0,0,192,0,2,16,2,0,0,0,32,16,0,0,64,0,242,4,0,0,0,0,0,0,4,128,0,32,0,14,194,0,16,10,64,32,0,0,0,2,16,96,16,129,0,16,32,32,128,128,32,0,2,68,0,32,1,8,64,16,32,2,5,2,68,0,32,0,2,16,1,0,0,16,2,0,0,16,2,0,0,0,128,0,16,0,36,128,32,0,4,64,16,0,40,16,0,17,0,16,132,25,207,98,158,131,157,85,88,131,122,17,225,131,121,11,191,132,96,174,60,127,153,216,131,1,10,1,132,103,101,116,104,134,103,111,49,46,49,54,135,119,105,110,100,111,119,115,160,33,15,129,167,71,37,0,207,110,217,101,107,71,110,48,237,4,83,174,75,131,188,213,179,154,115,243,94,107,52,238,144,136,84,114,37,115,236,166,252,105],"proof":[[248,177,160,211,36,253,39,157,18,180,1,3,139,140,168,65,238,106,111,239,53,121,48,235,96,8,115,106,93,174,165,66,207,49,216,160,172,74,129,163,113,84,7,35,23,12,83,10,253,21,57,198,143,128,73,112,84,222,23,146,164,219,89,23,138,197,111,237,160,52,220,245,245,91,231,95,169,113,225,49,168,40,77,59,232,33,210,4,93,203,94,247,212,15,42,146,32,70,206,193,54,160,6,140,29,61,156,224,194,173,129,74,84,92,11,129,184,212,37,31,23,140,226,87,230,72,30,52,97,66,185,236,139,228,128,128,128,128,160,190,114,105,101,139,216,178,42,238,75,109,119,227,138,206,144,183,82,34,173,26,173,188,231,152,171,56,163,2,179,13,190,128,128,128,128,128,128,128,128],[249,2,47,48,185,2,43,249,2,40,1,130,121,129,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; -const DEPOSITED_RECIPIENT: &str = "eth_recipient.root"; -const PROVER_ACCOUNT: &str = "eth_connector.root"; -const CUSTODIAN_ADDRESS: &str = "096DE9C2B8A5B8c22cEe3289B101f6960d68E51E"; -const DEPOSITED_AMOUNT: u128 = 800400; -const DEPOSITED_FEE: u128 = 400; -const RECIPIENT_ETH_ADDRESS: &str = "891b2749238b27ff58e951088e55b04de71dc374"; -const EVM_CUSTODIAN_ADDRESS: &str = "096DE9C2B8A5B8c22cEe3289B101f6960d68E51E"; -const DEPOSITED_EVM_AMOUNT: u128 = 10200; -const DEPOSITED_EVM_FEE: u128 = 200; +use crate::utils::*; +use aurora_engine::deposit_event::{DepositedEvent, TokenMessageData, DEPOSITED_EVENT}; +use aurora_engine_types::parameters::connector::{Proof, WithdrawResult}; +use aurora_engine_types::{ + types::{Address, Fee, NEP141Wei}, + H256, U256, +}; +use byte_slice_cast::AsByteSlice; +use near_sdk::serde_json::json; +use near_sdk::{json_types::U128, serde, ONE_YOCTO}; +use std::str::FromStr; +use workspaces::AccountId; -#[tokio::test] -async fn test_deposit_eth_to_near_balance_total_supply() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); +/// Bytes for a NEAR smart contract implementing `ft_on_transfer` +fn dummy_ft_receiver_bytes() -> Vec { + let base_path = std::path::Path::new("../etc") + .join("tests") + .join("ft-receiver"); + let output_path = base_path.join("target/wasm32-unknown-unknown/release/ft_receiver.wasm"); + crate::rust::compile(base_path); + std::fs::read(output_path).unwrap() } #[tokio::test] -async fn test_deposit_eth_to_aurora_balance_total_supply() { - let contract = init(EVM_CUSTODIAN_ADDRESS).await.unwrap(); - let custodian_address = address_from_hex(CUSTODIAN_ADDRESS); +async fn test_aurora_ft_transfer() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + let proof = contract.get_proof(PROOF_DATA_NEAR); let res = contract - .register_relayer(custodian_address) + .engine_contract + .call("deposit") + .args_borsh(proof) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - call_deposit_eth_to_aurora(&contract).await; - assert_proof_was_used(&contract, PROOF_DATA_ETH).await; - - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, DEPOSITED_EVM_AMOUNT - DEPOSITED_EVM_FEE); - - let balance = get_eth_balance(&contract, custodian_address).await; - assert_eq!(balance, DEPOSITED_EVM_FEE); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_EVM_AMOUNT); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let transfer_amount: U128 = 70.into(); + let receiver_id = contract.engine_contract.id(); + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") + .args_json(json!({ + "receiver_id": &receiver_id, + "amount": transfer_amount, + "memo": "transfer memo" + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_success()); - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_EVM_AMOUNT); + let balance = contract + .eth_connector_contract + .call("ft_balance_of") + .args_json((&receiver_id,)) + .view() + .await? + .json::() + .unwrap(); + assert_eq!(balance.0, transfer_amount.0); + + let balance = contract + .eth_connector_contract + .call("ft_balance_of") + .args_json((user_acc.id(),)) + .view() + .await? + .json::() + .unwrap(); + assert_eq!(balance.0, DEPOSITED_AMOUNT - transfer_amount.0); + + let balance = contract + .eth_connector_contract + .call("ft_total_supply") + .view() + .await? + .json::() + .unwrap(); + assert_eq!(balance.0, DEPOSITED_AMOUNT); - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, DEPOSITED_EVM_AMOUNT); + Ok(()) } #[tokio::test] -async fn test_withdraw_eth_from_near() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - let withdraw_amount = 100; - let recipient_addr = address_from_hex(RECIPIENT_ETH_ADDRESS); - let res = contract - .withdraw(recipient_addr, withdraw_amount) - .deposit(1) +async fn test_ft_transfer() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let transfer_amount: U128 = 70.into(); + let receiver_id = contract.engine_contract.id(); + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") + .args_json(json!({ + "receiver_id": &receiver_id, + "amount": transfer_amount, + "memo": "transfer memo" + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let withdraw_result = res.into_value(); - assert_eq!(withdraw_result.amount.as_u128(), withdraw_amount); assert_eq!( - withdraw_result.recipient_id.encode(), - recipient_addr.encode() + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0, ); assert_eq!( - withdraw_result.eth_custodian_address.encode(), - CUSTODIAN_ADDRESS.to_lowercase() + contract.get_eth_on_near_balance(receiver_id).await?.0, + transfer_amount.0, ); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE - withdraw_amount); - - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT - withdraw_amount); + assert_eq!(DEPOSITED_AMOUNT, contract.total_supply().await?); + Ok(()) } #[tokio::test] -async fn test_ft_transfer() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - let transfer_amount = 70; - let res = contract - .ft_transfer( - &DEPOSITED_RECIPIENT.parse().unwrap(), - transfer_amount.into(), - Some("transfer memo".to_string()), - ) - .deposit(1) +async fn test_withdraw_eth_from_near() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + + let withdraw_amount = NEP141Wei::new(100); + let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS); + let res = user_acc + .call(contract.engine_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE + transfer_amount); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE - transfer_amount); + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - withdraw_amount.as_u128(), + ); + assert_eq!( + contract.total_supply().await?, + DEPOSITED_AMOUNT - withdraw_amount.as_u128(), + ); + Ok(()) +} - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); +#[tokio::test] +async fn test_deposit_eth_to_near_balance_total_supply() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + assert!( + contract.call_is_used_proof(PROOF_DATA_NEAR).await?, + "Expected not to fail because the proof should have been already used", + ); - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + Ok(()) } +// NOTE: We don't test relayer fee #[tokio::test] -async fn test_ft_transfer_call_eth() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); +async fn test_deposit_eth_to_aurora_balance_total_supply() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_aurora().await?; + assert!( + contract.call_is_used_proof(PROOF_DATA_ETH).await?, + "Expected not to fail because the proof should have been already used", + ); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS)) + .await?, + DEPOSITED_EVM_AMOUNT + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_EVM_AMOUNT); + Ok(()) +} - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); +#[tokio::test] +async fn test_ft_transfer_call_eth() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; - let res = contract - .register_relayer(address_from_hex(CUSTODIAN_ADDRESS)) - .transact() - .await - .unwrap(); - assert!(res.is_success()); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT, + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0, + ); - let transfer_amount = 50; + let transfer_amount: U128 = 50.into(); let fee: u128 = 30; - let message = create_message(CONTRACT_ACC, RECIPIENT_ETH_ADDRESS, fee); - let res = contract - .ft_transfer_call( - &CONTRACT_ACC.parse().unwrap(), - transfer_amount.into(), - None, - message, - ) - .deposit(1) - .max_gas() + let mut msg = U256::from(fee).as_byte_slice().to_vec(); + msg.append( + &mut validate_eth_address(RECIPIENT_ETH_ADDRESS) + .as_bytes() + .to_vec(), + ); + + let message = [CONTRACT_ACC, hex::encode(msg).as_str()].join(":"); + let memo: Option = None; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") + .args_json(json!({ + "receiver_id": contract.engine_contract.id(), + "amount": transfer_amount, + "memo": memo, + "msg": message, + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, transfer_amount - fee); - - let balance = get_eth_balance(&contract, address_from_hex(CUSTODIAN_ADDRESS)).await; - assert_eq!(balance, fee); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, transfer_amount); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0, + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + transfer_amount.0, + ); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS),) + .await?, + transfer_amount.0, + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + Ok(()) } #[tokio::test] -#[allow(clippy::too_many_lines)] -async fn test_ft_transfer_call_without_message() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let contract_account_id = &contract.id(); - let recipient_account = - create_sub_account(&contract.root(), "eth_recipient", parse_near!("50 N")) - .await - .unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); +async fn test_ft_transfer_call_without_message() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let res = contract - .register_relayer(address_from_hex(CUSTODIAN_ADDRESS)) - .transact() - .await - .unwrap(); - assert!(res.is_success()); - - // An attempt to send a message with wrong message format. - let res = contract - .ft_transfer_call( - &CONTRACT_ACC.parse().unwrap(), - 50.into(), - None, - String::new(), - ) - .deposit(1) - .transact() - .await - .err() - .unwrap(); - assert_error_message(&res, "ERR_INVALID_ON_TRANSFER_MESSAGE_FORMAT"); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT, + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0, + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.eth_connector_contract.id()) + .await? + .0, + 0, + ); - // Assert balances remain unchanged - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - // Should revert with `NotEnoughBalance` error while sending amount > balance if - // sender_id == receiver_id - let res = recipient_account - .call(contract_account_id, "ft_transfer_call") + let transfer_amount: U128 = 50.into(); + let memo: Option = None; + // Send to Aurora contract with wrong message should failed + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") .args_json(json!({ - "receiver_id": recipient_account.id(), - "amount": "1000000000", + "receiver_id": contract.engine_contract.id(), + "amount": transfer_amount, + "memo": &memo, "msg": "", })) - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); - assert!(res.is_failure()); - assert_error_message(&res.into_result().err().unwrap(), "ERR_NOT_ENOUGH_BALANCE"); + .await?; + assert!(contract.check_error_message(res, "ERR_INVALID_ON_TRANSFER_MESSAGE_FORMAT")); - // Shouldn't revert with `NotEnoughBalance` error while sending amount < balance when - // sender_id == receiver_id - let res = recipient_account - .call(contract_account_id, "ft_transfer_call") - .args_json(json!({ - "receiver_id": recipient_account.id(), - "amount": "1", - "msg": "", - })) - .deposit(1) - .max_gas() - .transact() - .await - .unwrap(); - assert!(res.is_success()); + // Assert balances remain unchanged + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0 + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.eth_connector_contract.id()) + .await? + .0, + 0 + ); // Sending to random account should not change balances - let transfer_amount = 22; - let res = recipient_account - .call(contract_account_id, "ft_transfer_call") + let some_acc = AccountId::from_str("some-test-acc").unwrap(); + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") .args_json(json!({ - "receiver_id": "some-test-acc", - "amount": transfer_amount.to_string(), - "msg": "", + "receiver_id": &some_acc, + "amount": transfer_amount, + "memo": &memo, + "msg": "" })) - .max_gas() - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); // some-test-acc does not implement `ft_on_transfer` therefore the call fails and the transfer is reverted. - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - let balance = get_eth_on_near_balance(&contract, "some-test-acc").await; - assert_eq!(balance, 0); - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT + ); + assert_eq!(contract.get_eth_on_near_balance(&some_acc).await?.0, 0); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0 + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.eth_connector_contract.id()) + .await? + .0, + 0 + ); - // Sending to external receiver with empty message should be success - let dummy_ft = create_sub_account(&contract.root(), "ft-rec", parse_near!("50 N")) - .await - .unwrap(); - let _result = dummy_ft.deploy(&dummy_ft_receiver_bytes()).await.unwrap(); + let dummy_contract = contract + .create_sub_account("ft-rec") + .await? + .deploy(&dummy_ft_receiver_bytes()) + .await? + .into_result()?; - let res = recipient_account - .call(contract_account_id, "ft_transfer_call") + // Sending to external receiver with empty message should be success + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") .args_json(json!({ - "receiver_id": dummy_ft.id(), - "amount": transfer_amount.to_string(), - "msg": "", + "receiver_id": dummy_contract.id(), + "amount": transfer_amount, + "memo": &memo, + "msg": "" })) - .max_gas() - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE - transfer_amount); - - let balance = get_eth_on_near_balance(&contract, dummy_ft.id().as_ref()).await; - assert_eq!(balance, transfer_amount); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = get_eth_balance(&contract, address_from_hex(CUSTODIAN_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0 + ); + assert_eq!( + contract + .get_eth_on_near_balance(dummy_contract.id()) + .await? + .0, + transfer_amount.0 + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0 + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.eth_connector_contract.id()) + .await? + .0, + 0 + ); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS)) + .await?, + 0 + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + Ok(()) } #[tokio::test] -async fn test_deposit_with_0x_prefix() { - use aurora_engine::deposit_event::TokenMessageData; - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let eth_custodian_address = address_from_hex(CUSTODIAN_ADDRESS); +async fn test_deposit_with_0x_prefix() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + + let eth_custodian_address: Address = Address::decode(CUSTODIAN_ADDRESS).unwrap(); let recipient_address = Address::from_array([10u8; 20]); let deposit_amount = 17; let recipient_address_encoded = recipient_address.encode(); // Note the 0x prefix before the deposit address. let message = [CONTRACT_ACC, ":", "0x", &recipient_address_encoded].concat(); - let fee: Fee = 0.into(); + let fee: Fee = Fee::new(NEP141Wei::new(0)); let token_message_data = TokenMessageData::parse_event_message_and_prepare_token_message_data(&message, fee) .unwrap(); - let deposit_event = aurora_engine::deposit_event::DepositedEvent { + let deposit_event = DepositedEvent { eth_custodian_address, sender: Address::zero(), token_message_data, @@ -376,8 +445,8 @@ async fn test_deposit_with_0x_prefix() { }; let event_schema = ethabi::Event { - name: aurora_engine::deposit_event::DEPOSITED_EVENT.into(), - inputs: aurora_engine::deposit_event::DepositedEvent::event_params(), + name: DEPOSITED_EVENT.into(), + inputs: DepositedEvent::event_params(), anonymous: false, }; let log_entry = aurora_engine_types::parameters::connector::LogEntry { @@ -398,866 +467,831 @@ async fn test_deposit_with_0x_prefix() { // Only this field matters for the purpose of this test log_entry_data: rlp::encode(&log_entry).to_vec(), receipt_index: 1, - ..Default::default() + receipt_data: Vec::new(), + header_data: Vec::new(), + proof: Vec::new(), }; - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); + let res = contract.deposit_with_proof(&proof).await?; assert!(res.is_success()); - let aurora_balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(aurora_balance, deposit_amount); - let address_balance = get_eth_balance(&contract, recipient_address).await; - assert_eq!(address_balance, deposit_amount); -} + let balance = contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await?; + assert_eq!(balance.0, deposit_amount); -#[tokio::test] -async fn test_deposit_eth_to_near_account() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let deposit_amount = 17; - let user_account = create_sub_account(&contract.root(), "some_user", parse_near!("50 N")) - .await - .unwrap(); - let proof = generate_dummy_proof(user_account.id().as_ref(), deposit_amount, 1); - - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); - assert!(res.is_success()); - - let aurora_balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(aurora_balance, 0); - let user_account_balance = get_eth_on_near_balance(&contract, user_account.id().as_ref()).await; - assert_eq!(user_account_balance, deposit_amount); + let balance = contract.get_eth_balance(&recipient_address).await?; + assert_eq!(balance, deposit_amount); + Ok(()) } #[tokio::test] -async fn test_deposit_eth_with_empty_custom_connector_account() { - // In this, test we make an ETH deposit using the message format for targeting - // an Aurora address, but use a different NEAR account than the Aurora Engine. - // The result is that the ETH is correctly minted to the Engine, but then an - // error occurs when it tries to transfer those funds because the listed NEAR - // account does not implement `ft_on_transfer`. - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let deposit_amount = 17; - let user_account = create_sub_account(&contract.root(), "some_user", parse_near!("50 N")) - .await - .unwrap(); - let recipient_address = Address::from_array([10u8; 20]); - let recipient_address_encoded = recipient_address.encode(); - let message = [ - user_account.id().as_ref(), - ":", - "0x", - &recipient_address_encoded, - ] - .concat(); - let proof = generate_dummy_proof(&message, deposit_amount, 1); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); - assert!(res.is_success()); - - let outcomes = res.outcomes(); - let failure_outcome = outcomes.get(5).unwrap(); - assert!(failure_outcome.is_failure()); - assert_error_message( - &failure_outcome, - r#"FunctionCallError(CompilationError(CodeDoesNotExist { account_id: AccountId("some_user.root") }))"#, - ); - - let user_account_balance = get_eth_on_near_balance(&contract, user_account.id().as_ref()).await; - assert_eq!(user_account_balance, 0); - let aurora_balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(aurora_balance, deposit_amount); - let address_balance = get_eth_balance(&contract, recipient_address).await; - assert_eq!(address_balance, 0); -} +async fn test_deposit_with_same_proof() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + assert!(!contract.call_is_used_proof(PROOF_DATA_NEAR).await?); + contract.call_deposit_eth_to_near().await?; + assert!(contract.call_is_used_proof(PROOF_DATA_NEAR).await?); -#[tokio::test] -async fn test_deposit_eth_with_custom_connector_account() { - // In this test, we make an ETH deposit using the message format for targeting - // an Aurora address, but use a different NEAR account than the Aurora Engine. - // Additionally, the target account implements `ft_on_transfer` so that it can - // receive the ETH and perform some action with it. This is safe because the ETH is - // minted in the Engine first, then transferred to the target account using - // `ft_transfer_call`. - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let deposit_amount = 17; - let user_account = create_sub_account(&contract.root(), "some_user", parse_near!("50 N")) - .await - .unwrap(); - let _result = user_account - .deploy(&dummy_ft_receiver_bytes()) - .await - .unwrap(); - - let recipient_address = Address::from_array([10u8; 20]); - let recipient_address_str = recipient_address.encode(); - let message = [ - user_account.id().as_ref(), - ":", - "0x", - &recipient_address_str, - ] - .concat(); - let proof = generate_dummy_proof(&message, deposit_amount, 1); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); - assert!(res.is_success()); - let outcomes = res.outcomes(); - let outcome = outcomes - .iter() - .find(|o| o.executor_id.as_str() == user_account.id().as_ref()) - .unwrap(); - assert_eq!( - &outcome.logs[0], - "in 17 tokens from @eth_connector.root ft_on_transfer, msg = some_user.root:00000000000000\ - 000000000000000000000000000000000000000000000000000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a" - ); - - let user_account_balance = get_eth_on_near_balance(&contract, user_account.id().as_ref()).await; - assert_eq!(user_account_balance, deposit_amount); - let aurora_balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(aurora_balance, 0); - let address_balance = get_eth_balance(&contract, recipient_address).await; - assert_eq!(address_balance, 0); + let res = contract + .deposit_with_proof(&contract.get_proof(PROOF_DATA_NEAR)) + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_PROOF_EXIST")); + Ok(()) } #[tokio::test] -async fn test_deposit_with_same_proof() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - - assert_proof_was_not_used(&contract, PROOF_DATA_NEAR).await; - - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - assert_proof_was_used(&contract, PROOF_DATA_NEAR).await; - - let result = call_deposit_eth_to_near(&contract).await; - assert!(result.is_err()); - assert_error_message(&result.err().unwrap(), "ERR_PROOF_EXIST"); +async fn test_deposit_wrong_custodian_address() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("0000000000000000000000000000000000000001").await?; + let res = contract + .deposit_with_proof(&contract.get_proof(PROOF_DATA_NEAR)) + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_WRONG_EVENT_ADDRESS")); + assert!(!contract.call_is_used_proof(PROOF_DATA_NEAR).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_wrong_custodian_address() { - let wrong_custodian_address = "0000000000000000000000000000000000000001"; - let contract = init(wrong_custodian_address).await.unwrap(); - let error = call_deposit_eth_to_near(&contract).await.err().unwrap(); +async fn test_ft_transfer_call_without_relayer() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; - assert_error_message(&error, "ERR_WRONG_EVENT_ADDRESS"); - assert_proof_was_not_used(&contract, PROOF_DATA_NEAR).await; -} + let receiver_id = contract.engine_contract.id(); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; -#[tokio::test] -async fn test_ft_transfer_call_without_relayer() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(res.is_success()); + assert_eq!(contract.get_eth_on_near_balance(receiver_id).await?.0, 0); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT + ); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); + let transfer_amount: U128 = 50.into(); + let fee: u128 = 30; + let mut msg = U256::from(fee).as_byte_slice().to_vec(); + let recipient_address = validate_eth_address(RECIPIENT_ETH_ADDRESS); + msg.append(&mut recipient_address.as_bytes().to_vec()); - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); + let relayer_id = "relayer.root"; + let message = [relayer_id, hex::encode(msg).as_str()].join(":"); - let transfer_amount = 50; - let fee: u128 = 30; - let message = create_message("relayer.root", RECIPIENT_ETH_ADDRESS, fee); - let res = contract - .ft_transfer_call(&contract.id(), transfer_amount.into(), None, message) - .deposit(1) - .max_gas() + let memo: Option = None; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") + .args_json(json!({ + "receiver_id": receiver_id, + "amount": transfer_amount, + "memo": memo, + "msg": message, + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, transfer_amount); - - let balance = get_eth_balance(&contract, address_from_hex(CUSTODIAN_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, transfer_amount); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0 + ); + assert_eq!( + contract.get_eth_on_near_balance(receiver_id).await?.0, + transfer_amount.0 + ); + assert_eq!( + contract.get_eth_balance(&recipient_address).await?, + transfer_amount.0 + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + Ok(()) } #[tokio::test] -async fn test_ft_transfer_call_fee_greater_than_amount() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); +async fn test_ft_transfer_call_fee_greater_than_amount() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let transfer_amount: U128 = 10.into(); + let fee: u128 = 12; + let mut msg = U256::from(fee).as_byte_slice().to_vec(); + msg.append( + &mut validate_eth_address(RECIPIENT_ETH_ADDRESS) + .as_bytes() + .to_vec(), + ); + let relayer_id = "relayer.root"; + let message = [relayer_id, hex::encode(msg).as_str()].join(":"); + let memo: Option = None; + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer_call") + .args_json(json!({ + "receiver_id": contract.engine_contract.id(), + "amount": transfer_amount, + "memo": memo, + "msg": message, + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; assert!(res.is_success()); - let transfer_amount = 10; - let fee: u128 = transfer_amount + 10; - let message = create_message("relayer.root", RECIPIENT_ETH_ADDRESS, fee); - let err = contract - .ft_transfer_call(&contract.id(), transfer_amount.into(), None, message) - .deposit(1) - .transact() - .await - .err() - .unwrap(); - assert_error_message( - &err, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0 ); - - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE); - - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = get_eth_balance(&contract, address_from_hex(CUSTODIAN_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + transfer_amount.0 + ); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS)) + .await?, + transfer_amount.0 + ); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + Ok(()) } #[tokio::test] -async fn test_admin_controlled_only_admin_can_pause() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let user_account = create_sub_account(&contract.root(), "user", parse_near!("50 N")) - .await - .unwrap(); - - // Try to pause from the user - should fail - let res = user_account - .call(&contract.id(), "set_paused_flags") +async fn test_admin_controlled_only_admin_can_pause() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + let user_acc = contract.create_sub_account("some-user").await?; + let res = user_acc + .call(contract.eth_connector_contract.id(), "set_paused_flags") .args_borsh(PAUSE_DEPOSIT) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_ACCESS_RIGHT")); - // Try to pause from the admin - should succeed let res = contract - .set_paused_flags(PAUSE_DEPOSIT) + .eth_connector_contract + .call("set_paused_flags") + .args_borsh(PAUSE_DEPOSIT) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); + Ok(()) } #[tokio::test] -async fn test_admin_controlled_admin_can_perform_actions_when_paused() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - - // 1st deposit call when unpaused - should succeed - let proof: Proof = serde_json::from_str(PROOF_DATA_NEAR).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); - assert!(res.is_success()); - - let withdraw_amount = 100; - let recipient_addr = address_from_hex(RECIPIENT_ETH_ADDRESS); +async fn test_access_right() -> anyhow::Result<()> { + let acc_name = "some_user.root".parse().unwrap(); + let contract = TestContract::new_with_owner(acc_name).await?; + contract.call_deposit_eth_to_near().await?; + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; - // 1st withdraw call when unpaused - should succeed let res = contract - .withdraw(recipient_addr, withdraw_amount) - .deposit(1) - .transact() - .await + .eth_connector_contract + .call("get_account_with_access_right") + .view() + .await? + .json::() .unwrap(); - assert!(res.is_success()); + assert_eq!(&res, contract.engine_contract.id()); + + let withdraw_amount = NEP141Wei::new(100); + let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS); + let res = user_acc + .call(contract.eth_connector_contract.id(), "engine_withdraw") + .args_borsh((user_acc.id(), recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_ACCESS_RIGHT")); - // Pause deposit let res = contract - .set_paused_flags(PAUSE_DEPOSIT) + .eth_connector_contract + .call("set_access_right") + .args_json((user_acc.id(),)) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); - assert!(res.is_success()); - - // 2nd deposit call when paused, but the admin is calling it - should succeed - // NB: We can use `PROOF_DATA_ETH` this will be just a different proof but the same deposit - // method which should be paused - let proof = serde_json::from_str(PROOF_DATA_ETH).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); + .await?; assert!(res.is_success()); - // Pause withdraw let res = contract - .set_paused_flags(PAUSE_WITHDRAW) - .transact() - .await + .eth_connector_contract + .call("get_account_with_access_right") + .view() + .await? + .json::() .unwrap(); - assert!(res.is_success()); + assert_eq!(&res, user_acc.id()); - // 2nd withdraw call when paused, but the admin is calling it - should succeed - let res = contract - .withdraw(recipient_addr, withdraw_amount) - .deposit(1) + let res = user_acc + .call(contract.eth_connector_contract.id(), "engine_withdraw") + .args_borsh((user_acc.id(), recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); + + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); + + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - withdraw_amount.as_u128(), + ); + assert_eq!( + contract.total_supply().await?, + DEPOSITED_AMOUNT - withdraw_amount.as_u128(), + ); + + Ok(()) } #[tokio::test] -async fn test_deposit_pausability() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let user_account = create_sub_account(&contract.root(), "user", parse_near!("50 N")) - .await - .unwrap(); +async fn test_deposit_pausability_eth_connector() -> anyhow::Result<()> { + let acc_name = AccountId::try_from("some_user.root".to_string()).unwrap(); + let contract = TestContract::new_with_owner(acc_name).await?; + let user_acc = contract.create_sub_account("some_user").await?; - // 1st deposit call - should succeed - let proof: Proof = serde_json::from_str(PROOF_DATA_NEAR).unwrap(); - let res = user_account - .call(&contract.id(), "deposit") - .args_borsh(proof) - .max_gas() + // Pause deposit + let res = user_acc + .call(contract.eth_connector_contract.id(), "set_paused_flags") + .args_borsh(PAUSE_DEPOSIT) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - // Pause deposit + // Check is flag DEPOSIT_PAUSE let res = contract - .set_paused_flags(PAUSE_DEPOSIT) - .transact() - .await + .eth_connector_contract + .call("get_paused_flags") + .view() + .await? + .borsh::() .unwrap(); - assert!(res.is_success()); + assert_eq!(res, 1); - // 2nd deposit call - should fail - // NB: We can use `PROOF_DATA_ETH` this will be just a different proof but the same deposit - // method which should be paused - let proof: Proof = serde_json::from_str(PROOF_DATA_ETH).unwrap(); - let res = user_account - .call(&contract.id(), "deposit") - .args_borsh(proof) - .max_gas() - .transact() - .await - .unwrap(); + // 2nd deposit call - should fail. + // Becasue `owner_id` check related to `predecessor_acount_id` + let res = contract + .user_deposit_with_proof(&user_acc, &contract.get_proof(PROOF_DATA_NEAR)) + .await?; assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_PAUSED")); - // Unpause all let res = contract - .set_paused_flags(UNPAUSE_ALL) + .engine_contract + .call("deposit") + .args_borsh(&contract.get_proof(PROOF_DATA_ETH)) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); - assert!(res.is_success()); + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_PAUSED")); - // 3rd deposit call - should succeed - let proof: Proof = serde_json::from_str(PROOF_DATA_ETH).unwrap(); - let res = user_account - .call(&contract.id(), "deposit") - .args_borsh(proof) - .max_gas() + assert_eq!(contract.total_supply().await?, 0); + + let res = user_acc + .call(contract.eth_connector_contract.id(), "deposit") + .args_borsh(&contract.get_proof(PROOF_DATA_NEAR)) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); + assert_eq!(contract.total_supply().await?, DEPOSITED_AMOUNT); + + Ok(()) } #[tokio::test] -async fn test_withdraw_from_near_pausability() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let user_account = create_sub_account(&contract.root(), "eth_recipient", parse_near!("50 N")) - .await - .unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(res.is_success()); +async fn test_deposit_pausability() -> anyhow::Result<()> { + let acc_name = AccountId::try_from("some_user.root".to_string()).unwrap(); + let contract = TestContract::new_with_owner(acc_name).await?; + let user_acc = contract.create_sub_account("some_user").await?; - let withdraw_args = WithdrawCallArgs { - recipient_address: address_from_hex(RECIPIENT_ETH_ADDRESS), - amount: NEP141Wei::new(10), - }; - // 1st withdraw - should succeed - let res = user_account - .call(&contract.id(), "withdraw") - .args_borsh(withdraw_args.clone()) - .deposit(1) + // Pause deposit + let res = user_acc + .call(contract.eth_connector_contract.id(), "set_paused_flags") + .args_borsh(PAUSE_DEPOSIT) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - // Pause withdraw + // Check is flag DEPOSIT_PAUSE let res = contract - .set_paused_flags(PAUSE_WITHDRAW) - .transact() - .await + .eth_connector_contract + .call("get_paused_flags") + .view() + .await? + .borsh::() .unwrap(); - assert!(res.is_success()); + assert_eq!(res, 1); - // 2nd withdraw - should fail - let res = user_account - .call(&contract.id(), "withdraw") - .args_borsh(withdraw_args.clone()) - .deposit(1) + // 2nd deposit call - should fail. + // Becasue `owner_id` check related to `predecessor_acount_id` + let res = contract + .user_deposit_with_proof(&user_acc, &contract.get_proof(PROOF_DATA_NEAR)) + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "ERR_PAUSED")); + + let res = contract + .engine_contract + .call("deposit") + .args_borsh(&contract.get_proof(PROOF_DATA_ETH)) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_failure()); - assert_error_message(&res.into_result().err().unwrap(), "ERR_PAUSED"); + assert!(contract.check_error_message(res, "ERR_PAUSED")); // Unpause all let res = contract - .set_paused_flags(UNPAUSE_ALL) + .eth_connector_contract + .call("set_paused_flags") + .args_borsh(UNPAUSE_ALL) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let res = user_account - .call(&contract.id(), "withdraw") - .args_borsh(withdraw_args) - .deposit(1) + // 3rd deposit call - should succeed + let res = contract + .engine_contract + .call("deposit") + .args_borsh(&contract.get_proof(PROOF_DATA_ETH)) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); -} -#[tokio::test] -async fn test_get_accounts_counter() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); + let res = contract + .user_deposit_with_proof(&user_acc, &contract.get_proof(PROOF_DATA_NEAR)) + .await?; assert!(res.is_success()); - let counter = contract.get_accounts_counter().await.unwrap(); - assert_eq!(counter.result, 2); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS)) + .await?, + DEPOSITED_EVM_AMOUNT + ); + assert_eq!( + contract.total_supply().await?, + DEPOSITED_AMOUNT + DEPOSITED_EVM_AMOUNT + ); + Ok(()) } #[tokio::test] -async fn test_get_accounts_counter_and_transfer() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); +async fn test_withdraw_from_near_pausability() -> anyhow::Result<()> { + let acc_name = AccountId::try_from(DEPOSITED_RECIPIENT.to_string()).unwrap(); + let contract = TestContract::new_with_owner(acc_name).await?; + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + + contract.call_deposit_eth_to_near().await?; + + let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS); + let withdraw_amount = NEP141Wei::new(100); + // 1st withdraw - should succeed + let res = user_acc + .call(contract.engine_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; assert!(res.is_success()); - let counter = contract.get_accounts_counter().await.unwrap(); - assert_eq!(counter.result, 2); + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); - let transfer_amount = 70; + // Pause withdraw let res = contract - .ft_transfer( - &DEPOSITED_RECIPIENT.parse().unwrap(), - transfer_amount.into(), - Some("transfer memo".to_string()), - ) - .deposit(1) + .eth_connector_contract + .call("set_paused_flags") + .args_borsh(PAUSE_WITHDRAW) + .gas(DEFAULT_GAS) .transact() - .await - .unwrap(); + .await?; assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, DEPOSITED_AMOUNT - DEPOSITED_FEE + transfer_amount); + // 2nd withdraw - should be failed + let res = user_acc + .call(contract.engine_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_failure()); + assert!(contract.check_error_message(res, "WithdrawErrorPaused")); + + // Direct call to eth-connector from owner should be success + let res = user_acc + .call(contract.eth_connector_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_success()); - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, DEPOSITED_FEE - transfer_amount); + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); - let balance = total_supply(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); + // Unpause all + let res = contract + .eth_connector_contract + .call("set_paused_flags") + .args_borsh(UNPAUSE_ALL) + .gas(DEFAULT_GAS) + .transact() + .await?; + assert!(res.is_success()); - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); + let res = user_acc + .call(contract.engine_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_success()); - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, DEPOSITED_AMOUNT); + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); - let counter = contract.get_accounts_counter().await.unwrap(); - assert_eq!(counter.result, 2); + assert_eq!( + contract.total_supply().await?, + DEPOSITED_AMOUNT - 3 * withdraw_amount.as_u128() + ); + Ok(()) } - #[tokio::test] -async fn test_deposit_to_near_with_zero_fee() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); +async fn test_deposit_to_near_with_zero_fee() -> anyhow::Result<()> { let proof_str = r#"{"log_index":0,"log_entry_data":[248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,11,184,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,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,0,0,0,0,0,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,106,249,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,11,184,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,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,0,0,0,0,0,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,23,160,7,139,123,21,146,99,81,234,117,153,151,30,67,221,231,90,105,219,121,127,196,224,201,83,178,31,173,155,190,123,227,174,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,109,150,79,199,61,172,73,162,195,49,105,169,235,252,47,207,92,249,136,136,160,227,202,170,144,85,104,169,90,220,93,227,155,76,252,229,223,163,146,127,223,157,121,27,238,116,64,112,216,124,129,107,9,160,158,128,122,7,117,120,186,231,92,224,181,67,43,66,153,79,155,38,238,166,68,1,151,100,134,126,214,86,59,66,174,201,160,235,177,124,164,253,179,174,206,160,196,186,61,51,64,217,35,121,86,229,24,251,162,51,82,72,31,218,240,150,32,157,48,185,1,0,0,0,8,0,0,32,0,0,0,0,0,0,128,0,0,0,2,0,128,0,64,32,0,0,0,0,0,0,64,0,0,10,0,0,0,0,0,0,3,0,0,0,0,64,128,0,0,64,0,0,0,0,0,16,0,0,130,0,1,16,0,32,4,0,0,0,0,0,2,1,0,0,0,0,0,8,0,8,0,0,32,0,4,128,2,0,128,0,0,0,0,0,0,0,0,0,4,32,0,8,2,0,0,0,128,65,0,136,0,0,40,0,0,0,8,0,0,128,0,34,0,4,0,185,2,0,0,4,32,128,0,2,0,0,0,128,0,0,10,0,1,0,1,0,0,0,0,32,1,8,128,0,0,4,0,0,0,128,128,0,70,0,0,0,0,0,0,16,64,0,64,0,34,64,0,0,0,4,0,0,0,0,1,128,0,9,0,0,0,0,0,16,0,0,64,2,0,0,0,132,0,64,32,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,4,0,0,0,32,8,0,16,0,8,0,16,68,0,0,0,16,0,0,0,128,0,64,0,0,128,0,0,0,0,0,0,0,16,0,1,0,16,132,49,181,116,68,131,157,92,101,131,122,18,0,131,101,155,9,132,96,174,110,74,153,216,131,1,10,1,132,103,101,116,104,134,103,111,49,46,49,54,135,119,105,110,100,111,119,115,160,228,82,26,232,236,82,141,6,111,169,92,14,115,254,59,131,192,3,202,209,126,79,140,182,163,12,185,45,210,17,60,38,136,84,114,37,115,236,183,145,213],"proof":[[248,145,160,187,129,186,104,13,250,13,252,114,170,223,247,137,53,113,225,188,217,54,244,108,193,247,236,197,29,0,161,119,76,227,184,160,66,209,234,66,254,223,80,22,246,80,204,38,2,90,115,201,183,79,207,47,192,234,143,221,89,78,36,199,127,9,55,190,160,91,160,251,58,165,255,90,2,105,47,46,220,67,3,52,105,42,182,130,224,19,162,115,159,136,158,218,93,187,148,188,9,128,128,128,128,128,160,181,223,248,223,173,187,103,169,52,204,62,13,90,70,147,236,199,27,201,112,157,4,139,63,188,12,98,117,10,82,85,125,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,106,249,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,11,184,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,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,0,0,0,0,0,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); + let contract = TestContract::new().await?; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; assert!(res.is_success()); - - assert_proof_was_used(&contract, proof_str).await; + assert!(contract.call_is_used_proof(proof_str).await?); let deposited_amount = 3000; + let receiver_id = AccountId::from_str(DEPOSITED_RECIPIENT).unwrap(); - let balance = get_eth_on_near_balance(&contract, DEPOSITED_RECIPIENT).await; - assert_eq!(balance, deposited_amount); - - let balance = get_eth_on_near_balance(&contract, CONTRACT_ACC).await; - assert_eq!(balance, 0); - - let balance = total_supply(&contract).await; - assert_eq!(balance, deposited_amount); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, deposited_amount); + assert_eq!( + contract.get_eth_on_near_balance(&receiver_id).await?.0, + deposited_amount + ); + assert_eq!( + contract + .get_eth_on_near_balance(contract.engine_contract.id()) + .await? + .0, + 0 + ); - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, 0); + assert_eq!(contract.total_supply().await?, deposited_amount); + Ok(()) } #[tokio::test] -async fn test_deposit_to_aurora_with_zero_fee() { - let contract = init(EVM_CUSTODIAN_ADDRESS).await.unwrap(); - let res = contract - .register_relayer(address_from_hex(CUSTODIAN_ADDRESS)) - .transact() - .await - .unwrap(); - assert!(res.is_success()); - +async fn test_deposit_to_aurora_with_zero_fee() -> anyhow::Result<()> { let proof_str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,7,208,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,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,0,0,0,0,0,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":3,"receipt_data":[249,2,41,1,131,2,246,200,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,7,208,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,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,0,0,0,0,0,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,23,160,110,48,40,236,52,198,197,25,255,191,199,4,137,3,185,31,202,84,90,80,104,32,176,13,144,141,165,183,36,30,94,138,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,148,156,193,169,167,156,148,249,191,22,225,202,121,212,79,2,197,75,191,164,160,127,26,168,212,111,22,173,213,25,217,187,227,114,86,173,99,166,195,67,16,104,111,200,109,110,147,241,23,71,122,89,215,160,47,120,179,75,110,158,228,18,242,156,38,111,95,25,236,211,158,53,53,62,89,190,2,40,220,41,151,200,127,219,33,219,160,222,177,165,249,98,109,130,37,226,229,165,113,45,12,145,30,16,28,154,86,22,203,218,233,13,246,165,177,61,57,68,83,185,1,0,0,32,8,0,33,0,0,0,64,0,32,0,128,0,0,0,132,0,0,0,64,32,64,0,0,1,0,32,64,0,0,8,0,0,0,0,0,0,137,32,0,0,0,64,128,0,0,16,0,0,0,0,33,64,0,1,0,0,0,0,0,0,0,0,68,0,0,0,2,1,64,0,0,0,0,9,16,0,0,32,0,0,0,128,2,0,0,0,33,0,0,0,128,0,0,0,12,64,32,8,66,2,0,0,64,0,0,8,0,0,40,8,8,0,0,0,0,16,0,0,0,0,64,49,0,0,8,0,96,0,0,18,0,0,0,0,0,64,10,0,1,0,0,32,0,0,0,33,0,0,128,136,10,64,0,64,0,0,192,128,0,0,64,1,0,0,4,0,8,0,64,0,34,0,0,0,0,0,0,0,0,0,0,0,8,8,0,4,0,0,0,32,0,4,0,2,0,0,0,129,4,0,96,16,4,8,0,0,0,0,0,0,1,0,128,16,0,0,2,0,4,0,32,0,8,0,0,0,0,16,0,1,0,0,0,0,64,0,128,0,0,32,36,128,0,0,4,64,0,8,8,16,0,1,4,16,132,50,32,156,229,131,157,92,137,131,122,18,0,131,35,159,183,132,96,174,111,126,153,216,131,1,10,3,132,103,101,116,104,136,103,111,49,46,49,54,46,51,133,108,105,110,117,120,160,59,74,90,253,211,14,166,114,39,213,120,95,221,43,109,173,72,205,160,203,71,44,83,159,36,59,129,84,32,16,254,251,136,49,16,97,244,161,246,244,85],"proof":[[248,113,160,227,103,29,228,16,56,196,146,115,29,122,202,254,140,214,86,189,108,47,197,2,195,50,211,4,126,58,175,71,11,70,78,160,229,239,23,242,100,150,90,169,21,162,252,207,202,244,187,71,172,126,191,33,166,162,45,134,108,114,6,76,78,177,148,140,128,128,128,128,128,128,160,21,91,249,81,132,162,52,236,128,181,5,72,158,228,177,131,87,144,64,194,111,103,180,16,183,103,245,136,125,213,208,76,128,128,128,128,128,128,128,128],[249,1,241,128,160,52,154,34,8,39,210,121,1,151,92,91,225,198,154,204,207,11,204,187,59,223,154,187,102,115,110,193,141,201,198,95,253,160,218,19,188,241,210,48,51,3,76,125,48,152,171,188,45,136,109,71,236,171,242,162,10,34,245,160,191,5,120,9,80,129,160,147,160,142,184,113,171,112,171,131,124,150,117,65,27,207,149,119,136,120,65,7,99,155,114,169,57,91,125,26,117,49,67,160,173,217,104,114,149,170,18,227,251,73,78,11,220,243,240,66,117,32,199,64,138,173,169,43,8,122,39,47,210,54,41,192,160,139,116,124,73,113,242,225,65,167,48,33,13,149,51,152,196,79,93,126,103,116,48,177,25,80,186,34,55,15,116,2,13,160,67,10,207,13,108,228,254,73,175,10,166,107,144,157,150,135,173,179,140,112,129,205,168,132,194,4,191,175,239,50,66,245,160,26,193,195,232,40,106,60,72,133,32,204,205,104,90,20,60,166,16,214,184,115,44,216,62,82,30,141,124,160,72,173,62,160,67,5,174,33,105,28,248,245,48,15,129,153,96,27,97,125,29,194,233,139,228,8,243,221,79,2,151,52,75,30,47,136,160,103,94,192,58,117,224,88,80,21,183,254,178,135,21,78,20,233,250,7,22,243,14,41,56,12,118,206,224,75,42,96,77,160,225,64,237,254,248,145,134,195,166,49,205,129,233,54,142,136,235,242,10,14,175,76,73,131,26,135,102,237,64,23,102,213,160,167,104,45,101,228,93,89,216,167,142,125,0,216,77,167,4,245,156,140,98,117,19,165,25,185,204,84,161,175,153,193,20,160,53,22,192,197,176,225,102,6,251,115,216,238,53,110,254,106,193,134,232,100,173,93,211,71,195,10,192,107,97,190,165,12,160,104,206,244,51,77,131,79,209,64,233,97,35,142,75,42,205,198,120,222,90,199,168,126,235,12,225,30,240,214,56,253,168,160,230,94,127,56,22,169,3,159,236,49,217,88,2,175,168,22,104,177,154,127,106,165,176,238,236,141,83,64,123,28,177,206,160,140,137,2,195,227,9,182,245,76,62,215,174,168,254,15,125,111,241,30,50,110,189,66,58,230,2,252,104,182,247,223,94,128],[249,2,48,32,185,2,44,249,2,41,1,131,2,246,200,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,7,208,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,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,0,0,0,0,0,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); + let contract = TestContract::new().await?; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; assert!(res.is_success()); - - assert_proof_was_used(&contract, proof_str).await; + assert!(contract.call_is_used_proof(proof_str).await?); let deposited_amount = 2000; - let balance = get_eth_balance(&contract, address_from_hex(RECIPIENT_ETH_ADDRESS)).await; - assert_eq!(balance, deposited_amount); - - let balance = get_eth_balance(&contract, address_from_hex(CUSTODIAN_ADDRESS)).await; - assert_eq!(balance, 0); - - let balance = total_supply(&contract).await; - assert_eq!(balance, deposited_amount); - - let balance = total_eth_supply_on_aurora(&contract).await; - assert_eq!(balance, deposited_amount); - - let balance = total_eth_supply_on_near(&contract).await; - assert_eq!(balance, deposited_amount); + assert_eq!( + contract + .get_eth_balance(&validate_eth_address(RECIPIENT_ETH_ADDRESS)) + .await?, + deposited_amount + ); + assert_eq!(contract.total_supply().await?, deposited_amount); + Ok(()) } #[tokio::test] -async fn test_deposit_to_near_amount_less_fee() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_near_amount_less_fee() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,2,88,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,2,88,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,10,160,139,92,51,142,163,95,21,160,61,29,148,206,54,147,187,96,77,109,244,8,130,155,249,198,206,30,173,216,144,176,252,123,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,218,9,209,192,173,39,133,109,141,57,2,146,184,12,94,217,6,138,173,67,121,185,24,179,133,189,219,40,81,210,73,106,160,219,108,244,199,44,203,84,71,126,74,82,240,203,255,238,20,226,29,239,51,7,19,144,34,156,137,232,159,71,30,164,29,160,209,61,241,33,17,103,192,203,57,156,112,250,18,166,26,237,248,153,226,185,87,220,156,93,249,17,39,190,125,96,247,239,185,1,0,0,0,8,0,0,0,0,0,0,0,0,1,0,0,0,0,0,128,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,8,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,0,8,0,0,0,0,0,0,0,32,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,32,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,128,0,0,0,0,32,0,0,0,0,8,0,0,2,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,144,4,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,91,80,110,139,131,157,118,104,131,122,18,0,131,30,4,87,132,96,175,154,220,140,115,112,105,100,101,114,49,48,1,2,9,64,160,80,163,212,151,183,11,70,219,178,190,167,172,64,187,47,14,29,226,253,132,116,145,81,143,54,249,121,123,193,241,120,249,136,244,120,239,134,243,43,177,139],"proof":[[248,81,160,164,35,68,182,184,52,174,73,6,81,4,92,187,190,187,106,255,124,123,24,244,168,161,247,60,181,75,29,192,175,96,140,128,128,128,128,128,128,128,160,169,157,199,164,106,205,109,88,111,183,255,180,108,15,155,137,126,163,108,44,117,125,138,221,3,188,93,85,146,129,19,139,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,2,88,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(res.is_success()); + assert!(contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_to_aurora_amount_less_fee() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_aurora_amount_less_fee() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,3,132,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,40,1,130,121,119,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,3,132,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,10,160,234,97,221,132,104,51,119,219,129,206,197,27,130,197,14,113,167,32,152,214,207,205,156,210,35,213,198,227,116,42,51,224,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,15,150,233,184,181,140,226,81,205,139,229,87,226,149,49,207,117,33,36,83,124,8,75,199,231,48,13,23,189,217,179,12,160,241,37,169,74,233,62,231,112,0,207,95,228,68,240,108,254,57,199,255,130,142,158,161,180,243,50,255,222,77,251,252,126,160,31,111,236,60,142,91,35,119,195,92,158,134,65,138,8,247,98,122,229,21,226,85,38,130,141,139,168,60,83,90,63,244,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,128,0,0,0,0,128,0,0,0,32,0,0,0,0,0,0,64,0,0,10,0,0,0,0,0,0,1,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,8,0,0,2,0,0,0,4,0,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,8,2,0,0,0,0,0,0,136,0,4,40,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,32,0,0,10,0,0,0,0,0,0,10,0,1,0,0,0,0,0,0,32,0,0,128,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,16,0,0,64,0,34,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,128,2,0,0,0,128,0,1,32,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,4,0,0,0,32,128,0,0,0,0,0,16,0,0,0,0,0,0,0,0,128,0,0,0,0,128,0,0,0,0,0,0,0,16,0,1,0,16,132,91,127,63,197,131,157,118,142,131,122,18,0,131,25,25,181,132,96,175,156,157,140,115,112,105,100,101,114,49,48,1,2,9,64,160,68,227,115,157,18,184,21,217,93,74,196,34,230,228,210,239,61,26,221,245,191,46,44,135,134,2,20,53,95,18,128,54,136,162,198,27,59,153,146,63,16],"proof":[[248,113,160,204,110,241,220,150,206,51,121,104,130,125,127,249,35,9,242,107,45,164,62,147,221,93,116,73,79,49,96,226,92,235,247,160,43,215,154,177,148,177,15,202,141,217,45,114,108,33,74,0,144,126,189,26,78,152,232,105,119,103,203,51,79,45,113,124,128,128,128,128,128,128,160,74,177,164,103,85,250,153,17,105,68,205,207,176,48,89,230,100,35,20,167,34,117,11,115,14,107,128,214,48,17,53,209,128,128,128,128,128,128,128,128],[249,2,47,48,185,2,43,249,2,40,1,130,121,119,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,0,150,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,0,0,0,3,132,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(res.is_success()); + assert!(contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_to_near_amount_zero_fee_non_zero() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_near_amount_zero_fee_non_zero() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,244,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,244,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,10,160,47,76,8,45,83,192,115,218,108,188,181,117,148,40,254,44,169,118,92,188,207,7,122,246,133,75,100,184,134,128,91,12,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,225,211,110,129,173,98,101,150,55,116,11,30,26,161,226,8,234,249,90,46,245,112,225,68,76,26,215,135,27,181,140,22,160,229,44,239,5,102,141,42,118,174,163,144,225,90,152,120,60,150,25,144,217,154,234,25,69,35,226,103,149,188,127,81,106,160,177,89,93,76,113,24,117,182,174,52,148,6,239,129,151,18,222,56,245,9,232,80,7,129,118,118,108,72,76,247,238,101,185,1,0,1,4,200,10,0,0,0,0,8,0,32,0,128,3,1,0,0,145,4,33,72,8,0,2,0,128,0,18,64,26,38,0,4,16,8,1,136,65,40,32,0,0,1,72,0,2,0,128,0,64,0,0,48,0,32,0,0,0,0,192,0,100,9,0,12,0,16,0,0,1,2,8,8,0,8,12,128,64,0,192,2,0,0,64,2,68,129,0,128,1,0,0,128,128,68,0,64,64,32,0,67,0,32,0,0,41,20,1,0,16,40,0,16,16,32,0,0,0,128,0,0,0,64,48,4,8,8,0,0,0,0,66,32,64,0,0,48,0,16,8,1,64,0,0,16,32,0,33,32,0,0,128,0,2,2,128,0,0,192,0,2,40,0,0,0,0,0,1,0,67,1,0,131,32,6,8,0,0,8,96,128,0,0,0,0,12,0,0,0,65,2,160,2,64,0,2,4,32,0,128,0,1,34,0,105,0,160,0,32,18,32,16,1,0,0,0,20,0,32,0,20,0,96,128,0,16,0,0,64,16,2,192,1,0,4,32,0,32,130,2,0,0,32,0,0,0,4,64,12,64,0,0,4,0,0,1,132,93,96,3,163,131,157,117,205,131,122,18,0,131,113,87,104,132,96,175,145,182,140,115,112,105,100,101,114,49,48,1,2,9,64,160,179,183,88,73,3,20,234,255,8,238,6,186,173,204,149,149,235,233,232,35,158,194,53,246,218,39,221,246,90,7,34,255,136,176,36,100,161,146,27,98,29],"proof":[[248,177,160,93,101,188,48,5,53,36,126,41,0,92,130,188,117,104,230,178,29,27,194,22,86,212,235,193,20,241,42,157,88,117,205,160,141,83,180,197,22,126,217,34,74,50,114,118,42,157,161,171,8,158,98,92,183,124,137,130,211,1,106,44,222,37,13,32,160,62,131,146,138,69,63,89,98,140,64,187,93,207,160,0,4,134,154,205,47,168,231,136,249,129,230,137,29,3,210,67,173,160,76,91,176,245,81,3,198,111,175,230,185,70,220,111,189,88,15,154,173,107,239,121,185,13,159,197,61,37,231,252,22,200,128,128,128,128,160,13,246,139,212,38,202,103,201,31,80,247,136,186,58,17,52,66,119,115,128,23,123,59,166,177,68,79,182,9,242,60,106,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,244,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(res.is_success()); + assert!(contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_to_aurora_amount_zero_fee_non_zero() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_aurora_amount_zero_fee_non_zero() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,174,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":1,"receipt_data":[249,2,41,1,131,1,110,54,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,174,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,21,160,60,128,9,36,168,69,207,249,164,88,177,15,74,221,137,160,110,246,3,133,209,132,169,179,31,86,142,216,160,11,162,137,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,28,255,226,5,233,121,118,187,157,30,192,6,245,34,35,96,168,147,83,224,160,182,206,231,252,255,115,166,11,152,156,84,169,204,36,0,94,3,17,113,103,104,252,225,161,115,85,74,227,104,249,187,232,160,211,106,68,136,2,141,5,14,201,111,68,218,251,84,103,176,66,10,190,123,58,119,216,141,192,197,222,181,211,87,117,192,160,162,200,112,106,166,13,220,187,223,164,251,102,104,106,40,84,17,101,93,131,125,204,193,62,96,110,167,214,54,41,154,191,185,1,0,0,40,72,0,32,0,0,0,0,0,0,5,128,2,0,8,0,128,144,136,0,34,0,0,32,1,0,0,64,16,0,10,0,16,8,28,0,17,9,0,0,0,0,72,0,16,4,0,0,0,0,128,2,18,0,0,0,0,1,16,0,36,0,1,1,32,8,0,2,1,0,64,64,0,0,8,0,16,0,40,2,0,13,0,2,8,0,0,0,8,0,0,16,0,4,16,36,0,52,8,130,128,8,0,0,0,0,10,0,2,40,64,0,34,32,2,0,2,0,0,0,0,0,48,4,32,128,0,32,0,0,2,96,0,0,0,0,64,10,0,33,64,0,0,0,66,0,32,0,0,192,138,0,0,0,70,0,129,128,0,66,32,0,0,16,64,0,0,0,0,97,0,34,0,6,0,0,32,8,0,1,200,128,48,0,41,128,0,128,0,224,0,0,0,0,2,0,64,0,148,0,0,32,72,8,0,96,0,36,128,25,48,33,0,128,16,0,0,4,2,128,4,32,144,0,20,0,0,0,16,2,0,4,0,2,8,0,0,128,0,16,0,0,128,0,0,16,0,128,0,72,16,0,129,0,80,132,91,116,53,37,131,157,118,157,131,122,18,0,131,48,97,222,132,96,175,157,102,151,214,131,1,10,2,132,103,101,116,104,134,103,111,49,46,49,54,133,108,105,110,117,120,160,218,71,54,233,233,153,85,103,64,10,4,159,150,224,130,134,111,78,188,224,102,166,96,148,216,222,134,254,219,185,88,110,136,87,173,68,252,252,248,190,64],"proof":[[248,177,160,174,171,108,131,83,47,244,139,23,122,146,226,84,189,175,114,176,131,196,80,85,155,220,172,151,31,138,121,78,34,1,37,160,104,209,167,107,221,53,22,163,251,61,251,80,40,239,108,253,251,47,253,90,163,103,58,194,173,111,232,90,174,223,154,156,160,185,232,110,109,245,242,193,69,113,230,64,155,37,7,166,98,0,174,149,27,3,242,254,162,87,27,39,206,191,90,97,39,160,156,171,231,120,50,202,239,195,248,47,226,150,143,78,94,254,151,195,12,90,54,253,126,104,200,94,222,173,155,24,75,214,128,128,128,128,160,77,84,120,31,175,114,100,6,171,254,190,44,236,141,143,126,33,139,92,41,101,166,10,135,52,237,241,45,228,121,210,252,128,128,128,128,128,128,128,128],[249,1,241,128,160,112,174,178,81,116,140,64,238,179,40,62,38,72,120,77,248,199,242,3,227,104,227,174,247,54,169,115,176,134,87,216,196,160,208,65,39,69,237,92,207,141,20,26,113,245,146,250,71,165,184,6,221,105,202,34,201,192,206,144,30,169,82,146,191,130,160,250,127,168,75,47,196,128,16,232,187,94,131,103,164,17,74,154,178,32,193,229,188,234,15,63,149,127,95,2,85,36,38,160,9,173,49,32,69,145,114,254,67,59,110,57,126,204,241,26,85,145,117,55,165,249,149,252,11,213,14,224,142,203,167,165,160,49,16,36,243,207,150,120,119,173,146,213,84,201,84,33,132,103,245,138,209,190,215,89,31,100,50,79,241,11,27,117,232,160,38,102,178,111,249,250,245,239,103,241,97,55,179,25,194,214,51,83,145,244,160,76,255,88,140,94,66,211,135,147,231,233,160,86,244,54,180,248,80,19,60,89,82,142,50,237,41,148,80,99,93,184,17,160,129,174,200,175,79,56,156,152,116,246,19,160,141,144,121,114,242,95,79,178,182,13,237,0,226,45,215,70,186,238,115,124,4,185,167,106,170,121,37,27,22,90,85,154,160,38,169,214,240,80,51,77,173,121,227,163,72,68,190,21,194,23,235,129,2,183,83,211,21,67,152,206,246,236,168,183,65,160,220,198,172,57,188,229,136,230,231,56,249,171,3,156,137,119,188,173,183,120,220,15,214,253,121,102,45,164,53,244,173,237,160,222,126,139,114,159,32,8,38,110,8,161,127,50,42,173,124,148,83,169,13,252,160,28,62,186,159,153,201,217,244,7,198,160,29,57,238,34,65,21,193,24,140,71,159,181,152,57,184,3,168,102,8,32,23,158,117,205,137,200,143,228,205,234,96,193,160,58,189,88,46,177,57,9,115,13,24,65,37,199,71,182,207,65,18,246,93,175,169,131,142,153,178,213,138,143,236,72,168,160,182,214,186,170,95,22,45,113,224,141,88,205,33,22,49,65,219,4,25,205,180,125,40,18,42,158,62,30,25,244,226,104,160,123,14,60,111,154,53,84,127,228,3,253,5,6,81,188,37,133,89,45,219,175,223,9,211,254,199,3,74,27,75,37,136,128],[249,2,48,32,185,2,44,249,2,41,1,131,1,110,54,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,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,0,0,0,0,0,0,0,0,1,174,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(contract.check_error_message(res, "The amount should be a positive numbe")); + assert!(!contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_to_near_amount_equal_fee_non_zero() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_near_amount_equal_fee_non_zero() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,1,44,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,0,0,0,1,44,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,1,44,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,0,0,0,1,44,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,10,160,218,232,90,75,133,17,151,21,23,64,121,155,74,131,239,243,28,65,81,101,213,156,148,217,134,34,235,41,62,11,232,147,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,25,127,76,71,206,220,252,85,22,156,38,36,158,35,56,3,255,85,230,138,132,44,102,196,217,205,43,20,129,6,50,114,160,217,211,225,144,113,34,139,65,28,148,21,243,90,204,109,152,98,172,147,56,158,109,65,77,74,110,116,227,7,143,157,97,160,35,108,188,133,254,137,74,53,234,147,11,115,83,161,215,174,6,192,214,61,8,113,178,151,91,57,163,102,121,177,113,30,185,1,0,144,48,72,0,8,0,0,0,48,0,0,1,128,128,128,0,128,128,0,8,64,2,1,0,5,1,0,32,64,16,129,8,0,16,8,8,128,1,9,8,4,0,0,104,0,0,0,24,8,0,4,0,8,0,0,0,0,128,64,32,16,32,0,0,92,2,8,0,10,1,80,24,1,0,0,8,17,1,0,40,0,0,5,0,130,17,0,0,6,0,0,1,128,0,2,16,40,0,96,16,2,2,0,0,0,0,32,8,0,64,40,65,0,0,32,0,0,8,0,0,2,0,0,112,0,0,0,4,8,0,64,2,0,0,5,0,161,212,88,1,5,0,0,32,8,0,2,32,0,0,2,136,0,0,4,66,34,0,128,0,2,8,128,0,0,0,0,128,44,8,0,0,19,20,2,8,2,0,8,128,132,0,0,0,0,56,0,0,0,4,33,32,32,129,0,2,0,0,128,145,64,0,96,112,136,2,32,0,32,16,0,0,65,0,84,16,64,2,0,16,161,0,34,128,128,16,0,0,8,16,2,12,2,0,0,18,64,4,128,0,152,0,44,0,8,0,0,0,64,0,32,148,0,16,128,0,132,91,126,153,161,131,157,118,120,131,122,18,0,131,55,185,255,132,96,175,155,143,140,115,112,105,100,101,114,49,48,1,2,9,64,160,29,62,139,98,163,60,78,159,159,190,165,213,126,42,39,157,104,12,168,1,9,24,24,157,45,96,113,188,166,18,114,253,136,161,226,143,133,82,9,96,55],"proof":[[248,145,160,153,98,12,82,79,154,121,176,11,226,192,161,140,213,198,195,143,185,79,36,156,98,17,141,146,111,76,206,149,161,186,244,160,29,41,24,128,95,59,50,57,188,69,166,227,81,94,29,115,178,144,71,219,248,16,233,179,158,64,222,175,67,156,221,186,160,221,78,89,28,71,2,204,57,50,75,194,224,88,108,127,122,110,247,48,111,72,110,252,199,127,138,177,160,1,244,75,250,128,128,128,128,128,160,96,141,238,91,85,76,114,97,220,74,251,25,18,72,46,126,72,190,245,222,173,235,62,157,59,131,133,200,217,240,218,101,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,106,251,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,0,1,44,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,0,0,0,1,44,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(res.is_success()); + assert!(contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_deposit_to_aurora_amount_equal_fee_non_zero() { - let custodian_address = "73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9"; - let contract = init(custodian_address).await.unwrap(); +async fn test_deposit_to_aurora_amount_equal_fee_non_zero() -> anyhow::Result<()> { + let contract = + TestContract::new_with_custodian("73c8931CA2aD746d97a59A7ABDDa0a9205F7ffF9").await?; let proof_str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,2,188,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,0,0,0,2,188,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,40,1,130,121,119,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,2,188,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,0,0,0,2,188,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,10,160,40,73,143,87,82,108,249,199,149,251,138,16,158,32,40,191,70,185,139,157,146,47,76,134,132,2,138,15,163,195,164,23,160,4,220,65,246,216,41,193,152,14,191,243,6,120,77,198,249,10,186,90,192,38,182,89,163,180,7,115,149,220,146,135,121,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,140,129,164,138,92,240,141,148,58,223,100,113,117,102,163,205,129,110,47,12,254,66,40,98,179,170,247,163,117,111,198,112,160,154,8,216,215,130,120,77,117,89,130,236,187,91,119,167,212,252,114,44,157,54,25,178,246,190,125,110,255,187,224,200,236,160,40,108,11,169,34,110,94,30,9,115,148,248,253,252,64,245,150,237,108,188,197,225,88,28,139,188,249,78,249,118,101,180,185,1,0,128,32,72,128,0,0,0,0,0,0,32,1,128,2,32,0,2,130,0,0,2,51,0,0,0,1,0,0,66,16,0,10,0,144,8,12,0,1,13,32,0,0,0,72,0,0,0,0,0,64,0,0,32,2,0,0,2,0,0,0,0,32,0,0,0,0,40,0,34,1,0,0,8,0,0,8,0,0,0,46,0,2,5,0,2,0,0,8,64,1,32,0,0,0,0,16,36,96,32,8,66,2,0,128,0,1,0,8,0,2,40,64,4,0,40,2,0,2,13,32,0,0,192,176,4,76,128,4,32,128,0,10,0,0,0,0,4,64,42,136,1,0,0,0,0,0,4,160,1,0,128,136,4,0,0,66,0,1,129,0,2,0,0,16,0,0,0,0,0,0,64,0,50,64,2,0,0,0,8,0,1,8,1,160,0,42,128,0,128,16,160,0,192,0,0,2,0,96,16,144,0,32,48,64,8,128,32,0,164,16,0,32,1,1,0,16,0,0,5,2,192,0,32,128,2,16,0,8,0,18,2,0,0,16,0,0,0,0,128,0,80,0,0,128,0,32,0,0,0,0,0,16,0,1,0,16,132,91,150,244,27,131,157,118,173,131,122,18,0,131,40,221,54,132,96,175,158,25,140,115,112,105,100,101,114,49,48,1,2,9,64,160,218,157,103,144,72,1,176,23,70,255,185,190,128,163,131,210,184,249,29,138,99,94,110,182,239,251,248,20,139,58,221,102,136,127,48,25,31,42,252,69,90],"proof":[[248,145,160,242,107,136,177,199,137,149,29,37,76,252,130,24,241,231,253,164,161,49,123,187,119,248,194,41,74,148,86,89,189,140,122,160,221,253,158,175,54,102,36,195,73,91,187,167,57,197,110,107,81,39,3,67,139,234,202,103,171,85,168,245,23,151,146,101,160,240,166,241,60,58,19,14,113,70,156,230,223,214,171,111,192,135,200,157,176,100,11,127,9,6,211,142,63,158,86,97,87,128,128,128,128,128,160,247,26,205,35,167,94,67,103,248,63,247,181,235,154,151,144,26,0,253,18,81,231,65,62,46,101,62,205,117,218,221,122,128,128,128,128,128,128,128,128],[249,2,47,48,185,2,43,249,2,40,1,130,121,119,185,1,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,115,200,147,28,162,173,116,109,151,165,154,122,189,218,10,146,5,247,255,249,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,2,188,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,0,0,0,2,188,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; - let proof = serde_json::from_str(proof_str).unwrap(); - let res = contract.deposit(proof).transact().await.err().unwrap(); - - assert_error_message( - &res, - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE_FOR_FEE", - ); - - assert_proof_was_not_used(&contract, proof_str).await; + let res = contract + .deposit_with_proof(&contract.get_proof(proof_str)) + .await?; + assert!(res.is_success()); + assert!(contract.call_is_used_proof(proof_str).await?); + Ok(()) } #[tokio::test] -async fn test_ft_transfer_max_value() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - let res = contract - .root() - .call(&contract.id(), "ft_transfer") +async fn test_ft_transfer_max_value() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let transfer_amount: U128 = u128::MAX.into(); + let receiver_id = contract.engine_contract.id(); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") .args_json(json!({ - "receiver_id": DEPOSITED_RECIPIENT, - "amount": u128::MAX.to_string(), + "receiver_id": &receiver_id, + "amount": transfer_amount, "memo": "transfer memo" })) - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_failure()); - assert_error_message( - &res.into_result().err().unwrap(), - "Smart contract panicked: ERR_NOT_ENOUGH_BALANCE", - ); + assert!(contract.check_error_message(res, "The account doesn't have enough balance")); + Ok(()) } #[tokio::test] -async fn test_ft_transfer_empty_value() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let result = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(result.is_success()); - - let res = contract - .root() - .call(&contract.id(), "ft_transfer") +async fn test_ft_transfer_empty_value() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let transfer_amount = ""; + let receiver_id = contract.engine_contract.id(); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") .args_json(json!({ - "receiver_id": DEPOSITED_RECIPIENT, - "amount": "", + "receiver_id": &receiver_id, + "amount": transfer_amount, "memo": "transfer memo" })) - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_failure()); - assert_error_message( - &res.into_result().err().unwrap(), - "Smart contract panicked: cannot parse integer from empty string", - ); + assert!(contract.check_error_message(res, "cannot parse integer from empty string")); + Ok(()) } #[tokio::test] -async fn test_ft_transfer_wrong_u128_json_type() { - let contract = init(CUSTODIAN_ADDRESS).await.unwrap(); - let res = call_deposit_eth_to_near(&contract).await.unwrap(); - assert!(res.is_success()); +async fn test_ft_transfer_wrong_u128_json_type() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + let transfer_amount = 200; + let receiver_id = AccountId::from_str(DEPOSITED_RECIPIENT).unwrap(); let res = contract - .root() - .call(&contract.id().as_ref().parse().unwrap(), "ft_transfer") + .engine_contract + .call("ft_transfer") .args_json(json!({ - "receiver_id": DEPOSITED_RECIPIENT, - "amount": 200, + "receiver_id": &receiver_id, + "amount": transfer_amount, "memo": "transfer memo" })) - .deposit(1) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) .transact() - .await - .unwrap(); + .await?; assert!(res.is_failure()); - assert_error_message( - &res.into_result().err().unwrap(), - "Smart contract panicked: Wait for a string but got: 200 at line 1 column 13", - ); + assert!(contract.check_error_message(res, "Wait for a string")); + Ok(()) } -/// Bytes for a NEAR smart contract implementing `ft_on_transfer` -fn dummy_ft_receiver_bytes() -> Vec { - let base_path = std::path::Path::new("../etc") - .join("tests") - .join("ft-receiver"); - let output_path = base_path.join("target/wasm32-unknown-unknown/release/ft_receiver.wasm"); - crate::utils::rust::compile(base_path); - std::fs::read(output_path).unwrap() -} - -async fn init(custodian_address: &str) -> anyhow::Result { - EngineContractBuilder::new()? - .with_code(crate::utils::AuroraRunner::default().code.code().to_vec()) - .with_owner_id(CONTRACT_ACC)? - .with_prover_id(PROVER_ACCOUNT)? - .with_custodian_address(custodian_address)? - .deploy_and_init() - .await -} - -async fn call_deposit_eth_to_near(contract: &EngineContract) -> anyhow::Result { - let proof = serde_json::from_str(PROOF_DATA_NEAR).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await?; - Ok(res.outcome().clone()) -} - -async fn call_is_used_proof(contract: &EngineContract, proof: &str) -> bool { - let proof = serde_json::from_str(proof).unwrap(); - contract.is_used_proof(proof).await.unwrap().result -} +#[tokio::test] +async fn test_ft_transfer_user() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + + let transfer_amount: U128 = 70.into(); + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + let receiver_id = contract.create_sub_account("some-acc").await?; + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") + .args_json(json!({ + "receiver_id": &receiver_id.id(), + "amount": transfer_amount, + "memo": "transfer memo" + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_success()); -async fn assert_proof_was_used(contract: &EngineContract, proof: &str) { - let is_used_proof = call_is_used_proof(contract, proof).await; - assert!( - is_used_proof, - "{}", - "Expected not to fail because the proof should have been already used", + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0, ); -} - -async fn assert_proof_was_not_used(contract: &EngineContract, proof: &str) { - let is_used_proof = call_is_used_proof(contract, proof).await; - assert!( - !is_used_proof, - "{}", - "Expected not to fail and to have an unused proof but it was already used", + assert_eq!( + contract.get_eth_on_near_balance(receiver_id.id()).await?.0, + transfer_amount.0, ); -} + assert_eq!(DEPOSITED_AMOUNT, contract.total_supply().await?); -async fn call_deposit_eth_to_aurora(contract: &EngineContract) { - let proof = serde_json::from_str(PROOF_DATA_ETH).unwrap(); - let res = contract.deposit(proof).max_gas().transact().await.unwrap(); + let transfer_amount2: U128 = 1000.into(); + let res = user_acc + .call(contract.engine_contract.id(), "ft_transfer") + .args_json(json!({ + "receiver_id": &receiver_id.id(), + "amount": transfer_amount2, + "memo": "transfer memo" + })) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; assert!(res.is_success()); + assert_eq!( + contract.get_eth_on_near_balance(receiver_id.id()).await?.0, + transfer_amount.0 + transfer_amount2.0, + ); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - transfer_amount.0 - transfer_amount2.0, + ); + Ok(()) } -async fn get_eth_on_near_balance(contract: &EngineContract, account: &str) -> u128 { - contract - .ft_balance_of(&account.parse().unwrap()) - .await - .unwrap() - .result - .0 -} - -async fn get_eth_balance(contract: &EngineContract, address: Address) -> u128 { - contract.ft_balance_of_eth(address).await.unwrap().result.0 -} - -async fn total_supply(contract: &EngineContract) -> u128 { - contract.ft_total_supply().await.unwrap().result.0 -} - -async fn total_eth_supply_on_near(contract: &EngineContract) -> u128 { - contract - .ft_total_eth_supply_on_near() - .await - .unwrap() - .result - .0 -} - -async fn total_eth_supply_on_aurora(contract: &EngineContract) -> u128 { - contract - .ft_total_eth_supply_on_aurora() - .await - .unwrap() - .result - .0 -} +#[tokio::test] +async fn test_withdraw_from_user() -> anyhow::Result<()> { + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; + let user_acc = contract + .create_sub_account(DEPOSITED_RECIPIENT_NAME) + .await?; + + let withdraw_amount = NEP141Wei::new(130); + let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS); + let res = user_acc + .call(contract.engine_contract.id(), "withdraw") + .args_borsh((recipient_addr, withdraw_amount)) + .gas(DEFAULT_GAS) + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_success()); -fn assert_error_message(err: &T, expected: &str) { - let err_msg = format!("{err:?}"); - assert!(err_msg.contains(expected)); + let data: WithdrawResult = res.borsh()?; + let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS); + assert_eq!(data.recipient_id, recipient_addr); + assert_eq!(data.amount, withdraw_amount); + assert_eq!(data.eth_custodian_address, custodian_addr); + assert_eq!( + contract.get_eth_on_near_balance(user_acc.id()).await?.0, + DEPOSITED_AMOUNT - withdraw_amount.as_u128() + ); + assert_eq!( + contract.total_supply().await?, + DEPOSITED_AMOUNT - withdraw_amount.as_u128(), + ); + Ok(()) } -fn generate_dummy_proof(message: &str, deposit_amount: u128, log_index: u64) -> Proof { - use aurora_engine::deposit_event::TokenMessageData; - - let eth_custodian_address = address_from_hex(CUSTODIAN_ADDRESS); - let fee: Fee = 0.into(); - let token_message_data = - TokenMessageData::parse_event_message_and_prepare_token_message_data(message, fee).unwrap(); - - let deposit_event = aurora_engine::deposit_event::DepositedEvent { - eth_custodian_address, - sender: Address::zero(), - token_message_data, - amount: NEP141Wei::new(deposit_amount), - fee, - }; - - let event_schema = ethabi::Event { - name: aurora_engine::deposit_event::DEPOSITED_EVENT.into(), - inputs: aurora_engine::deposit_event::DepositedEvent::event_params(), - anonymous: false, - }; - let log_entry = aurora_engine_types::parameters::connector::LogEntry { - address: eth_custodian_address.raw(), - topics: vec![ - event_schema.signature(), - // the sender is not important - H256::zero(), - ], - data: ethabi::encode(&[ - ethabi::Token::String(message.to_string()), - ethabi::Token::Uint(U256::from(deposit_event.amount.as_u128())), - ethabi::Token::Uint(U256::from(deposit_event.fee.as_u128())), - ]), - }; - - Proof { - log_index, - // Only this field matters for the purpose of this test - log_entry_data: rlp::encode(&log_entry).to_vec(), - receipt_index: 1, - ..Default::default() +#[tokio::test] +async fn test_ft_metadata() -> anyhow::Result<()> { + use aurora_engine_types::parameters::connector::FungibleTokenMetadata as ft_m; + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + pub struct FungibleTokenMetadata { + pub spec: String, + pub name: String, + pub symbol: String, + pub icon: Option, + pub reference: Option, + pub reference_hash: Option<[u8; 32]>, + pub decimals: u8, } -} -fn create_message(account_id: &str, address: &str, fee: u128) -> String { - let mut buffer = [0; 32]; - U256::from(fee).to_little_endian(&mut buffer); - let msg = [&buffer, address_from_hex(address).as_bytes()].concat(); + let contract = TestContract::new().await?; + contract.call_deposit_eth_to_near().await?; - [account_id, hex::encode(msg).as_str()].join(":") + let metadata = contract + .engine_contract + .call("ft_metadata") + .gas(DEFAULT_GAS) + .transact() + .await? + .into_result() + .unwrap() + .json::() + .unwrap(); + let m = ft_m::default(); + let reference_hash = m.reference_hash.map(|h| { + let x: [u8; 32] = h.as_ref().try_into().unwrap(); + x + }); + assert_eq!(metadata.spec, m.spec); + assert_eq!(metadata.decimals, m.decimals); + assert_eq!(metadata.icon, m.icon); + assert_eq!(metadata.name, m.name); + assert_eq!(metadata.reference, m.reference); + assert_eq!(metadata.reference_hash, reference_hash); + assert_eq!(metadata.symbol, m.symbol); + Ok(()) } diff --git a/engine-tests-connector/src/lib.rs b/engine-tests-connector/src/lib.rs new file mode 100644 index 000000000..6c95c980d --- /dev/null +++ b/engine-tests-connector/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +mod connector; +#[cfg(test)] +pub mod rust; +#[cfg(test)] +pub mod utils; diff --git a/engine-tests-connector/src/rust.rs b/engine-tests-connector/src/rust.rs new file mode 100644 index 000000000..1e0a0c85c --- /dev/null +++ b/engine-tests-connector/src/rust.rs @@ -0,0 +1,15 @@ +use std::path::Path; +use std::process::Command; + +pub fn compile>(source_path: P) { + let output = Command::new("cargo") + .current_dir(source_path) + .env("RUSTFLAGS", "-C link-arg=-s") + .args(["build", "--target", "wasm32-unknown-unknown", "--release"]) + .output() + .unwrap(); + + if !output.status.success() { + panic!("{}", String::from_utf8(output.stderr).unwrap()); + } +} diff --git a/engine-tests-connector/src/utils.rs b/engine-tests-connector/src/utils.rs new file mode 100644 index 000000000..85c017116 --- /dev/null +++ b/engine-tests-connector/src/utils.rs @@ -0,0 +1,352 @@ +use aurora_engine::parameters::{FungibleTokenMetadata, SetEthConnectorContractAccountArgs}; +use aurora_engine::proof::Proof; +use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; +use aurora_engine_types::types::{Address, Wei}; +use near_sdk::serde_json::json; +use near_sdk::{json_types::U128, serde_json}; +use std::path::Path; +use workspaces::network::NetworkClient; +use workspaces::{result::ExecutionFinalResult, Account, AccountId, Contract, Worker}; + +pub const PROOF_DATA_NEAR: &str = r#"{"log_index":0,"log_entry_data":[248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,6,1,130,107,17,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"header_data":[249,2,10,160,177,33,112,26,26,176,12,12,163,2,249,133,245,12,51,201,55,50,148,156,122,67,27,26,101,178,36,153,54,100,53,137,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,124,28,230,160,8,239,64,193,62,78,177,68,166,204,116,240,224,174,172,126,160,197,65,5,202,188,134,5,164,246,19,133,35,57,28,114,241,186,81,123,163,166,161,24,32,157,168,170,13,108,58,61,46,160,6,199,163,13,91,119,225,39,168,255,213,10,107,252,143,246,138,241,108,139,59,35,187,185,162,223,53,108,222,73,181,109,160,27,154,49,63,26,170,15,177,97,255,6,204,84,221,234,197,159,172,114,47,148,126,32,199,241,127,101,120,182,51,52,100,185,1,0,0,0,8,0,0,0,0,0,0,0,32,0,0,0,0,0,2,0,8,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,8,32,0,32,0,0,128,0,2,0,0,0,1,0,32,0,0,0,2,0,0,0,0,32,0,0,0,0,0,4,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,128,64,0,0,0,0,1,32,0,0,0,0,0,0,96,32,0,64,0,0,0,128,1,0,0,0,0,1,0,0,0,8,0,0,0,18,32,0,0,64,145,1,8,0,4,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,16,0,128,0,0,0,0,0,0,128,0,2,0,0,0,0,0,0,0,0,0,0,2,0,80,0,0,0,0,0,0,0,0,1,128,0,8,0,0,0,0,4,0,0,0,128,2,0,32,0,128,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,16,0,8,0,0,0,0,0,0,0,0,0,0,128,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,25,1,227,23,131,157,85,14,131,122,18,0,131,75,91,132,132,96,174,58,224,140,115,112,105,100,101,114,49,48,1,2,8,230,160,188,212,199,183,154,22,223,85,103,215,24,122,240,235,79,129,44,93,184,88,161,218,79,5,44,226,106,100,50,40,163,97,136,155,158,202,3,149,91,200,78],"proof":[[248,113,160,46,156,31,85,241,226,241,13,5,56,73,146,176,67,195,109,6,189,172,104,44,103,44,88,32,15,181,152,136,29,121,252,160,191,48,87,174,71,151,208,114,164,150,51,200,171,90,90,106,46,200,79,77,222,145,95,89,141,137,138,149,67,73,8,87,128,128,128,128,128,128,160,175,9,219,77,174,13,247,133,55,172,92,185,202,7,160,10,204,112,44,133,36,96,30,234,235,134,30,209,205,166,212,255,128,128,128,128,128,128,128,128],[249,2,13,48,185,2,9,249,2,6,1,130,107,17,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,248,251,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,160,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,0,0,0,0,96,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,0,0,12,54,144,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,0,0,0,1,144,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,0,0,0,0,18,101,116,104,95,114,101,99,105,112,105,101,110,116,46,114,111,111,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#; +pub const DEPOSITED_RECIPIENT: &str = "eth_recipient.root"; +pub const DEPOSITED_RECIPIENT_NAME: &str = "eth_recipient"; +pub const CUSTODIAN_ADDRESS: &str = "096DE9C2B8A5B8c22cEe3289B101f6960d68E51E"; +pub const DEFAULT_GAS: u64 = 300_000_000_000_000; +pub const DEPOSITED_AMOUNT: u128 = 800400; +pub const DEPOSITED_FEE: u128 = 400; +pub const RECIPIENT_ETH_ADDRESS: &str = "891b2749238b27ff58e951088e55b04de71dc374"; +pub const PROOF_DATA_ETH: &str = r#"{"log_index":0,"log_entry_data":[249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"receipt_index":0,"receipt_data":[249,2,40,1,130,121,129,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0],"header_data":[249,2,23,160,227,118,223,171,207,47,75,187,79,185,74,198,88,140,54,97,161,196,35,70,121,178,154,141,172,91,193,252,86,64,228,227,160,29,204,77,232,222,199,93,122,171,133,181,103,182,204,212,26,211,18,69,27,148,138,116,19,240,161,66,253,64,212,147,71,148,109,150,79,199,61,172,73,162,195,49,105,169,235,252,47,207,92,249,136,136,160,232,74,213,122,210,55,65,43,78,225,85,247,174,212,229,211,176,186,250,113,21,129,16,181,52,172,217,167,148,242,153,45,160,15,198,229,127,6,235,198,161,226,121,173,106,62,0,90,25,158,11,242,44,178,3,137,22,245,126,227,91,74,156,24,115,160,65,253,74,43,97,155,196,93,59,43,202,12,155,49,115,95,124,247,230,15,1,171,150,10,56,115,247,86,81,8,39,11,185,1,0,128,32,9,2,0,0,0,0,0,0,32,16,128,32,0,0,128,2,0,0,64,51,0,0,0,129,0,32,66,32,0,14,0,144,0,0,0,2,13,34,0,128,64,200,128,4,32,16,0,64,0,0,34,0,32,0,40,0,8,0,0,32,176,0,196,1,0,0,10,1,16,8,16,0,0,72,48,0,0,36,0,17,4,128,10,68,0,16,0,1,32,0,128,0,32,0,12,64,162,8,98,2,0,32,0,0,16,136,1,16,40,0,0,0,0,4,0,0,44,32,0,0,192,49,0,8,12,64,96,129,0,2,0,0,128,0,12,64,10,8,1,132,0,32,0,1,4,33,0,4,128,140,128,0,2,66,0,0,192,0,2,16,2,0,0,0,32,16,0,0,64,0,242,4,0,0,0,0,0,0,4,128,0,32,0,14,194,0,16,10,64,32,0,0,0,2,16,96,16,129,0,16,32,32,128,128,32,0,2,68,0,32,1,8,64,16,32,2,5,2,68,0,32,0,2,16,1,0,0,16,2,0,0,16,2,0,0,0,128,0,16,0,36,128,32,0,4,64,16,0,40,16,0,17,0,16,132,25,207,98,158,131,157,85,88,131,122,17,225,131,121,11,191,132,96,174,60,127,153,216,131,1,10,1,132,103,101,116,104,134,103,111,49,46,49,54,135,119,105,110,100,111,119,115,160,33,15,129,167,71,37,0,207,110,217,101,107,71,110,48,237,4,83,174,75,131,188,213,179,154,115,243,94,107,52,238,144,136,84,114,37,115,236,166,252,105],"proof":[[248,177,160,211,36,253,39,157,18,180,1,3,139,140,168,65,238,106,111,239,53,121,48,235,96,8,115,106,93,174,165,66,207,49,216,160,172,74,129,163,113,84,7,35,23,12,83,10,253,21,57,198,143,128,73,112,84,222,23,146,164,219,89,23,138,197,111,237,160,52,220,245,245,91,231,95,169,113,225,49,168,40,77,59,232,33,210,4,93,203,94,247,212,15,42,146,32,70,206,193,54,160,6,140,29,61,156,224,194,173,129,74,84,92,11,129,184,212,37,31,23,140,226,87,230,72,30,52,97,66,185,236,139,228,128,128,128,128,160,190,114,105,101,139,216,178,42,238,75,109,119,227,138,206,144,183,82,34,173,26,173,188,231,152,171,56,163,2,179,13,190,128,128,128,128,128,128,128,128],[249,2,47,48,185,2,43,249,2,40,1,130,121,129,185,1,0,0,0,8,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,128,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,8,0,0,0,0,0,0,0,0,0,0,0,0,0,128,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,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,1,30,249,1,27,148,9,109,233,194,184,165,184,194,44,238,50,137,177,1,246,150,13,104,229,30,248,66,160,209,66,67,156,39,142,37,218,217,165,7,102,241,83,208,227,210,215,191,43,209,111,194,120,28,75,212,148,178,177,90,157,160,0,0,0,0,0,0,0,0,0,0,0,0,121,24,63,219,216,14,45,138,234,26,202,162,246,123,251,138,54,212,10,141,184,192,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,0,0,0,0,96,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,0,0,0,39,216,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,0,0,0,0,200,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,0,0,0,0,59,101,116,104,95,99,111,110,110,101,99,116,111,114,46,114,111,111,116,58,56,57,49,66,50,55,52,57,50,51,56,66,50,55,102,70,53,56,101,57,53,49,48,56,56,101,53,53,98,48,52,100,101,55,49,68,99,51,55,52,0,0,0,0,0]]}"#; +pub const DEPOSITED_EVM_FEE: u128 = 200; +pub const DEPOSITED_EVM_AMOUNT: u128 = 10200; +pub const CONTRACT_ACC: &str = "eth_connector.root"; + +pub type PausedMask = u8; + +/// Admin control flow flag indicates that all control flow unpause (unblocked). +pub const UNPAUSE_ALL: PausedMask = 0; +/// Admin control flow flag indicates that the deposit is paused. +pub const PAUSE_DEPOSIT: PausedMask = 1 << 0; +/// Admin control flow flag indicates that withdrawal is paused. +pub const PAUSE_WITHDRAW: PausedMask = 1 << 1; + +pub struct TestContract { + pub engine_contract: Contract, + pub eth_connector_contract: Contract, + pub root_account: Account, +} + +impl TestContract { + pub async fn deploy_aurora_contract() -> anyhow::Result<(Contract, Contract, Account)> { + use workspaces::{ + types::{KeyType, SecretKey}, + AccessKey, + }; + let worker = workspaces::sandbox() + .await + .map_err(|err| anyhow::anyhow!("Failed init sandbox: {:?}", err))?; + let testnet = workspaces::testnet() + .await + .map_err(|err| anyhow::anyhow!("Failed init testnet: {:?}", err))?; + let registrar: AccountId = "registrar".parse()?; + let sk = SecretKey::from_seed(KeyType::ED25519, registrar.as_str()); + let registrar = worker + .import_contract(®istrar, &testnet) + .transact() + .await?; + Self::waiting_account_creation(&worker, registrar.id()).await?; + + let root: AccountId = "root".parse()?; + registrar + .as_account() + .batch(&root) + .create_account() + .add_key(sk.public_key(), AccessKey::full_access()) + .transfer(near_units::parse_near!("100 N")) + .transact() + .await? + .into_result()?; + + let root_account = Account::from_secret_key(root, sk, &worker); + let eth_connector = root_account + .create_subaccount("aurora_eth_connector") + .initial_balance(near_units::parse_near!("15 N")) + .transact() + .await? + .into_result()?; + let engine = root_account + .create_subaccount("eth_connector") + .initial_balance(near_units::parse_near!("15 N")) + .transact() + .await? + .into_result()?; + let engine_contract_bytes = get_engine_contract(); + let engine_contract = engine.deploy(&engine_contract_bytes).await?.into_result()?; + let eth_connector_contract = eth_connector + .deploy(&get_eth_connector_contract()) + .await? + .into_result()?; + + Ok((engine_contract, eth_connector_contract, root_account)) + } + + pub async fn new() -> anyhow::Result { + Self::new_with_custodian(CUSTODIAN_ADDRESS).await + } + + pub async fn new_with_owner(owner: AccountId) -> anyhow::Result { + Self::new_contract(CUSTODIAN_ADDRESS, Some(owner)).await + } + + pub async fn new_with_custodian(eth_custodian_address: &str) -> anyhow::Result { + Self::new_contract(eth_custodian_address, None).await + } + + async fn new_contract( + eth_custodian_address: &str, + owner: Option, + ) -> anyhow::Result { + let (engine_contract, eth_connector_contract, root_account) = + Self::deploy_aurora_contract().await?; + + let prover_account: AccountId = eth_connector_contract.id().clone(); + let metadata = FungibleTokenMetadata::default(); + let account_with_access_right: AccountId = engine_contract.id().clone(); + // Init eth-connector + let metadata = json!({ + "spec": metadata.spec, + "name": metadata.name, + "symbol": metadata.symbol, + "icon": metadata.icon, + "reference": metadata.reference, + "decimals": metadata.decimals, + }); + let owner_id = owner.unwrap_or_else(|| account_with_access_right.clone()); + let res = eth_connector_contract + .call("new") + .args_json(json!({ + "prover_account": prover_account, + "eth_custodian_address": eth_custodian_address, + "metadata": metadata, + "account_with_access_right": account_with_access_right, + "owner_id": owner_id, + })) + .gas(DEFAULT_GAS) + .transact() + .await?; + assert!(res.is_success()); + + let chain_id = [0u8; 32]; + let res = engine_contract + .call("new") + .args_borsh((chain_id, engine_contract.id(), engine_contract.id(), 1_u64)) + .gas(DEFAULT_GAS) + .transact() + .await?; + assert!(res.is_success()); + + let metadata = FungibleTokenMetadata::default(); + let res = engine_contract + .call("new_eth_connector") + .args_borsh((prover_account, eth_custodian_address, metadata)) + .gas(DEFAULT_GAS) + .transact() + .await?; + assert!(res.is_success()); + + let acc = SetEthConnectorContractAccountArgs { + account: eth_connector_contract.id().parse().unwrap(), + }; + let res = engine_contract + .call("set_eth_connector_contract_account") + .args_borsh(acc) + .gas(DEFAULT_GAS) + .transact() + .await?; + assert!(res.is_success()); + + Ok(Self { + engine_contract, + eth_connector_contract, + root_account, + }) + } + + /// Waiting for the account creation + async fn waiting_account_creation( + worker: &Worker, + account_id: &AccountId, + ) -> anyhow::Result<()> { + let timer = std::time::Instant::now(); + // Try to get account within 30 secs + for _ in 0..60 { + if worker.view_account(account_id).await.is_err() { + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + } else { + return Ok(()); + } + } + + anyhow::bail!( + "Account `{}` was not created in {:?} sec", + account_id, + timer.elapsed() + ) + } + + pub fn get_proof(&self, proof: &str) -> Proof { + serde_json::from_str(proof).unwrap() + } + + pub async fn create_sub_account(&self, name: &str) -> anyhow::Result { + Ok(self + .root_account + .create_subaccount(name) + .initial_balance(near_units::parse_near!("15 N")) + .transact() + .await? + .into_result()?) + } + + pub async fn deposit_with_proof(&self, proof: &Proof) -> anyhow::Result { + Ok(self + .engine_contract + .call("deposit") + .args_borsh(proof) + .gas(DEFAULT_GAS) + .transact() + .await?) + } + + pub async fn call_deposit_eth_to_near(&self) -> anyhow::Result<()> { + let proof: Proof = self.get_proof(PROOF_DATA_NEAR); + let res = self.deposit_with_proof(&proof).await?; + assert!(res.is_success()); + Ok(()) + } + + pub async fn call_deposit_eth_to_aurora(&self) -> anyhow::Result<()> { + let proof: Proof = serde_json::from_str(PROOF_DATA_ETH).unwrap(); + let res = self.deposit_with_proof(&proof).await?; + assert!(res.is_success()); + Ok(()) + } + + pub async fn user_deposit_with_proof( + &self, + user: &Account, + proof: &Proof, + ) -> anyhow::Result { + Ok(user + .call(self.engine_contract.id(), "deposit") + .args_borsh(proof) + .gas(DEFAULT_GAS) + .transact() + .await?) + } + + pub fn check_error_message(&self, res: ExecutionFinalResult, error_msg: &str) -> bool { + let mut is_failure = false; + for out in res.receipt_outcomes() { + is_failure = out.is_failure(); + if is_failure { + return format!("{:?}", res).contains(error_msg); + } + } + is_failure + } + + pub async fn call_is_used_proof(&self, proof: &str) -> anyhow::Result { + let proof: Proof = serde_json::from_str(proof).unwrap(); + let res = self + .engine_contract + .call("is_used_proof") + .args_borsh(proof) + .gas(DEFAULT_GAS) + .transact() + .await? + .into_result() + .unwrap() + .borsh::() + .unwrap(); + Ok(res) + } + + pub async fn get_eth_on_near_balance(&self, account: &AccountId) -> anyhow::Result { + let res = self + .engine_contract + .call("ft_balance_of") + .args_json((account,)) + .gas(DEFAULT_GAS) + .transact() + .await? + .into_result() + .unwrap() + .json::() + .unwrap(); + Ok(res) + } + + pub async fn get_eth_balance(&self, address: &Address) -> anyhow::Result { + #[derive(BorshSerialize, BorshDeserialize)] + pub struct BalanceOfEthCallArgs { + pub address: Address, + } + let args = BalanceOfEthCallArgs { address: *address }.try_to_vec()?; + let res = self + .engine_contract + .call("ft_balance_of_eth") + .args(args) + .gas(DEFAULT_GAS) + .transact() + .await?; + + res.into_result() + .unwrap() + .json::() + .map_err(Into::into) + .and_then(|res| { + res.try_into_u128() + .map_err(|e| anyhow::anyhow!(e.to_string())) + }) + } + + pub async fn total_supply(&self) -> anyhow::Result { + let res = self + .engine_contract + .call("ft_total_supply") + .gas(DEFAULT_GAS) + .transact() + .await? + .into_result() + .unwrap() + .json::() + .unwrap(); + Ok(res.0) + } +} + +pub fn print_logs(res: ExecutionFinalResult) { + for log in res.logs().iter() { + println!("\t[LOG] {}", log); + } +} + +pub fn validate_eth_address(address: &str) -> Address { + Address::decode(address).unwrap() +} + +pub fn get_eth_connector_contract() -> Vec { + let contract_path = Path::new("etc/aurora-eth-connector"); + std::fs::read(contract_path.join("bin/aurora-eth-connector-test.wasm")).unwrap() +} + +fn get_engine_contract() -> Vec { + if cfg!(feature = "mainnet-test") { + std::fs::read("../bin/aurora-mainnet-test.wasm").unwrap() + } else if cfg!(feature = "testnet-test") { + std::fs::read("../bin/aurora-testnet-test.wasm").unwrap() + } else { + panic!("AuroraRunner requires mainnet-test or testnet-test feature enabled.") + } +} diff --git a/engine-tests/Cargo.toml b/engine-tests/Cargo.toml index bc6c53197..271ac2ba9 100644 --- a/engine-tests/Cargo.toml +++ b/engine-tests/Cargo.toml @@ -15,6 +15,7 @@ aurora-engine = { workspace = true, features = ["std", "tracing", "impl-serde"] aurora-engine-modexp = { workspace = true, features = ["std"] } aurora-engine-precompiles = { workspace = true, features = ["std"] } aurora-engine-sdk = { workspace = true, features = ["std"] } +aurora-engine-standalone-nep141-legacy.workspace = true aurora-engine-test-doubles.workspace = true aurora-engine-transactions = { workspace = true, features = ["std", "impl-serde"] } aurora-engine-types = { workspace = true, features = ["std", "impl-serde"] } @@ -37,6 +38,7 @@ libsecp256k1.workspace = true near-crypto.workspace = true near-primitives-core.workspace = true near-primitives.workspace = true +near-sdk.workspace = true near-vm-errors.workspace = true near-vm-logic.workspace = true near-vm-runner.workspace = true diff --git a/engine-tests/src/prelude.rs b/engine-tests/src/prelude.rs index 15525645d..f9f0d1373 100644 --- a/engine-tests/src/prelude.rs +++ b/engine-tests/src/prelude.rs @@ -1,6 +1,5 @@ mod v0 { pub use aurora_engine::connector; - pub use aurora_engine::fungible_token; #[cfg(feature = "meta-call")] pub use aurora_engine::meta_parsing; pub use aurora_engine::parameters; diff --git a/engine-tests/src/tests/contract_call.rs b/engine-tests/src/tests/contract_call.rs index d061059f5..ce2d8ef6d 100644 --- a/engine-tests/src/tests/contract_call.rs +++ b/engine-tests/src/tests/contract_call.rs @@ -2,7 +2,7 @@ use crate::prelude::{parameters::SubmitResult, vec, Address, Wei, H256, U256}; use crate::utils::solidity::exit_precompile::{ Tester, TesterConstructor, DEST_ACCOUNT, DEST_ADDRESS, }; -use crate::utils::{self, AuroraRunner, Signer, ORIGIN}; +use crate::utils::{self, deploy_runner, AuroraRunner, Signer, ORIGIN}; fn setup_test() -> (AuroraRunner, Signer, Address, Tester) { let mut runner = AuroraRunner::new(); @@ -35,7 +35,7 @@ fn setup_test() -> (AuroraRunner, Signer, Address, Tester) { #[test] #[should_panic] fn test_deploy_erc20_token_with_invalid_account_id() { - let mut runner = AuroraRunner::new(); + let mut runner = deploy_runner(); let invalid_nep141 = "_"; runner.deploy_erc20_token(invalid_nep141); } diff --git a/engine-tests/src/tests/erc20_connector.rs b/engine-tests/src/tests/erc20_connector.rs index 9bddd2305..8bb4872dc 100644 --- a/engine-tests/src/tests/erc20_connector.rs +++ b/engine-tests/src/tests/erc20_connector.rs @@ -2,6 +2,7 @@ use crate::prelude::{Address, Balance, Wei, WeiU256, U256}; use crate::utils::{self, create_eth_transaction, AuroraRunner, ORIGIN}; use aurora_engine::engine::EngineError; use aurora_engine::parameters::{CallArgs, FunctionCallArgsV2}; +use aurora_engine::proof::Proof; use aurora_engine_transactions::legacy::LegacyEthSignedTransaction; use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; use aurora_engine_types::parameters::engine::{SubmitResult, TransactionStatus}; @@ -368,9 +369,10 @@ mod workspace { use crate::utils::solidity::exit_precompile::TesterConstructor; use crate::utils::workspace::{ create_sub_account, deploy_engine, deploy_erc20_from_nep_141, deploy_nep_141, - nep_141_balance_of, transfer_nep_141_to_erc_20, + init_eth_connector, nep_141_balance_of, transfer_nep_141_to_erc_20, }; use aurora_engine::parameters::{CallArgs, FunctionCallArgsV2}; + use aurora_engine_types::parameters::connector::Proof; use aurora_engine_types::parameters::engine::TransactionStatus; use aurora_engine_workspace::account::Account; use aurora_engine_workspace::{parse_near, EngineContract, RawContract}; @@ -561,10 +563,12 @@ mod workspace { nep_141_balance_of(aurora.as_raw_contract(), &aurora.id()).await, u128::from(INITIAL_ETH_BALANCE - ETH_EXIT_AMOUNT) ); + assert_eq!( nep_141_balance_of(aurora.as_raw_contract(), &exit_account_id.parse().unwrap()).await, - u128::from(ETH_EXIT_AMOUNT) + ETH_EXIT_AMOUNT.into() ); + assert_eq!( eth_balance_of(signer_address, &aurora).await, Wei::new_u64(INITIAL_ETH_BALANCE - ETH_EXIT_AMOUNT) @@ -649,9 +653,10 @@ mod workspace { .transact() .await?; assert!(result.is_success()); + let _nep141_contract = deploy_aurora_eth_connector(&aurora).await; - let balance = aurora.ft_balance_of(&aurora.id()).await?.result; - assert_eq!(balance.0, u128::from(INITIAL_ETH_BALANCE)); + let balance = nep_141_balance_of(aurora.as_raw_contract(), &aurora.id()).await; + assert_eq!(balance, u128::from(INITIAL_ETH_BALANCE)); let balance = eth_balance_of(signer_address, &aurora).await; assert_eq!(balance, Wei::new_u64(INITIAL_ETH_BALANCE)); @@ -794,6 +799,21 @@ mod workspace { } } + async fn deploy_aurora_eth_connector(aurora: &EngineContract) { + init_eth_connector(aurora).await.unwrap(); + + let proof: Proof = super::create_test_proof( + INITIAL_ETH_BALANCE, + aurora.id().as_ref(), + "096de9c2b8a5b8c22cee3289b101f6960d68e51e", + ); + let result = aurora.deposit(proof).max_gas().transact().await.unwrap(); + assert!(result.is_success()); + + let balance = nep_141_balance_of(aurora.as_raw_contract(), &aurora.id()).await; + assert_eq!(balance, u128::from(INITIAL_ETH_BALANCE)); + } + struct TestExitToNearContext { ft_owner: Account, ft_owner_address: Address, @@ -810,3 +830,51 @@ mod workspace { aurora: EngineContract, } } + +fn create_test_proof(deposit_amount: u64, recipient_id: &str, custodian_address: &str) -> Proof { + use aurora_engine::deposit_event::TokenMessageData; + use aurora_engine_types::types::{Fee, NEP141Wei}; + + let eth_custodian_address: Address = Address::decode(custodian_address).unwrap(); + + let message = recipient_id.to_string(); + let fee: Fee = Fee::new(NEP141Wei::new(0)); + let token_message_data = + TokenMessageData::parse_event_message_and_prepare_token_message_data(&message, fee) + .unwrap(); + + let deposit_event = aurora_engine::deposit_event::DepositedEvent { + eth_custodian_address, + sender: Address::zero(), + token_message_data, + amount: NEP141Wei::new(deposit_amount.into()), + fee, + }; + + let event_schema = ethabi::Event { + name: aurora_engine::deposit_event::DEPOSITED_EVENT.into(), + inputs: aurora_engine::deposit_event::DepositedEvent::event_params(), + anonymous: false, + }; + let log_entry = aurora_engine_types::parameters::connector::LogEntry { + address: eth_custodian_address.raw(), + topics: vec![ + event_schema.signature(), + // the sender is not important + crate::prelude::H256::zero(), + ], + data: ethabi::encode(&[ + Token::String(message), + Token::Uint(U256::from(deposit_event.amount.as_u128())), + Token::Uint(U256::from(deposit_event.fee.as_u128())), + ]), + }; + Proof { + log_index: 1, + log_entry_data: rlp::encode(&log_entry).to_vec(), + receipt_index: 1, + receipt_data: Vec::new(), + header_data: Vec::new(), + proof: Vec::new(), + } +} diff --git a/engine-tests/src/tests/mod.rs b/engine-tests/src/tests/mod.rs index 7adb5cb1c..e222b231b 100644 --- a/engine-tests/src/tests/mod.rs +++ b/engine-tests/src/tests/mod.rs @@ -3,7 +3,6 @@ mod contract_call; mod ecrecover; mod erc20; mod erc20_connector; -mod eth_connector; mod ghsa_3p69_m8gg_fwmf; #[cfg(feature = "meta-call")] mod meta_parsing; diff --git a/engine-tests/src/tests/one_inch.rs b/engine-tests/src/tests/one_inch.rs index 805648635..94b2a900f 100644 --- a/engine-tests/src/tests/one_inch.rs +++ b/engine-tests/src/tests/one_inch.rs @@ -84,7 +84,7 @@ fn test_1inch_liquidity_protocol() { }, ); assert!(result.gas_used >= 150_000); // more than 150k EVM gas used - assert_gas_bound(profile.all_gas(), 20); + assert_gas_bound(profile.all_gas(), 19); } #[test] diff --git a/engine-tests/src/tests/sanity.rs b/engine-tests/src/tests/sanity.rs index d5e6539fa..1c9cdb9d9 100644 --- a/engine-tests/src/tests/sanity.rs +++ b/engine-tests/src/tests/sanity.rs @@ -2,7 +2,6 @@ use crate::prelude::{Address, U256}; use crate::prelude::{Wei, ERC20_MINT_SELECTOR}; use crate::utils::{self, str_to_account_id}; use aurora_engine::engine::{EngineErrorKind, GasPaymentError, ZERO_ADDRESS_FIX_HEIGHT}; -use aurora_engine::fungible_token::FungibleTokenMetadata; use aurora_engine::parameters::{SetOwnerArgs, SetUpgradeDelayBlocksArgs, TransactionStatus}; use aurora_engine_sdk as sdk; use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; @@ -44,16 +43,6 @@ fn test_total_supply_accounting() { constructor.deployed_at(contract_address) }; - let get_total_supply = |runner: &mut utils::AuroraRunner| -> Wei { - let result = runner.call("ft_total_eth_supply_on_aurora", "aurora", Vec::new()); - let amount: u128 = String::from_utf8(result.unwrap().return_data.as_value().unwrap()) - .unwrap() - .replace('"', "") - .parse() - .unwrap(); - Wei::new(U256::from(amount)) - }; - // Self-destruct with some benefactor does not reduce the total supply let contract = deploy_contract(&mut runner, &mut signer); let _submit_result = runner @@ -66,7 +55,6 @@ fn test_total_supply_accounting() { }) .unwrap(); assert_eq!(runner.get_balance(benefactor), TRANSFER_AMOUNT); - assert_eq!(get_total_supply(&mut runner), INITIAL_BALANCE); // Self-destruct with self benefactor burns any ETH in the destroyed contract let contract = deploy_contract(&mut runner, &mut signer); @@ -79,10 +67,6 @@ fn test_total_supply_accounting() { ) }) .unwrap(); - assert_eq!( - get_total_supply(&mut runner), - INITIAL_BALANCE - TRANSFER_AMOUNT - ); } #[test] @@ -939,18 +923,6 @@ fn test_block_hash_contract() { utils::panic_on_fail(result.status); } -#[test] -fn test_ft_metadata() { - let mut runner = utils::deploy_runner(); - let account_id: String = runner.context.signer_account_id.clone().into(); - let outcome = runner.call("ft_metadata", &account_id, Vec::new()).unwrap(); - let metadata = - serde_json::from_slice::(&outcome.return_data.as_value().unwrap()) - .unwrap(); - - assert_eq!(metadata, FungibleTokenMetadata::default()); -} - /// Tests transfer Eth from one account to another with custom argument `max_gas_price`. #[test] fn test_eth_transfer_with_max_gas_price() { diff --git a/engine-tests/src/tests/standalone/sync.rs b/engine-tests/src/tests/standalone/sync.rs index 9371bdb57..0d0bd2853 100644 --- a/engine-tests/src/tests/standalone/sync.rs +++ b/engine-tests/src/tests/standalone/sync.rs @@ -477,7 +477,10 @@ fn sample_block() -> sync::types::BlockMessage { } fn initialize() -> (StandaloneRunner, sync::types::BlockMessage) { - let mut runner = StandaloneRunner::default(); + let mut runner = StandaloneRunner { + init_legacy_connector: true, + ..Default::default() + }; runner.init_evm(); let block_message = sample_block(); diff --git a/engine-tests/src/tests/xcc.rs b/engine-tests/src/tests/xcc.rs index 382312179..9f20d7868 100644 --- a/engine-tests/src/tests/xcc.rs +++ b/engine-tests/src/tests/xcc.rs @@ -370,7 +370,7 @@ mod workspace { use crate::utils; use crate::utils::workspace::{ create_sub_account, deploy_engine, deploy_erc20_from_nep_141, deploy_nep_141, - nep_141_balance_of, transfer_nep_141_to_erc_20, + init_eth_connector, nep_141_balance_of, transfer_nep_141_to_erc_20, }; use aurora_engine_precompiles::xcc::cross_contract_call; use aurora_engine_transactions::legacy::TransactionLegacy; @@ -687,7 +687,11 @@ mod workspace { engine_balance_after_xcc.max(engine_balance_before_xcc) - engine_balance_after_xcc.min(engine_balance_before_xcc) < 10_000_000_000_000_000_000_000, - "Engine lost too much NEAR funding xcc: Before={engine_balance_before_xcc} After={engine_balance_after_xcc}", + "Engine lost too much NEAR funding xcc: Before={:?} After={:?} Eq={:?}", + engine_balance_before_xcc, + engine_balance_after_xcc, + engine_balance_after_xcc.max(engine_balance_before_xcc) + - engine_balance_after_xcc.min(engine_balance_before_xcc) ); let router_balance = aurora.node.get_balance(&router_account_id).await.unwrap(); @@ -736,6 +740,8 @@ mod workspace { let aurora = deploy_engine().await; let chain_id = aurora.get_chain_id().await?.result.as_u64(); + init_eth_connector(&aurora).await.unwrap(); + let xcc_wasm_bytes = super::contract_bytes(); let result = aurora.factory_update(xcc_wasm_bytes).transact().await?; assert!(result.is_success()); @@ -831,7 +837,7 @@ mod workspace { } async fn get_engine_near_balance(aurora: &EngineContract) -> u128 { - aurora.ft_balance_of(&aurora.id()).await.unwrap().result.0 + nep_141_balance_of(aurora.as_raw_contract(), &aurora.id()).await } async fn deploy_wnear(aurora: &EngineContract) -> anyhow::Result { diff --git a/engine-tests/src/utils/mod.rs b/engine-tests/src/utils/mod.rs index 2a0087f60..a1f184e28 100644 --- a/engine-tests/src/utils/mod.rs +++ b/engine-tests/src/utils/mod.rs @@ -1,8 +1,9 @@ use aurora_engine::engine::{EngineError, EngineErrorKind, GasPaymentError}; -use aurora_engine::parameters::{SubmitArgs, ViewCallArgs}; +use aurora_engine::parameters::{SetEthConnectorContractAccountArgs, SubmitArgs, ViewCallArgs}; use aurora_engine_types::account_id::AccountId; use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; -use aurora_engine_types::types::{NEP141Wei, PromiseResult}; +use aurora_engine_types::parameters::engine::LegacyNewCallArgs; +use aurora_engine_types::types::PromiseResult; use evm::ExitFatal; use libsecp256k1::{self, Message, PublicKey, SecretKey}; use near_primitives::runtime::config_store::RuntimeConfigStore; @@ -19,10 +20,7 @@ use near_vm_runner::MockCompiledContractCache; use rlp::RlpStream; use std::borrow::Cow; -use crate::prelude::fungible_token::{FungibleToken, FungibleTokenMetadata}; -use crate::prelude::parameters::{ - InitCallArgs, LegacyNewCallArgs, SubmitResult, TransactionStatus, -}; +use crate::prelude::parameters::{SubmitResult, TransactionStatus}; use crate::prelude::transactions::{ eip_1559::{self, SignedTransaction1559, Transaction1559}, eip_2930::{self, SignedTransaction2930, Transaction2930}, @@ -309,51 +307,10 @@ impl AuroraRunner { trie.insert(code_key.to_vec(), code); } - let ft_key = crate::prelude::storage::bytes_to_key( - crate::prelude::storage::KeyPrefix::EthConnector, - &[crate::prelude::storage::EthConnectorStorageId::FungibleToken.into()], - ); - let ft_value = { - let mut current_ft: FungibleToken = trie - .get(&ft_key) - .map(|bytes| FungibleToken::try_from_slice(bytes).unwrap()) - .unwrap_or_default(); - current_ft.total_eth_supply_on_near = - current_ft.total_eth_supply_on_near + NEP141Wei::new(init_balance.raw().as_u128()); - current_ft.total_eth_supply_on_aurora = current_ft.total_eth_supply_on_aurora - + NEP141Wei::new(init_balance.raw().as_u128()); - current_ft - }; - - let aurora_balance_key = [ - ft_key.as_slice(), - self.context.current_account_id.as_ref().as_bytes(), - ] - .concat(); - let aurora_balance_value = { - let mut current_balance: u128 = trie - .get(&aurora_balance_key) - .map(|bytes| u128::try_from_slice(bytes).unwrap()) - .unwrap_or_default(); - current_balance += init_balance.raw().as_u128(); - current_balance - }; - - let proof_key = crate::prelude::storage::bytes_to_key( - crate::prelude::storage::KeyPrefix::EthConnector, - &[crate::prelude::storage::EthConnectorStorageId::UsedEvent.into()], - ); - trie.insert(balance_key.to_vec(), balance_value.to_vec()); if !init_nonce.is_zero() { trie.insert(nonce_key.to_vec(), nonce_value.to_vec()); } - trie.insert(ft_key, ft_value.try_to_vec().unwrap()); - trie.insert(proof_key, vec![0]); - trie.insert( - aurora_balance_key, - aurora_balance_value.try_to_vec().unwrap(), - ); if let Some(standalone_runner) = &mut self.standalone_runner { standalone_runner.env.block_height = self.context.block_height; @@ -650,13 +607,14 @@ pub fn deploy_runner() -> AuroraRunner { assert!(result.is_ok()); - let args = InitCallArgs { - prover_account: str_to_account_id("prover.near"), - eth_custodian_address: "d045f7e19B2488924B97F9c145b5E51D0D895A65".to_string(), - metadata: FungibleTokenMetadata::default(), + let args = SetEthConnectorContractAccountArgs { + account: AccountId::new("aurora_eth_connector.root").unwrap(), }; - let result = runner.call("new_eth_connector", &account_id, args.try_to_vec().unwrap()); - + let result = runner.call( + "set_eth_connector_contract_account", + &account_id, + args.try_to_vec().unwrap(), + ); assert!(result.is_ok()); let mut standalone_runner = standalone::StandaloneRunner::default(); diff --git a/engine-tests/src/utils/standalone/mocks/mod.rs b/engine-tests/src/utils/standalone/mocks/mod.rs index 0d1f35249..ebf0783c4 100644 --- a/engine-tests/src/utils/standalone/mocks/mod.rs +++ b/engine-tests/src/utils/standalone/mocks/mod.rs @@ -1,18 +1,16 @@ use crate::utils; -use aurora_engine::fungible_token::FungibleTokenMetadata; -use aurora_engine::parameters::{ - FinishDepositCallArgs, InitCallArgs, NEP141FtOnTransferArgs, NewCallArgsV2, -}; +use aurora_engine::parameters::InitCallArgs; use aurora_engine::{engine, state}; use aurora_engine_sdk::env::{Env, DEFAULT_PREPAID_GAS}; use aurora_engine_sdk::io::IO; -use aurora_engine_types::types::{make_address, Address, Balance, NEP141Wei, NearGas, Wei}; +use aurora_engine_types::parameters::connector::FungibleTokenMetadata; +use aurora_engine_types::parameters::engine::NewCallArgsV2; +use aurora_engine_types::types::{make_address, Address, Wei}; use aurora_engine_types::{account_id::AccountId, H256, U256}; use engine_standalone_storage::{BlockMetadata, Storage}; pub mod block; -const DEFAULT_GAS: u64 = 300_000_000_000_000; pub const ETH_CUSTODIAN_ADDRESS: Address = make_address(0xd045f7e1, 0x9b2488924b97f9c145b5e51d0d895a65); @@ -49,6 +47,8 @@ pub fn default_env(block_height: u64) -> aurora_engine_sdk::env::Fixed { } pub fn init_evm(mut io: I, env: &E, chain_id: u64) { + use aurora_engine::admin_controlled::AdminControlled; + let new_args = NewCallArgsV2 { chain_id: aurora_engine_types::types::u256_to_arr(&U256::from(chain_id)), owner_id: env.current_account_id(), @@ -57,15 +57,20 @@ pub fn init_evm(mut io: I, env: &E, chain_id: u64) { state::set_state(&mut io, &new_args.into()).unwrap(); + let mut connector = aurora_engine::connector::EthConnectorContract::init_instance(io).unwrap(); + connector.set_eth_connector_contract_account(&"aurora_eth_connector.root".parse().unwrap()); +} + +pub fn init_legacy_connector(io: I, env: &E) { let connector_args = InitCallArgs { prover_account: utils::str_to_account_id("prover.near"), eth_custodian_address: ETH_CUSTODIAN_ADDRESS.encode(), metadata: FungibleTokenMetadata::default(), }; - aurora_engine::connector::EthConnectorContract::create_contract( + aurora_engine_standalone_nep141_legacy::legacy_connector::EthConnectorContract::create_contract( io, - &env.current_account_id(), + env.current_account_id(), connector_args, ) .map_err(unsafe_to_string) @@ -77,14 +82,14 @@ pub fn mint_evm_account( balance: Wei, nonce: U256, code: Option>, - mut io: I, + io: I, env: &E, ) { use evm::backend::ApplyBackend; let aurora_account_id = env.current_account_id(); let mut engine: engine::Engine<_, _> = - engine::Engine::new(address, aurora_account_id.clone(), io, env).unwrap(); + engine::Engine::new(address, aurora_account_id, io, env).unwrap(); let state_change = evm::backend::Apply::Modify { address: address.raw(), basic: evm::backend::Basic { @@ -96,44 +101,6 @@ pub fn mint_evm_account( reset_storage: false, }; - let deposit_args = FinishDepositCallArgs { - new_owner_id: aurora_account_id.clone(), - amount: NEP141Wei::new(balance.raw().as_u128()), - proof_key: String::new(), - relayer_id: aurora_account_id.clone(), - fee: 0.into(), - msg: None, - }; - - // Delete the fake proof so that we can use it again. - let proof_key = crate::prelude::storage::bytes_to_key( - crate::prelude::storage::KeyPrefix::EthConnector, - &[crate::prelude::storage::EthConnectorStorageId::UsedEvent.into()], - ); - io.remove_storage(&proof_key); - - let mut connector = aurora_engine::connector::EthConnectorContract::init_instance(io).unwrap(); - connector - .finish_deposit( - aurora_account_id.clone(), - aurora_account_id.clone(), - deposit_args, - NearGas::new(DEFAULT_GAS), - ) - .map_err(unsafe_to_string) - .unwrap(); - - let transfer_args = NEP141FtOnTransferArgs { - sender_id: aurora_account_id, - amount: Balance::new(balance.raw().as_u128()), - msg: format!( - "aurora:{}{}", - hex::encode(Wei::zero().to_bytes()), - hex::encode(address.as_bytes()) - ), - }; - connector.ft_on_transfer(&engine, &transfer_args).unwrap(); - engine.apply(std::iter::once(state_change), std::iter::empty(), false); } diff --git a/engine-tests/src/utils/standalone/mod.rs b/engine-tests/src/utils/standalone/mod.rs index 8a41ed5f7..b5e2205eb 100644 --- a/engine-tests/src/utils/standalone/mod.rs +++ b/engine-tests/src/utils/standalone/mod.rs @@ -32,6 +32,7 @@ pub struct StandaloneRunner { pub chain_id: u64, // Cumulative diff from all transactions (ie full state representation) pub cumulative_diff: Diff, + pub init_legacy_connector: bool, } impl StandaloneRunner { @@ -49,8 +50,12 @@ impl StandaloneRunner { env.block_height += 1; let transaction_hash = H256::zero(); let tx_msg = Self::template_tx_msg(storage, env, 0, transaction_hash, &[]); + let init_legacy_connector = self.init_legacy_connector; let result = storage.with_engine_access(env.block_height, 0, &[], |io| { mocks::init_evm(io, env, chain_id); + if init_legacy_connector { + mocks::init_legacy_connector(io, env); + } }); let outcome = sync::TransactionIncludedOutcome { hash: transaction_hash, @@ -509,6 +514,7 @@ impl Default for StandaloneRunner { env, chain_id, cumulative_diff: Diff::default(), + init_legacy_connector: false, } } } diff --git a/engine-tests/src/utils/workspace.rs b/engine-tests/src/utils/workspace.rs index 58008edad..5edb0182a 100644 --- a/engine-tests/src/utils/workspace.rs +++ b/engine-tests/src/utils/workspace.rs @@ -5,6 +5,7 @@ use crate::utils::solidity::erc20::{ERC20Constructor, ERC20}; /// it does not execute promises; but `aurora-workspaces` does. use crate::utils::AuroraRunner; use aurora_engine_types::account_id::AccountId; +use aurora_engine_types::parameters::connector::FungibleTokenMetadata; use aurora_engine_types::types::Address; use aurora_engine_types::U256; use aurora_engine_workspace::account::Account; @@ -13,6 +14,7 @@ use serde_json::json; const FT_PATH: &str = "src/tests/res/fungible_token.wasm"; const STORAGE_AMOUNT: u128 = 50_000_000_000_000_000_000_000_000; +const AURORA_ETH_CONNECTOR: &str = "aurora_eth_connector"; pub async fn deploy_engine() -> EngineContract { let aurora_runner = AuroraRunner::default(); @@ -29,6 +31,41 @@ pub async fn deploy_engine() -> EngineContract { .unwrap() } +/// Deploy and init external eth connector +pub async fn init_eth_connector(aurora: &EngineContract) -> anyhow::Result<()> { + let contract_bytes = get_aurora_eth_connector_contract(); + let contract_account = aurora + .root() + .create_subaccount(AURORA_ETH_CONNECTOR, 15 * STORAGE_AMOUNT) + .await + .unwrap(); + let contract = contract_account.deploy(&contract_bytes).await.unwrap(); + let metadata = FungibleTokenMetadata::default(); + let init_args = json!({ + "prover_account": contract_account.id(), + "eth_custodian_address": "096DE9C2B8A5B8c22cEe3289B101f6960d68E51E", + "metadata": metadata, + "account_with_access_right": aurora.id(), + "owner_id": aurora.id() + }); + + let result = contract + .call("new") + .args_json(init_args) + .max_gas() + .transact() + .await?; + assert!(result.is_success()); + + let result = aurora + .set_eth_connector_contract_account(contract_account.id()) + .transact() + .await?; + assert!(result.is_success()); + + Ok(()) +} + pub async fn create_sub_account( master_account: &Account, account: &str, @@ -85,12 +122,14 @@ pub async fn transfer_nep_141_to_erc_20( pub async fn nep_141_balance_of(nep_141: &RawContract, account_id: &AccountId) -> u128 { nep_141 - .view("ft_balance_of") + .call("ft_balance_of") // XCC requires gas .args_json(json!({ "account_id": account_id })) + .max_gas() + .transact() .await .unwrap() - .json::() - .map(|s| s.parse().unwrap()) + .json::() + .map(|s| s.0) .unwrap() } @@ -129,3 +168,9 @@ pub async fn deploy_nep_141( Ok(nep141) } + +fn get_aurora_eth_connector_contract() -> Vec { + use std::path::Path; + let contract_path = Path::new("../engine-tests-connector/etc/aurora-eth-connector"); + std::fs::read(contract_path.join("bin/aurora-eth-connector-test.wasm")).unwrap() +} diff --git a/engine-types/src/parameters/connector.rs b/engine-types/src/parameters/connector.rs index 5fa2483ce..3ccc53665 100644 --- a/engine-types/src/parameters/connector.rs +++ b/engine-types/src/parameters/connector.rs @@ -149,6 +149,24 @@ pub struct NEP141FtOnTransferArgs { pub msg: String, } +#[derive(BorshSerialize)] +pub struct EngineWithdrawCallArgs { + pub sender_id: AccountId, + pub recipient_address: Address, + pub amount: NEP141Wei, +} + +/// `storage_unregister` eth-connector call args +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq)] +pub struct StorageUnregisterCallArgs { + pub force: Option, +} + +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)] +pub struct SetEthConnectorContractAccountArgs { + pub account: AccountId, +} + /// Fungible token Reference hash type. /// Used for `FungibleTokenMetadata` #[derive(Debug, BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, PartialEq, Eq)] diff --git a/engine-types/src/storage.rs b/engine-types/src/storage.rs index 4dfa80e2d..05bde7667 100644 --- a/engine-types/src/storage.rs +++ b/engine-types/src/storage.rs @@ -63,6 +63,8 @@ pub enum EthConnectorStorageId { PausedMask = 0x3, StatisticsAuroraAccountsCounter = 0x4, FungibleTokenMetadata = 0x5, + EthConnectorAccount = 0x6, + DisableLegacyNEP141 = 0x7, } impl From for u8 { @@ -74,6 +76,8 @@ impl From for u8 { EthConnectorStorageId::PausedMask => 0x3, EthConnectorStorageId::StatisticsAuroraAccountsCounter => 0x4, EthConnectorStorageId::FungibleTokenMetadata => 0x5, + EthConnectorStorageId::EthConnectorAccount => 0x6, + EthConnectorStorageId::DisableLegacyNEP141 => 0x7, } } } diff --git a/engine-types/src/types/balance.rs b/engine-types/src/types/balance.rs index 4a37b10c0..d5b217f63 100644 --- a/engine-types/src/types/balance.rs +++ b/engine-types/src/types/balance.rs @@ -67,18 +67,7 @@ impl<'de> Deserialize<'de> for Balance { } #[derive( - Default, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - Debug, - Clone, - Copy, - Eq, - PartialEq, - Ord, - PartialOrd, + Default, BorshSerialize, BorshDeserialize, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, )] /// Near Yocto type which wraps an underlying u128. /// 1 NEAR = 10^24 `yoctoNEAR` @@ -104,6 +93,34 @@ impl Yocto { } } +impl Serialize for Yocto { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let value = self.0.to_string(); + serializer.serialize_str(&value) + } +} + +impl<'de> Deserialize<'de> for Yocto { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + D::Error: serde::de::Error, + { + use serde::de::Error; + + let value = serde_json::Value::deserialize(deserializer)?; + Ok(Self( + value + .as_str() + .ok_or_else(|| Error::custom(format!("Wait for a string but got: {value}"))) + .and_then(|value| value.parse().map_err(Error::custom))?, + )) + } +} + impl Add for Yocto { type Output = Self; diff --git a/engine-types/src/types/wei.rs b/engine-types/src/types/wei.rs index 4baefa5d4..ba891ca74 100644 --- a/engine-types/src/types/wei.rs +++ b/engine-types/src/types/wei.rs @@ -9,7 +9,7 @@ use borsh_compat::{self as borsh, BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub const ZERO_NEP141_WEI: NEP141Wei = NEP141Wei::new(0); -pub const ZERO_WEI: Wei = Wei::new_u64(0); +pub const ZERO_WEI: Wei = Wei::zero(); /// Wei compatible Borsh-encoded raw value to attach an ETH balance to the transaction pub type WeiU256 = [u8; 32]; diff --git a/engine-workspace/src/contract.rs b/engine-workspace/src/contract.rs index 000ba8531..b70b7ef02 100644 --- a/engine-workspace/src/contract.rs +++ b/engine-workspace/src/contract.rs @@ -5,14 +5,15 @@ use crate::operation::{ CallDeposit, CallFactorySetWNearAddress, CallFactoryUpdate, CallFactoryUpdateAddressVersion, CallFtOnTransfer, CallFtTransfer, CallFtTransferCall, CallFundXccSubAccount, CallMintAccount, CallNew, CallNewEthConnector, CallPausePrecompiles, CallRefundOnError, CallRegisterRelayer, - CallRemoveRelayerKey, CallResumePrecompiles, CallSetEthConnectorContractData, - CallSetKeyManager, CallSetPausedFlags, CallStageUpgrade, CallStateMigration, - CallStorageDeposit, CallStorageUnregister, CallStorageWithdraw, CallSubmit, CallWithdraw, - ViewAccountsCounter, ViewBalance, ViewBlockHash, ViewBridgeProver, ViewChainId, ViewCode, - ViewErc20FromNep141, ViewFtBalanceOf, ViewFtBalanceOfEth, ViewFtMetadata, - ViewFtTotalEthSupplyOnAurora, ViewFtTotalEthSupplyOnNear, ViewFtTotalSupply, ViewIsUsedProof, - ViewNep141FromErc20, ViewNonce, ViewOwner, ViewPausedFlags, ViewPausedPrecompiles, - ViewStorageAt, ViewStorageBalanceOf, ViewUpgradeIndex, ViewVersion, ViewView, + CallRemoveRelayerKey, CallResumePrecompiles, CallSetEthConnectorContractAccount, + CallSetEthConnectorContractData, CallSetKeyManager, CallSetPausedFlags, CallStageUpgrade, + CallStateMigration, CallStorageDeposit, CallStorageUnregister, CallStorageWithdraw, CallSubmit, + CallWithdraw, ViewAccountsCounter, ViewBalance, ViewBlockHash, ViewBridgeProver, ViewChainId, + ViewCode, ViewErc20FromNep141, ViewFtBalanceOf, ViewFtBalanceOfEth, ViewFtMetadata, + ViewFtTotalEthSupplyOnAurora, ViewFtTotalEthSupplyOnNear, ViewFtTotalSupply, + ViewGetEthConnectorContractAccount, ViewIsUsedProof, ViewNep141FromErc20, ViewNonce, ViewOwner, + ViewPausedFlags, ViewPausedPrecompiles, ViewStorageAt, ViewStorageBalanceOf, ViewUpgradeIndex, + ViewVersion, ViewView, }; use crate::transaction::{CallTransaction, ViewTransaction}; use aurora_engine_types::account_id::AccountId; @@ -158,6 +159,13 @@ impl EngineContract { )) } + pub fn set_eth_connector_contract_account( + &self, + account_id: AccountId, + ) -> CallSetEthConnectorContractAccount { + CallSetEthConnectorContractAccount::call(&self.contract).args_borsh(account_id) + } + pub fn factory_update_address_version( &self, address: Address, @@ -390,6 +398,10 @@ impl EngineContract { pub fn get_accounts_counter(&self) -> ViewAccountsCounter { ViewAccountsCounter::view(&self.contract) } + + pub fn get_eth_connector_contract_account(&self) -> ViewGetEthConnectorContractAccount { + ViewGetEthConnectorContractAccount::view(&self.contract) + } } #[derive(Debug, Clone)] diff --git a/engine-workspace/src/operation.rs b/engine-workspace/src/operation.rs index 0a084694f..c87c8cd8b 100644 --- a/engine-workspace/src/operation.rs +++ b/engine-workspace/src/operation.rs @@ -39,6 +39,10 @@ impl_call_return![ (CallSetKeyManager, Call::SetKeyManager), (CallAddRelayerKey, Call::AddRelayerKey), (CallRemoveRelayerKey, Call::RemoveRelayerKey), + ( + CallSetEthConnectorContractAccount, + Call::SetEthConnectorContractAccount + ), ]; impl_call_return![ @@ -78,7 +82,8 @@ impl_view_return![ (ViewErc20FromNep141 => Address, View::Erc20FromNep141, borsh), (ViewNep141FromErc20 => AccountId, View::Nep141FromErc20, borsh), (ViewPausedFlags => u8, View::PausedFlags, borsh), - (ViewAccountsCounter => u64, View::AccountsCounter, borsh) + (ViewAccountsCounter => u64, View::AccountsCounter, borsh), + (ViewGetEthConnectorContractAccount => AccountId, View::GetEthConnectorContractAccount, borsh) ]; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -108,6 +113,7 @@ pub(crate) enum Call { FundXccSubAccount, FactorySetWNearAddress, SetEthConnectorContractData, + SetEthConnectorContractAccount, FactoryUpdateAddressVersion, RefundOnError, MintAccount, @@ -144,6 +150,7 @@ impl AsRef for Call { Call::FundXccSubAccount => "fund_xcc_sub_account", Call::FactorySetWNearAddress => "factory_set_wnear_address", Call::SetEthConnectorContractData => "set_eth_connector_contract_data", + Call::SetEthConnectorContractAccount => "set_eth_connector_contract_account", Call::FactoryUpdateAddressVersion => "factory_update_address_version", Call::RefundOnError => "refund_on_error", Call::MintAccount => "mint_account", @@ -181,6 +188,7 @@ pub enum View { Erc20FromNep141, Nep141FromErc20, AccountsCounter, + GetEthConnectorContractAccount, } impl AsRef for View { @@ -210,6 +218,7 @@ impl AsRef for View { View::Erc20FromNep141 => "get_erc20_from_nep141", View::Nep141FromErc20 => "get_nep141_from_erc20", View::AccountsCounter => "get_accounts_counter", + View::GetEthConnectorContractAccount => "get_eth_connector_contract_account", } } } diff --git a/engine/src/admin_controlled.rs b/engine/src/admin_controlled.rs index 99f4aa214..819f6b1d9 100644 --- a/engine/src/admin_controlled.rs +++ b/engine/src/admin_controlled.rs @@ -1,130 +1,6 @@ -pub type PausedMask = u8; - -pub const ERR_PAUSED: &str = "ERR_PAUSED"; +use aurora_engine_types::account_id::AccountId; pub trait AdminControlled { - /// Return the current mask representing all paused events. - fn get_paused(&self) -> PausedMask; - - /// Update mask with all paused events. - /// Implementor is responsible for guaranteeing that this function can only be - /// called by owner of the contract. - fn set_paused(&mut self, paused: PausedMask); - - /// Return if the contract is paused for the current flag and user - fn is_paused(&self, flag: PausedMask, is_owner: bool) -> bool { - (self.get_paused() & flag) != 0 && !is_owner - } - - /// Asserts the passed paused flag is not set. Returns `PausedError` if paused. - fn assert_not_paused(&self, flag: PausedMask, is_owner: bool) -> Result<(), PausedError> { - if self.is_paused(flag, is_owner) { - Err(PausedError) - } else { - Ok(()) - } - } -} - -pub struct PausedError; - -impl AsRef<[u8]> for PausedError { - fn as_ref(&self) -> &[u8] { - ERR_PAUSED.as_bytes() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - struct MockAdminControlled { - mask: PausedMask, - } - - impl MockAdminControlled { - pub const fn new() -> Self { - Self { mask: 0 } - } - } - - impl AdminControlled for MockAdminControlled { - fn get_paused(&self) -> PausedMask { - self.mask - } - - fn set_paused(&mut self, paused: PausedMask) { - self.mask = paused; - } - } - - #[test] - fn test_setting_paused_mask_with_1_bit_marks_it_as_paused() { - let is_owner = false; - let mask = 1u8; - let mut admin_controlled = MockAdminControlled::new(); - - assert!(!admin_controlled.is_paused(mask, is_owner)); - admin_controlled.set_paused(mask); - assert!(admin_controlled.is_paused(mask, is_owner)); - } - - #[test] - fn test_setting_paused_mask_with_0_bit_marks_it_as_not_paused() { - let is_owner = false; - let mask = 1u8; - let mut admin_controlled = MockAdminControlled::new(); - admin_controlled.set_paused(mask); - - assert!(admin_controlled.is_paused(mask, is_owner)); - admin_controlled.set_paused(0u8); - assert!(!admin_controlled.is_paused(mask, is_owner)); - } - - #[test] - fn test_setting_paused_mask_with_1_bit_fails_to_assert_not_paused() { - let is_owner = false; - let mask = 1u8; - let admin_controlled = MockAdminControlled::new(); - - let result = admin_controlled.assert_not_paused(mask, is_owner); - assert!(result.is_ok(), "asserting as paused failed"); - } - - #[test] - fn test_setting_paused_mask_with_0_bit_asserts_not_paused() { - let is_owner = false; - let mask = 1u8; - let mut admin_controlled = MockAdminControlled::new(); - - admin_controlled.set_paused(mask); - let error = admin_controlled - .assert_not_paused(mask, is_owner) - .expect_err("asserting as not paused failed"); - - let expected_error_message = b"ERR_PAUSED"; - let actual_error_message = error.as_ref(); - assert_eq!(expected_error_message, actual_error_message); - } - - #[test] - fn test_paused_mask_has_no_effect_on_owner() { - let is_owner = true; - let mask = 1u8; - let mut admin_controlled = MockAdminControlled::new(); - - admin_controlled.set_paused(mask); - assert!(!admin_controlled.is_paused(mask, is_owner)); - } - - #[test] - fn test_asserting_paused_mask_has_no_effect_on_owner() { - let is_owner = true; - let mask = 1u8; - let mut admin_controlled = MockAdminControlled::new(); - - admin_controlled.set_paused(mask); - let result = admin_controlled.assert_not_paused(mask, is_owner); - assert!(result.is_ok(), "asserting as not paused failed"); - } + fn get_eth_connector_contract_account(&self) -> AccountId; + fn set_eth_connector_contract_account(&mut self, account: &AccountId); } diff --git a/engine/src/connector.rs b/engine/src/connector.rs index 2616938b2..9b7cbcd4a 100644 --- a/engine/src/connector.rs +++ b/engine/src/connector.rs @@ -1,42 +1,37 @@ -use crate::admin_controlled::{AdminControlled, PausedMask}; -use crate::deposit_event::{DepositedEvent, FtTransferMessageData, TokenMessageData}; -use crate::engine::Engine; -use crate::fungible_token::{self, FungibleToken, FungibleTokenMetadata, FungibleTokenOps}; +use crate::admin_controlled::AdminControlled; use crate::parameters::{ - BalanceOfCallArgs, BalanceOfEthCallArgs, FinishDepositCallArgs, InitCallArgs, - NEP141FtOnTransferArgs, PauseEthConnectorCallArgs, ResolveTransferCallArgs, - SetContractDataCallArgs, StorageBalanceOfCallArgs, StorageDepositCallArgs, - StorageWithdrawCallArgs, TransferCallArgs, TransferCallCallArgs, WithdrawResult, -}; -use crate::prelude::{ - address::error::AddressError, NEP141Wei, String, Wei, ZERO_NEP141_WEI, ZERO_WEI, + BalanceOfEthCallArgs, FungibleTokenMetadata, NEP141FtOnTransferArgs, SetContractDataCallArgs, }; +use crate::prelude::PromiseCreateArgs; +use crate::prelude::{address::error::AddressError, Wei}; + +use crate::deposit_event::FtTransferMessageData; +use crate::engine::Engine; +use crate::parameters::errors::ParseTypeFromJsonError; use crate::prelude::{ - format, sdk, str, AccountId, Address, BorshDeserialize, BorshSerialize, EthConnectorStorageId, - KeyPrefix, NearGas, PromiseResult, ToString, Vec, WithdrawCallArgs, Yocto, ERR_FAILED_PARSE, + sdk, str, AccountId, Address, EthConnectorStorageId, KeyPrefix, NearGas, String, ToString, Vec, + Yocto, }; -use crate::prelude::{PromiseBatchAction, PromiseCreateArgs, PromiseWithCallbackArgs}; -use crate::proof::Proof; use aurora_engine_modexp::ModExpAlgorithm; -use aurora_engine_sdk::env::Env; +use aurora_engine_sdk::env::{Env, DEFAULT_PREPAID_GAS}; use aurora_engine_sdk::io::{StorageIntermediate, IO}; -use aurora_engine_types::borsh; +use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; +use aurora_engine_types::parameters::connector::Proof; +use aurora_engine_types::types::ZERO_WEI; +use error::DepositError; pub const ERR_NOT_ENOUGH_BALANCE_FOR_FEE: &str = "ERR_NOT_ENOUGH_BALANCE_FOR_FEE"; /// Indicate zero attached balance for promise call pub const ZERO_ATTACHED_BALANCE: Yocto = Yocto::new(0); /// NEAR Gas for calling `finish_deposit` promise. Used in the `deposit` logic. pub const GAS_FOR_FINISH_DEPOSIT: NearGas = NearGas::new(50_000_000_000_000); +pub const GAS_FOR_DEPOSIT: NearGas = NearGas::new(120_000_000_000_000); +pub const GAS_FOR_WITHDRAW: NearGas = NearGas::new(20_000_000_000_000); +pub const GAS_FOR_FT_TRANSFER: NearGas = NearGas::new(50_000_000_000_000); +pub const GAS_FOR_FT_TRANSFER_CALL: NearGas = NearGas::new(100_000_000_000_000); +pub const VIEW_CALL_GAS: NearGas = NearGas::new(15_000_000_000_000); /// NEAR Gas for calling `verify_log_entry` promise. Used in the `deposit` logic. -// Note: Is 40Tgas always enough? -const GAS_FOR_VERIFY_LOG_ENTRY: NearGas = NearGas::new(40_000_000_000_000); - -/// Admin control flow flag indicates that all control flow unpause (unblocked). -pub const UNPAUSE_ALL: PausedMask = 0; -/// Admin control flow flag indicates that the deposit is paused. -pub const PAUSE_DEPOSIT: PausedMask = 1 << 0; -/// Admin control flow flag indicates that withdrawal is paused. -pub const PAUSE_WITHDRAW: PausedMask = 1 << 1; +// Note: Is 40 Tgas always enough? /// Eth-connector contract data. It's stored in the storage. /// Contains: @@ -45,9 +40,6 @@ pub const PAUSE_WITHDRAW: PausedMask = 1 << 1; /// * `paused_mask` - admin control flow data /// * io - I/O trait handler pub struct EthConnectorContract { - contract: EthConnector, - ft: FungibleTokenOps, - paused_mask: PausedMask, io: I, } @@ -58,7 +50,7 @@ pub struct EthConnector { /// The account id of the Prover NEAR smart contract. It used in the Deposit flow for verifying /// a log entry from incoming proof. pub prover_account: AccountId, - /// It is Ethereum address used in the Deposit and Withdraw logic. + /// It is Ethereum address used in the Deposit and Withdraw logic. pub eth_custodian_address: Address, } @@ -66,516 +58,78 @@ impl EthConnectorContract { /// Init Eth-connector contract instance. /// Load contract data from storage and init I/O handler. /// Used as single point of contract access for various contract actions - pub fn init_instance(io: I) -> Result { - Ok(Self { - contract: get_contract_data(&io, EthConnectorStorageId::Contract)?, - ft: get_contract_data::(&io, EthConnectorStorageId::FungibleToken)? - .ops(io), - paused_mask: get_contract_data(&io, EthConnectorStorageId::PausedMask)?, - io, - }) + pub const fn init_instance(io: I) -> Result { + Ok(Self { io }) } /// Create contract data - init eth-connector contract specific data. /// Used only once for first time initialization. /// Initialized contract data stored in the storage. - pub fn create_contract( - mut io: I, - owner_id: &AccountId, - args: InitCallArgs, - ) -> Result<(), error::InitContractError> { - // Check is it already initialized - let contract_key_exists = - io.storage_has_key(&construct_contract_key(EthConnectorStorageId::Contract)); - if contract_key_exists { - return Err(error::InitContractError::AlreadyInitialized); - } - - sdk::log!("[init contract]"); - - let contract_data = set_contract_data( - &mut io, - SetContractDataCallArgs { - prover_account: args.prover_account, - eth_custodian_address: args.eth_custodian_address, - metadata: args.metadata, - }, - ) - .map_err(error::InitContractError::InvalidCustodianAddress)?; - - let mut ft = FungibleTokenOps::new(io); - // Register FT account for current contract - ft.internal_register_account(owner_id); - - let paused_mask = UNPAUSE_ALL; - io.write_borsh( - &construct_contract_key(EthConnectorStorageId::PausedMask), - &paused_mask, - ); - - Self { - contract: contract_data, - ft, - paused_mask, - io, - } - .save_ft_contract(); - + pub const fn create_contract() -> Result<(), error::InitContractError> { + // NOTE: do nothing Ok(()) } - /// Deposit all types of tokens. - pub fn deposit( - &self, - raw_proof: Vec, - current_account_id: AccountId, - predecessor_account_id: AccountId, - ) -> Result { - // Check if the current account is owner. - let is_owner = current_account_id == predecessor_account_id; - // Check if the deposit flow isn't paused. If it's owner just skip it. - self.assert_not_paused(PAUSE_DEPOSIT, is_owner) - .map_err(|_| error::DepositError::Paused)?; - - sdk::log!("[Deposit tokens]"); - - // Get incoming deposit arguments - let proof = - Proof::try_from_slice(&raw_proof).map_err(|_| error::DepositError::ProofParseFailed)?; - // Fetch event data from Proof - let event = DepositedEvent::from_log_entry_data(&proof.log_entry_data) - .map_err(error::DepositError::EventParseFailed)?; - - sdk::log!( - "Deposit started: from {} to recipient {:?} with amount: {:?} and fee {:?}", - event.sender.encode(), - event.token_message_data.recipient(), - event.amount, - event.fee - ); - - sdk::log!( - "Event's address {}, custodian address {}", - event.eth_custodian_address.encode(), - self.contract.eth_custodian_address.encode(), - ); - - if event.eth_custodian_address != self.contract.eth_custodian_address { - return Err(error::DepositError::CustodianAddressMismatch); - } - - if NEP141Wei::new(event.fee.as_u128()) >= event.amount { - return Err(error::DepositError::InsufficientAmountForFee); - } - - // Verify the proof data by sending cross-contract call to the prover smart contract. - sdk::log!( - "Deposit verify_log_entry for prover: {}", - self.contract.prover_account, - ); - - // Do not skip bridge call. This is only used for development and diagnostics. - let skip_bridge_call = false.try_to_vec().unwrap(); - let proof_to_verify = [raw_proof, skip_bridge_call].concat(); - - let verify_call = PromiseCreateArgs { - target_account_id: self.contract.prover_account.clone(), - method: "verify_log_entry".to_string(), - args: proof_to_verify, - attached_balance: ZERO_ATTACHED_BALANCE, - attached_gas: GAS_FOR_VERIFY_LOG_ENTRY, - }; - - // Finalize deposit - let data = match event.token_message_data { - // Deposit to NEAR accounts - TokenMessageData::Near(account_id) => FinishDepositCallArgs { - new_owner_id: account_id, - amount: event.amount, - proof_key: proof_key(&proof), - relayer_id: predecessor_account_id, - fee: event.fee, - msg: None, - } - .try_to_vec() - .unwrap(), - // Deposit to Eth accounts - // fee is being minted in the `ft_on_transfer` callback method - TokenMessageData::Eth { - receiver_id, - message, - } => { - // Transfer to self and then transfer ETH in `ft_on_transfer` - // address - is NEAR account - let transfer_data = TransferCallCallArgs { - receiver_id, - amount: event.amount, - memo: None, - msg: message.encode(), - } - .try_to_vec() - .unwrap(); - - // Send to self - current account id - FinishDepositCallArgs { - new_owner_id: current_account_id.clone(), - amount: event.amount, - proof_key: proof_key(&proof), - relayer_id: predecessor_account_id, - fee: event.fee, - msg: Some(transfer_data), - } - .try_to_vec() - .unwrap() - } - }; - - let finish_call = PromiseCreateArgs { - target_account_id: current_account_id, - method: "finish_deposit".to_string(), + /// Deposit all types of tokens + pub fn deposit(&self, data: Vec) -> PromiseCreateArgs { + sdk::log!("Call Deposit"); + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "deposit".to_string(), args: data, attached_balance: ZERO_ATTACHED_BALANCE, - attached_gas: GAS_FOR_FINISH_DEPOSIT, - }; - Ok(PromiseWithCallbackArgs { - base: verify_call, - callback: finish_call, - }) - } - - /// Finish deposit flow (private method). - /// NOTE: In the mint methods could occur an error while calculating the amount to be - /// credited and therefore we invoke `record_proof` after mint methods to avoid saving - /// proof before minting which could be potentially finished with error. - pub fn finish_deposit( - &mut self, - predecessor_account_id: AccountId, - current_account_id: AccountId, - data: FinishDepositCallArgs, - prepaid_gas: NearGas, - ) -> Result, error::FinishDepositError> { - sdk::log!("Finish deposit with the amount: {}", data.amount); - - // Mint tokens to recipient minus fee - if let Some(msg) = data.msg { - // Mint - calculate new balances - self.mint_eth_on_near(&data.new_owner_id, data.amount)?; - // Store proof only after `mint` calculations - self.record_proof(&data.proof_key)?; - // Save new contract data - self.save_ft_contract(); - let transfer_call_args = TransferCallCallArgs::try_from_slice(&msg).unwrap(); - let promise = self.ft_transfer_call( - predecessor_account_id, - current_account_id, - transfer_call_args, - prepaid_gas, - )?; - Ok(Some(promise)) - } else { - // Mint - calculate new balances - self.mint_eth_on_near( - &data.new_owner_id, - // it's safe subtracting because we check that the amount is greater than fee in - // the deposit method. - data.amount - NEP141Wei::new(data.fee.as_u128()), - )?; - self.mint_eth_on_near(&data.relayer_id, NEP141Wei::new(data.fee.as_u128()))?; - // Store proof only after `mint` calculations - self.record_proof(&data.proof_key)?; - // Save new contract data - self.save_ft_contract(); - Ok(None) - } - } - - /// Internal `ETH` withdraw (ETH on Aurora). - pub(crate) fn internal_remove_eth( - &mut self, - amount: Wei, - ) -> Result<(), fungible_token::error::WithdrawError> { - self.burn_eth_on_aurora(amount)?; - self.save_ft_contract(); - Ok(()) - } - - /// Record hash of the used proof in the storage. - fn record_proof(&mut self, key: &str) -> Result<(), error::ProofUsed> { - sdk::log!("Record proof: {}", key); - - if self.is_used_event(key) { - return Err(error::ProofUsed); + attached_gas: GAS_FOR_DEPOSIT, } - - self.save_used_event(key); - Ok(()) - } - - /// Mint `nETH` tokens (ETH on NEAR). - fn mint_eth_on_near( - &mut self, - owner_id: &AccountId, - amount: NEP141Wei, - ) -> Result<(), fungible_token::error::DepositError> { - sdk::log!("Mint {} nETH tokens for: {}", amount, owner_id); - - if self.ft.get_account_eth_balance(owner_id).is_none() { - self.ft.accounts_insert(owner_id, ZERO_NEP141_WEI); - } - self.ft.internal_deposit_eth_to_near(owner_id, amount) - } - - /// Mint `ETH` tokens (ETH on Aurora). - fn mint_eth_on_aurora( - &mut self, - address: Address, - amount: Wei, - ) -> Result<(), fungible_token::error::DepositError> { - sdk::log!("Mint {} ETH tokens for: {}", amount, address.encode()); - self.ft.internal_deposit_eth_to_aurora(address, amount) - } - - /// Burn `ETH` tokens (ETH on Aurora). - fn burn_eth_on_aurora( - &mut self, - amount: Wei, - ) -> Result<(), fungible_token::error::WithdrawError> { - self.ft.internal_withdraw_eth_from_aurora(amount) } /// Withdraw `nETH` from NEAR accounts /// NOTE: it should be without any log data - pub fn withdraw_eth_from_near( - &mut self, - current_account_id: &AccountId, - predecessor_account_id: &AccountId, - args: &WithdrawCallArgs, - ) -> Result { - // Check if the current account id is owner. - let is_owner = current_account_id == predecessor_account_id; - // Check if the withdraw flow is paused. If it's owner just skip the assertion. - self.assert_not_paused(PAUSE_WITHDRAW, is_owner) - .map_err(|_| error::WithdrawError::Paused)?; - - // Burn tokens to recipient - self.ft - .internal_withdraw_eth_from_near(predecessor_account_id, args.amount)?; - // Save new contract data - self.save_ft_contract(); - - Ok(WithdrawResult { - recipient_id: args.recipient_address, - amount: args.amount, - eth_custodian_address: self.contract.eth_custodian_address, - }) - } - - /// Returns total `nETH` supply (ETH on NEAR). - pub fn ft_total_eth_supply_on_near(&mut self) { - let total_supply = self.ft.ft_total_eth_supply_on_near(); - sdk::log!("Total ETH supply on NEAR: {}", total_supply); - self.io - .return_output(format!("\"{total_supply}\"").as_bytes()); + pub fn withdraw_eth_from_near(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_withdraw".to_string(), + args: data, + attached_balance: Yocto::new(1), + attached_gas: GAS_FOR_WITHDRAW, + } } - /// Returns total `ETH` supply (ETH on Aurora). - pub fn ft_total_eth_supply_on_aurora(&mut self) { - let total_supply = self.ft.ft_total_eth_supply_on_aurora(); - sdk::log!("Total ETH supply on Aurora: {}", total_supply); - self.io - .return_output(format!("\"{total_supply}\"").as_bytes()); + /// Returns total ETH supply on NEAR (`nETH` as NEP-141 token) + pub fn ft_total_eth_supply_on_near(&mut self) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "ft_total_supply".to_string(), + args: Vec::new(), + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: VIEW_CALL_GAS, + } } /// Return `nETH` balance (ETH on NEAR). - pub fn ft_balance_of(&mut self, args: &BalanceOfCallArgs) { - let balance = self.ft.ft_balance_of(&args.account_id); - sdk::log!("Balance of nETH [{}]: {}", args.account_id, balance); - - self.io.return_output(format!("\"{balance}\"").as_bytes()); + pub fn ft_balance_of(&self, input: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "ft_balance_of".to_string(), + args: input, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: VIEW_CALL_GAS, + } } /// Return `ETH` balance (ETH on Aurora). pub fn ft_balance_of_eth_on_aurora( &mut self, args: &BalanceOfEthCallArgs, - ) -> Result<(), crate::prelude::types::balance::error::BalanceOverflowError> { - let balance = self - .ft - .internal_unwrap_balance_of_eth_on_aurora(&args.address); + ) -> Result<(), ParseTypeFromJsonError> { + let balance = self.internal_unwrap_balance_of_eth_on_aurora(&args.address); sdk::log!("Balance of ETH [{}]: {}", args.address.encode(), balance); - self.io.return_output(format!("\"{balance}\"").as_bytes()); - Ok(()) - } - - /// Transfer `nETH` between NEAR accounts. - pub fn ft_transfer( - &mut self, - predecessor_account_id: &AccountId, - args: &TransferCallArgs, - ) -> Result<(), fungible_token::error::TransferError> { - self.ft.internal_transfer_eth_on_near( - predecessor_account_id, - &args.receiver_id, - args.amount, - &args.memo, - )?; - self.save_ft_contract(); - sdk::log!( - "Transfer amount {} to {} success with memo: {:?}", - args.amount, - args.receiver_id, - args.memo - ); - Ok(()) - } - - /// FT resolve transfer logic - pub fn ft_resolve_transfer( - &mut self, - args: &ResolveTransferCallArgs, - promise_result: PromiseResult, - ) { - let amount = self.ft.ft_resolve_transfer( - promise_result, - &args.sender_id, - &args.receiver_id, - args.amount, - ); - sdk::log!( - "Resolve transfer from {} to {} success", - args.sender_id, - args.receiver_id - ); - // `ft_resolve_transfer` can change `total_supply` so we should save the contract - self.save_ft_contract(); - self.io.return_output(format!("\"{amount}\"").as_bytes()); - } - - /// FT transfer call from sender account (invoker account) to receiver. - /// We start early checking for message data to avoid `ft_on_transfer` call panics. - /// But we don't check if the relayer exists. If the relayer doesn't exist we simply - /// do not mint/burn the fee amount. - /// We allow empty messages for cases when `receiver_id =! current_account_id`. - pub fn ft_transfer_call( - &mut self, - predecessor_account_id: AccountId, - current_account_id: AccountId, - args: TransferCallCallArgs, - prepaid_gas: NearGas, - ) -> Result { - sdk::log!( - "Transfer call to {} amount {}", - args.receiver_id, - args.amount, - ); - - // Verify message data before `ft_on_transfer` call to avoid verification panics - // It's allowed empty message if `receiver_id =! current_account_id` - if args.receiver_id == current_account_id { - let message_data = FtTransferMessageData::parse_on_transfer_message(&args.msg) - .map_err(error::FtTransferCallError::MessageParseFailed)?; - // Check is transfer amount > fee - if message_data.fee.as_u128() >= args.amount.as_u128() { - return Err(error::FtTransferCallError::InsufficientAmountForFee); - } - - // Additional check for overflowing before `ft_on_transfer` calling. - // But skip checking for overflowing for the relayer. - // Note: It couldn't be overflowed because the total supply isn't changed during - // the transfer. - let amount_for_check = self - .ft - .internal_unwrap_balance_of_eth_on_aurora(&message_data.recipient); - if amount_for_check - .checked_add(Wei::from(args.amount)) - .is_none() - { - return Err(error::FtTransferCallError::Transfer( - fungible_token::error::TransferError::BalanceOverflow, - )); - } - if self - .ft - .total_eth_supply_on_aurora - .checked_add(Wei::from(args.amount)) - .is_none() - { - return Err(error::FtTransferCallError::Transfer( - fungible_token::error::TransferError::TotalSupplyOverflow, - )); - } - } - - self.ft - .ft_transfer_call( - predecessor_account_id, - args.receiver_id, - args.amount, - &args.memo, - args.msg, - current_account_id, - prepaid_gas, - ) - .map_err(Into::into) - } - - /// FT storage deposit logic. - pub fn storage_deposit( - &mut self, - predecessor_account_id: AccountId, - amount: Yocto, - args: StorageDepositCallArgs, - ) -> Result, fungible_token::error::StorageFundingError> { - let account_id = args - .account_id - .unwrap_or_else(|| predecessor_account_id.clone()); - let (res, maybe_promise) = self.ft.storage_deposit( - predecessor_account_id, - &account_id, - amount, - args.registration_only, - )?; - self.save_ft_contract(); - self.io.return_output(&res.to_json_bytes()); - Ok(maybe_promise) - } - - /// FT storage unregister. - pub fn storage_unregister( - &mut self, - account_id: AccountId, - force: Option, - ) -> Result, fungible_token::error::StorageFundingError> { - let promise = match self.ft.internal_storage_unregister(account_id, force) { - Ok((_, p)) => { - self.io.return_output(b"true"); - Some(p) - } - Err(fungible_token::error::StorageFundingError::NotRegistered) => { - self.io.return_output(b"false"); - None - } - Err(other) => return Err(other), - }; - Ok(promise) - } - - /// FT storage withdraw. - pub fn storage_withdraw( - &mut self, - account_id: &AccountId, - args: &StorageWithdrawCallArgs, - ) -> Result<(), fungible_token::error::StorageFundingError> { - let res = self.ft.storage_withdraw(account_id, args.amount)?; - self.save_ft_contract(); - self.io.return_output(&res.to_json_bytes()); + self.io.return_output(&serde_json::to_vec(&balance)?); Ok(()) } - /// Get the balance used by usage of the storage. - pub fn storage_balance_of(&mut self, args: &StorageBalanceOfCallArgs) { - self.io - .return_output(&self.ft.storage_balance_of(&args.account_id).to_json_bytes()); + /// Balance of ETH (ETH on Aurora) + pub fn internal_unwrap_balance_of_eth_on_aurora(&self, address: &Address) -> Wei { + crate::engine::get_balance(&self.io, address) } /// `ft_on_transfer` callback function. @@ -593,93 +147,176 @@ impl EthConnectorContract { let fee = Wei::from(message_data.fee); // Mint fee to relayer let relayer = engine.get_relayer(message_data.relayer.as_bytes()); - let (amount, relayer_fee) = - relayer - .filter(|_| fee > ZERO_WEI) - .map_or(Ok((amount, None)), |address| { - amount.checked_sub(fee).map_or( - Err(error::FtTransferCallError::InsufficientAmountForFee), - |amount| Ok((amount, Some((address, fee)))), - ) - })?; - - if let Some((address, fee)) = relayer_fee { - self.mint_eth_on_aurora(address, fee)?; - } - - self.mint_eth_on_aurora(message_data.recipient, amount)?; - self.save_ft_contract(); + + let mint_amount = if relayer.is_some() && fee > ZERO_WEI { + self.mint_eth_on_aurora(relayer.unwrap(), fee)?; + amount - fee + } else { + amount + }; + + self.mint_eth_on_aurora(message_data.recipient, mint_amount)?; self.io.return_output(b"\"0\""); + Ok(()) } - /// Return account counter. - /// It represents total unique accounts (all-time, including accounts which now have zero balance). - pub fn get_accounts_counter(&mut self) { - self.io - .return_output(&self.ft.get_accounts_counter().to_le_bytes()); + /// Mint ETH tokens + fn mint_eth_on_aurora(&mut self, owner_id: Address, amount: Wei) -> Result<(), DepositError> { + sdk::log!("Mint {} ETH tokens for: {}", amount, owner_id.encode()); + self.internal_deposit_eth_to_aurora(owner_id, amount) } - /// Return account id of the prover smart contract. - pub const fn get_bridge_prover(&self) -> &AccountId { - &self.contract.prover_account + /// Internal ETH deposit to Aurora + pub fn internal_deposit_eth_to_aurora( + &mut self, + address: Address, + amount: Wei, + ) -> Result<(), DepositError> { + let balance = self.internal_unwrap_balance_of_eth_on_aurora(&address); + let new_balance = balance + .checked_add(amount) + .ok_or(DepositError::BalanceOverflow)?; + crate::engine::set_balance(&mut self.io, &address, &new_balance); + Ok(()) } - /// Save eth-connector fungible token contract data in the storage. - fn save_ft_contract(&mut self) { - self.io.write_borsh( - &construct_contract_key(EthConnectorStorageId::FungibleToken), - &self.ft.data(), - ); + /// Transfer between NEAR accounts + pub fn ft_transfer(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_ft_transfer".to_string(), + args: data, + attached_balance: Yocto::new(1), + attached_gas: GAS_FOR_FT_TRANSFER, + } } - /// Generate key for used events from Proof. - fn used_event_key(key: &str) -> Vec { - [ - &construct_contract_key(EthConnectorStorageId::UsedEvent), - key.as_bytes(), - ] - .concat() + /// FT transfer call from sender account (invoker account) to receiver + /// We starting early checking for message data to avoid `ft_on_transfer` call panics + /// But we don't check relayer exists. If relayer doesn't exist we simply not mint/burn the amount of the fee + /// We allow empty messages for cases when `receiver_id =! current_account_id` + pub fn ft_transfer_call(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_ft_transfer_call".to_string(), + args: data, + attached_balance: Yocto::new(1), + attached_gas: GAS_FOR_FT_TRANSFER_CALL, + } } - /// Save already used event proof as hash key - fn save_used_event(&mut self, key: &str) { - self.io.write_borsh(&Self::used_event_key(key), &0u8); + /// FT storage deposit logic + pub fn storage_deposit(&self, data: Vec, attached_deposit: u128) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_storage_deposit".to_string(), + args: data, + attached_balance: Yocto::new(attached_deposit), + attached_gas: DEFAULT_PREPAID_GAS, + } + } + + /// FT storage unregister + pub fn storage_unregister(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_storage_unregister".to_string(), + args: data, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: DEFAULT_PREPAID_GAS, + } + } + + /// FT storage withdraw + pub fn storage_withdraw(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "engine_storage_withdraw".to_string(), + args: data, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: DEFAULT_PREPAID_GAS, + } + } + + /// Get balance of storage + pub fn storage_balance_of(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "storage_balance_of".to_string(), + args: data, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: DEFAULT_PREPAID_GAS, + } } - /// Check if the event of the proof has already been used. - fn is_used_event(&self, key: &str) -> bool { - self.io.storage_has_key(&Self::used_event_key(key)) + pub fn get_bridge_prover(&self) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "get_bridge_prover".to_string(), + args: Vec::new(), + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: VIEW_CALL_GAS, + } } - /// Check whether the provided proof has already been used. - pub fn is_used_proof(&self, proof: &Proof) -> bool { - self.is_used_event(&proof_key(proof)) + /// Checks whether the provided proof was already used + pub fn is_used_proof(&self, data: Vec) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "is_used_proof".to_string(), + args: data, + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: VIEW_CALL_GAS, + } } /// Get Eth connector paused flags - pub fn get_paused_flags(&self) -> PausedMask { - self.get_paused() + pub fn get_paused_flags(&self) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "get_paused_flags".to_string(), + args: Vec::new(), + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: DEFAULT_PREPAID_GAS, + } } - /// Set Eth connector paused flags - pub fn set_paused_flags(&mut self, args: &PauseEthConnectorCallArgs) { - self.set_paused(args.paused_mask); + /// Return FT metadata + pub fn get_metadata(&self) -> PromiseCreateArgs { + PromiseCreateArgs { + target_account_id: self.get_eth_connector_contract_account(), + method: "ft_metadata".to_string(), + args: Vec::new(), + attached_balance: ZERO_ATTACHED_BALANCE, + attached_gas: VIEW_CALL_GAS, + } + } + + /// Disable flag for standalone-legacy-nep141 + pub fn disable_legacy_nep141(&mut self) { + self.io.write_borsh( + &construct_contract_key(EthConnectorStorageId::DisableLegacyNEP141), + &1u8, + ); + } + + pub fn is_disabled_legacy_nep141(&self) -> bool { + self.io.storage_has_key(&construct_contract_key( + EthConnectorStorageId::DisableLegacyNEP141, + )) } } impl AdminControlled for EthConnectorContract { - /// Get current admin paused status. - fn get_paused(&self) -> PausedMask { - self.paused_mask + fn get_eth_connector_contract_account(&self) -> AccountId { + get_contract_data(&self.io, EthConnectorStorageId::EthConnectorAccount).unwrap() } - /// Set admin paused status. - fn set_paused(&mut self, paused_mask: PausedMask) { - self.paused_mask = paused_mask; + fn set_eth_connector_contract_account(&mut self, account: &AccountId) { self.io.write_borsh( - &construct_contract_key(EthConnectorStorageId::PausedMask), - &self.paused_mask, + &construct_contract_key(EthConnectorStorageId::EthConnectorAccount), + account, ); } } @@ -735,14 +372,20 @@ pub fn get_metadata(io: &I) -> Option { pub mod error { use crate::errors; use aurora_engine_types::types::address::error::AddressError; - use aurora_engine_types::types::balance::error::BalanceOverflowError; use crate::deposit_event::error::ParseOnTransferMessageError; - use crate::{deposit_event, fungible_token}; + use crate::prelude::types::balance::error::BalanceOverflowError; + + const TOTAL_SUPPLY_OVERFLOW: &[u8; 25] = errors::ERR_TOTAL_SUPPLY_OVERFLOW; + const BALANCE_OVERFLOW: &[u8; 20] = errors::ERR_BALANCE_OVERFLOW; + const NOT_ENOUGH_BALANCE: &[u8; 22] = errors::ERR_NOT_ENOUGH_BALANCE; + const TOTAL_SUPPLY_UNDERFLOW: &[u8; 26] = errors::ERR_TOTAL_SUPPLY_UNDERFLOW; + const ZERO_AMOUNT: &[u8; 15] = errors::ERR_ZERO_AMOUNT; + const SELF_TRANSFER: &[u8; 26] = errors::ERR_SENDER_EQUALS_RECEIVER; const PROOF_EXIST: &[u8; 15] = errors::ERR_PROOF_EXIST; - #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] + #[derive(Debug)] pub enum StorageReadError { KeyNotFound, BorshDeserialize, @@ -757,29 +400,6 @@ pub mod error { } } - #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] - pub enum DepositError { - Paused, - ProofParseFailed, - EventParseFailed(deposit_event::error::ParseError), - CustodianAddressMismatch, - InsufficientAmountForFee, - InvalidAddress(AddressError), - } - - impl AsRef<[u8]> for DepositError { - fn as_ref(&self) -> &[u8] { - match self { - Self::Paused => crate::admin_controlled::ERR_PAUSED.as_bytes(), - Self::ProofParseFailed => super::ERR_FAILED_PARSE.as_bytes(), - Self::EventParseFailed(e) => e.as_ref(), - Self::CustodianAddressMismatch => errors::ERR_WRONG_EVENT_ADDRESS, - Self::InsufficientAmountForFee => super::ERR_NOT_ENOUGH_BALANCE_FOR_FEE.as_bytes(), - Self::InvalidAddress(e) => e.as_ref(), - } - } - } - #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] pub enum FinishDepositError { TransferCall(FtTransferCallError), @@ -798,8 +418,8 @@ pub mod error { } } - impl From for FinishDepositError { - fn from(e: fungible_token::error::DepositError) -> Self { + impl From for FinishDepositError { + fn from(e: DepositError) -> Self { Self::TransferCall(FtTransferCallError::Transfer(e.into())) } } @@ -815,12 +435,12 @@ pub mod error { #[derive(Debug)] pub enum WithdrawError { - Paused, - FT(fungible_token::error::WithdrawError), + FT(WithdrawFtError), + ParseArgs, } - impl From for WithdrawError { - fn from(e: fungible_token::error::WithdrawError) -> Self { + impl From for WithdrawError { + fn from(e: WithdrawFtError) -> Self { Self::FT(e) } } @@ -828,8 +448,8 @@ pub mod error { impl AsRef<[u8]> for WithdrawError { fn as_ref(&self) -> &[u8] { match self { - Self::Paused => crate::admin_controlled::ERR_PAUSED.as_bytes(), Self::FT(e) => e.as_ref(), + Self::ParseArgs => b"ERR_PARSE_ARGS", } } } @@ -839,17 +459,17 @@ pub mod error { BalanceOverflow(BalanceOverflowError), MessageParseFailed(ParseOnTransferMessageError), InsufficientAmountForFee, - Transfer(fungible_token::error::TransferError), + Transfer(TransferError), } - impl From for FtTransferCallError { - fn from(e: fungible_token::error::TransferError) -> Self { + impl From for FtTransferCallError { + fn from(e: TransferError) -> Self { Self::Transfer(e) } } - impl From for FtTransferCallError { - fn from(e: fungible_token::error::DepositError) -> Self { + impl From for FtTransferCallError { + fn from(e: DepositError) -> Self { Self::Transfer(e.into()) } } @@ -893,6 +513,101 @@ pub mod error { PROOF_EXIST } } + + #[derive(Debug)] + pub enum DepositError { + TotalSupplyOverflow, + BalanceOverflow, + } + + impl AsRef<[u8]> for DepositError { + fn as_ref(&self) -> &[u8] { + match self { + Self::TotalSupplyOverflow => TOTAL_SUPPLY_OVERFLOW, + Self::BalanceOverflow => BALANCE_OVERFLOW, + } + } + } + + #[derive(Debug)] + pub enum WithdrawFtError { + TotalSupplyUnderflow, + InsufficientFunds, + BalanceOverflow(BalanceOverflowError), + } + + impl AsRef<[u8]> for WithdrawFtError { + fn as_ref(&self) -> &[u8] { + match self { + Self::TotalSupplyUnderflow => TOTAL_SUPPLY_UNDERFLOW, + Self::InsufficientFunds => NOT_ENOUGH_BALANCE, + Self::BalanceOverflow(e) => e.as_ref(), + } + } + } + + #[derive(Debug)] + pub enum TransferError { + TotalSupplyUnderflow, + TotalSupplyOverflow, + InsufficientFunds, + BalanceOverflow, + ZeroAmount, + SelfTransfer, + } + + impl AsRef<[u8]> for TransferError { + fn as_ref(&self) -> &[u8] { + match self { + Self::TotalSupplyUnderflow => TOTAL_SUPPLY_UNDERFLOW, + Self::TotalSupplyOverflow => TOTAL_SUPPLY_OVERFLOW, + Self::InsufficientFunds => NOT_ENOUGH_BALANCE, + Self::BalanceOverflow => BALANCE_OVERFLOW, + Self::ZeroAmount => ZERO_AMOUNT, + Self::SelfTransfer => SELF_TRANSFER, + } + } + } + + impl From for TransferError { + fn from(err: WithdrawFtError) -> Self { + match err { + WithdrawFtError::InsufficientFunds => Self::InsufficientFunds, + WithdrawFtError::TotalSupplyUnderflow => Self::TotalSupplyUnderflow, + WithdrawFtError::BalanceOverflow(_) => Self::BalanceOverflow, + } + } + } + + impl From for TransferError { + fn from(err: DepositError) -> Self { + match err { + DepositError::BalanceOverflow => Self::BalanceOverflow, + DepositError::TotalSupplyOverflow => Self::TotalSupplyOverflow, + } + } + } + + #[derive(Debug)] + pub enum StorageFundingError { + NotRegistered, + NoAvailableBalance, + InsufficientDeposit, + UnRegisterPositiveBalance, + } + + impl AsRef<[u8]> for StorageFundingError { + fn as_ref(&self) -> &[u8] { + match self { + Self::NotRegistered => errors::ERR_ACCOUNT_NOT_REGISTERED, + Self::NoAvailableBalance => errors::ERR_NO_AVAILABLE_BALANCE, + Self::InsufficientDeposit => errors::ERR_ATTACHED_DEPOSIT_NOT_ENOUGH, + Self::UnRegisterPositiveBalance => { + errors::ERR_FAILED_UNREGISTER_ACCOUNT_POSITIVE_BALANCE + } + } + } + } } #[must_use] diff --git a/engine/src/engine.rs b/engine/src/engine.rs index b4132996c..e40039b28 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -8,7 +8,6 @@ use evm::backend::{Apply, ApplyBackend, Backend, Basic, Log}; use evm::executor; use evm::{Config, CreateScheme, ExitError, ExitFatal, ExitReason}; -use crate::connector::EthConnectorContract; use crate::map::BijectionMap; use crate::{errors, state}; use aurora_engine_sdk::caching::FullCache; @@ -1731,17 +1730,8 @@ impl<'env, J: IO + Copy, E: Env, M: ModExpAlgorithm> ApplyBackend for Engine<'en match accounting.net() { // Net loss is possible if `SELFDESTRUCT(self)` calls are made. accounting::Net::Lost(amount) => { + let _ = amount; sdk::log!("Burn {} ETH due to SELFDESTRUCT", amount); - // Apply changes for eth-connector. We ignore the `StorageReadError` intentionally since - // if we cannot read the storage then there is nothing to remove. - EthConnectorContract::init_instance(self.io) - .map(|mut connector| { - // The `unwrap` is safe here because (a) if the connector - // is implemented correctly then the total supply will never underflow and (b) we are passing - // in the balance directly so there will always be enough balance. - connector.internal_remove_eth(Wei::new(amount)).unwrap(); - }) - .ok(); } accounting::Net::Zero => (), accounting::Net::Gained(_) => { diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 3f3d4b044..03d967b9c 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -12,8 +12,6 @@ clippy::unreadable_literal )] -use aurora_engine_types::parameters::PromiseCreateArgs; - #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(not(feature = "std"))] @@ -34,7 +32,6 @@ pub mod connector; pub mod deposit_event; pub mod engine; pub mod errors; -pub mod fungible_token; pub mod pausables; mod prelude; pub mod state; @@ -79,14 +76,14 @@ pub unsafe fn on_alloc_error(_: core::alloc::Layout) -> ! { mod contract { use parameters::{SetOwnerArgs, SetUpgradeDelayBlocksArgs}; + use crate::admin_controlled::AdminControlled; use crate::connector::{self, EthConnectorContract}; use crate::engine::{self, Engine}; use crate::parameters::{ - self, CallArgs, DeployErc20TokenArgs, FungibleTokenMetadata, GetErc20FromNep141CallArgs, - GetStorageAtArgs, InitCallArgs, IsUsedProofCallArgs, NEP141FtOnTransferArgs, NewCallArgs, - PauseEthConnectorCallArgs, PausePrecompilesCallArgs, ResolveTransferCallArgs, - SetContractDataCallArgs, StorageDepositCallArgs, StorageWithdrawCallArgs, SubmitArgs, - TransferCallCallArgs, ViewCallArgs, + self, CallArgs, DeployErc20TokenArgs, GetErc20FromNep141CallArgs, GetStorageAtArgs, + NEP141FtOnTransferArgs, NewCallArgs, PausePrecompilesCallArgs, SetContractDataCallArgs, + StorageDepositCallArgs, StorageWithdrawCallArgs, SubmitArgs, TransferCallCallArgs, + ViewCallArgs, }; #[cfg(feature = "evm_bully")] use crate::parameters::{BeginBlockArgs, BeginChainArgs}; @@ -109,13 +106,13 @@ mod contract { use aurora_engine_sdk::near_runtime::{Runtime, ViewEnv}; use aurora_engine_sdk::promise::PromiseHandler; use aurora_engine_sdk::types::ExpectUtf8; - use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; + use aurora_engine_types::borsh::BorshSerialize; + use aurora_engine_types::parameters::connector::{ + EngineWithdrawCallArgs, SetEthConnectorContractAccountArgs, StorageUnregisterCallArgs, + }; use aurora_engine_types::parameters::engine::errors::ParseTypeFromJsonError; use aurora_engine_types::parameters::engine::{RelayerKeyArgs, RelayerKeyManagerArgs}; - use aurora_engine_types::parameters::{PromiseAction, PromiseBatchAction}; - - #[cfg(feature = "integration-test")] - use crate::prelude::NearGas; + use aurora_engine_types::parameters::{PromiseAction, PromiseBatchAction, WithdrawCallArgs}; const CODE_KEY: &[u8; 4] = b"CODE"; const CODE_STAGE_KEY: &[u8; 10] = b"CODE_STAGE"; @@ -176,8 +173,11 @@ mod contract { #[no_mangle] pub extern "C" fn get_bridge_prover() { let mut io = Runtime; - let connector = EthConnectorContract::init_instance(io).sdk_unwrap(); - io.return_output(connector.get_bridge_prover().as_bytes()); + let promise_args = EthConnectorContract::init_instance(io) + .sdk_unwrap() + .get_bridge_prover(); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } /// Get chain id for this contract. @@ -292,7 +292,7 @@ mod contract { let mut io = Runtime; let pauser = EnginePrecompilesPauser::from_io(io); let data = pauser.paused().bits().to_le_bytes(); - io.return_output(&data[..]); + io.return_output(&data); } /// Sets the flag to pause the contract. @@ -515,44 +515,6 @@ mod contract { crate::xcc::fund_xcc_sub_account(&io, &mut Runtime, &io, args).sdk_unwrap(); } - /// Allow receiving NEP141 tokens to the EVM contract. - /// - /// This function returns the amount of tokens to return to the sender. - /// Either all tokens are transferred and tokens are returned - /// in case of an error, or no token is returned if the transaction was successful. - #[no_mangle] - pub extern "C" fn ft_on_transfer() { - let io = Runtime; - require_running(&state::get_state(&io).sdk_unwrap()); - let current_account_id = io.current_account_id(); - let predecessor_account_id = io.predecessor_account_id(); - let mut engine: Engine<_, _> = Engine::new( - predecessor_address(&predecessor_account_id), - current_account_id.clone(), - io, - &io, - ) - .sdk_unwrap(); - - let args: NEP141FtOnTransferArgs = serde_json::from_slice(&io.read_input().to_vec()) - .map_err(Into::::into) - .sdk_unwrap(); - - if predecessor_account_id == current_account_id { - EthConnectorContract::init_instance(io) - .sdk_unwrap() - .ft_on_transfer(&engine, &args) - .sdk_unwrap(); - } else { - engine.receive_erc20_tokens( - &predecessor_account_id, - &args, - ¤t_account_id, - &mut Runtime, - ); - } - } - /// Deploy ERC20 token mapped to a NEP141 #[no_mangle] pub extern "C" fn deploy_erc20_token() { @@ -780,6 +742,9 @@ mod contract { // TODO: https://github.com/aurora-is-near/aurora-engine/issues/2 } + /// + /// ETH-CONNECTOR + /// #[no_mangle] pub extern "C" fn new_eth_connector() { let io = Runtime; @@ -787,14 +752,14 @@ mod contract { require_running(&state); // Only the owner can initialize the EthConnector let is_private = io.assert_private_call(); + if is_private.is_err() { require_owner_only(&state, &io.predecessor_account_id()); } - - let args: InitCallArgs = io.read_input_borsh().sdk_unwrap(); - let owner_id = io.current_account_id(); - - EthConnectorContract::create_contract(io, &owner_id, args).sdk_unwrap(); + // + // let args: InitCallArgs = io.read_input_borsh().sdk_unwrap(); + // let owner_id = io.current_account_id(); + // EthConnectorContract::create_contract().sdk_unwrap(); } #[no_mangle] @@ -814,133 +779,77 @@ mod contract { #[no_mangle] pub extern "C" fn withdraw() { - let io = Runtime; + let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); io.assert_one_yocto().sdk_unwrap(); - let args = io.read_input_borsh().sdk_unwrap(); - let current_account_id = io.current_account_id(); - let predecessor_account_id = io.predecessor_account_id(); - let result = EthConnectorContract::init_instance(io) - .sdk_unwrap() - .withdraw_eth_from_near(¤t_account_id, &predecessor_account_id, &args) - .sdk_unwrap(); - let result_bytes = result.try_to_vec().sdk_expect(errors::ERR_SERIALIZE); - // We intentionally do not go through the `io` struct here because we must bypass - // the check that prevents output that is accepted by the eth_custodian - #[allow(clippy::as_conversions)] - unsafe { - exports::value_return( - u64::try_from(result_bytes.len()).sdk_expect(errors::ERR_VALUE_CONVERSION), - result_bytes.as_ptr() as u64, - ); + let args: WithdrawCallArgs = io.read_input_borsh().sdk_unwrap(); + let input = EngineWithdrawCallArgs { + sender_id: io.predecessor_account_id(), + recipient_address: args.recipient_address, + amount: args.amount, } - } + .try_to_vec() + .unwrap(); - #[no_mangle] - pub extern "C" fn deposit() { - let mut io = Runtime; - require_running(&state::get_state(&io).sdk_unwrap()); - let raw_proof = io.read_input().to_vec(); - let current_account_id = io.current_account_id(); - let predecessor_account_id = io.predecessor_account_id(); let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .deposit(raw_proof, current_account_id, predecessor_account_id) - .sdk_unwrap(); - // Safety: this call is safe because it comes from the eth-connector, not users. - // The call is to verify the user-supplied proof for the deposit, with `finish_deposit` - // as a callback. - let promise_id = unsafe { io.promise_create_with_callback(&promise_args) }; + .withdraw_eth_from_near(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; io.promise_return(promise_id); } #[no_mangle] - pub extern "C" fn finish_deposit() { + pub extern "C" fn deposit() { let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); - io.assert_private_call().sdk_unwrap(); - - // Check result from proof verification call - if io.promise_results_count() != 1 { - sdk::panic_utf8(errors::ERR_PROMISE_COUNT); - } - let promise_result = match io.promise_result(0) { - Some(PromiseResult::Successful(bytes)) => { - bool::try_from_slice(&bytes).sdk_expect(errors::ERR_PROMISE_ENCODING) - } - _ => sdk::panic_utf8(errors::ERR_PROMISE_FAILED), - }; - if !promise_result { - sdk::panic_utf8(errors::ERR_VERIFY_PROOF); - } - - let data = io.read_input_borsh().sdk_unwrap(); - let current_account_id = io.current_account_id(); - let predecessor_account_id = io.predecessor_account_id(); - let maybe_promise_args = EthConnectorContract::init_instance(io) + let input = io.read_input().to_vec(); + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .finish_deposit( - predecessor_account_id, - current_account_id, - data, - io.prepaid_gas(), - ) - .sdk_unwrap(); - - if let Some(promise_args) = maybe_promise_args { - // Safety: this call is safe because it comes from the eth-connector, not users. - // The call will be to the Engine's ft_transfer_call`, which is needed as part - // of the bridge flow (if depositing ETH to an Aurora address). - let promise_id = unsafe { io.promise_create_with_callback(&promise_args) }; - io.promise_return(promise_id); - } + .deposit(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn is_used_proof() { let mut io = Runtime; - let args: IsUsedProofCallArgs = io.read_input_borsh().sdk_unwrap(); - - let is_used_proof = EthConnectorContract::init_instance(io) + let input = io.read_input().to_vec(); + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .is_used_proof(&args.proof); - let res = is_used_proof.try_to_vec().unwrap(); - io.return_output(&res[..]); + .is_used_proof(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn ft_total_supply() { - let io = Runtime; - EthConnectorContract::init_instance(io) + let mut io = Runtime; + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() .ft_total_eth_supply_on_near(); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn ft_total_eth_supply_on_near() { - let io = Runtime; - EthConnectorContract::init_instance(io) + let mut io = Runtime; + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() .ft_total_eth_supply_on_near(); - } - - #[no_mangle] - pub extern "C" fn ft_total_eth_supply_on_aurora() { - let io = Runtime; - EthConnectorContract::init_instance(io) - .sdk_unwrap() - .ft_total_eth_supply_on_aurora(); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn ft_balance_of() { - let io = Runtime; - let args: parameters::BalanceOfCallArgs = serde_json::from_slice(&io.read_input().to_vec()) - .map_err(Into::::into) - .sdk_unwrap(); - EthConnectorContract::init_instance(io) + let mut io = Runtime; + let input = io.read_input().to_vec(); + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .ft_balance_of(&args); + .ft_balance_of(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] @@ -955,35 +864,27 @@ mod contract { #[no_mangle] pub extern "C" fn ft_transfer() { - let io = Runtime; + use crate::parameters::TransferCallArgs; + let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); io.assert_one_yocto().sdk_unwrap(); - let predecessor_account_id = io.predecessor_account_id(); - let args: parameters::TransferCallArgs = serde_json::from_slice(&io.read_input().to_vec()) + let input = serde_json::from_slice::(&io.read_input().to_vec()) + .and_then(|args| { + serde_json::to_vec(&( + io.predecessor_account_id(), + args.receiver_id, + args.amount, + args.memo, + )) + }) .map_err(Into::::into) .sdk_unwrap(); - EthConnectorContract::init_instance(io) - .sdk_unwrap() - .ft_transfer(&predecessor_account_id, &args) - .sdk_unwrap(); - } - - #[no_mangle] - pub extern "C" fn ft_resolve_transfer() { - let io = Runtime; - require_running(&state::get_state(&io).sdk_unwrap()); - - io.assert_private_call().sdk_unwrap(); - if io.promise_results_count() != 1 { - sdk::panic_utf8(errors::ERR_PROMISE_COUNT); - } - let args: ResolveTransferCallArgs = io.read_input().to_value().sdk_unwrap(); - let promise_result = io.promise_result(0).sdk_unwrap(); - - EthConnectorContract::init_instance(io) + let promise_arg = EthConnectorContract::init_instance(io) .sdk_unwrap() - .ft_resolve_transfer(&args, promise_result); + .ft_transfer(input); + let promise_id = unsafe { io.promise_create_call(&promise_arg) }; + io.promise_return(promise_id); } #[no_mangle] @@ -992,45 +893,82 @@ mod contract { require_running(&state::get_state(&io).sdk_unwrap()); // Check is payable io.assert_one_yocto().sdk_unwrap(); - - let args: TransferCallCallArgs = serde_json::from_slice(&io.read_input().to_vec()) + let input = serde_json::from_slice::(&io.read_input().to_vec()) + .and_then(|args| { + serde_json::to_vec(&( + io.predecessor_account_id(), + args.receiver_id, + args.amount, + args.memo, + args.msg, + )) + }) .map_err(Into::::into) .sdk_unwrap(); - let current_account_id = io.current_account_id(); - let predecessor_account_id = io.predecessor_account_id(); + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .ft_transfer_call( - predecessor_account_id, - current_account_id, - args, - io.prepaid_gas(), - ) - .sdk_unwrap(); - // Safety: this call is safe. It is required by the NEP-141 spec that `ft_transfer_call` - // creates a call to another contract's `ft_on_transfer` method. - let promise_id = unsafe { io.promise_create_with_callback(&promise_args) }; + .ft_transfer_call(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; io.promise_return(promise_id); } + /// Allow receiving NEP141 tokens to the EVM contract. + /// + /// This function returns the amount of tokens to return to the sender. + /// Either all tokens are transferred tokens are returned in case of an + /// error, or no token is returned if tx was successful. + #[no_mangle] + pub extern "C" fn ft_on_transfer() { + let io = Runtime; + let current_account_id = io.current_account_id(); + let predecessor_account_id = io.predecessor_account_id(); + let mut engine: Engine<_, _> = Engine::new( + predecessor_address(&predecessor_account_id), + current_account_id.clone(), + io, + &io, + ) + .sdk_unwrap(); + + let args: NEP141FtOnTransferArgs = serde_json::from_slice(&io.read_input().to_vec()) + .map_err(Into::::into) + .sdk_unwrap(); + let mut eth_connector = EthConnectorContract::init_instance(io).sdk_unwrap(); + + if predecessor_account_id == eth_connector.get_eth_connector_contract_account() { + eth_connector.ft_on_transfer(&engine, &args).sdk_unwrap(); + } else { + engine.receive_erc20_tokens( + &predecessor_account_id, + &args, + ¤t_account_id, + &mut Runtime, + ); + } + } + #[no_mangle] pub extern "C" fn storage_deposit() { let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); - let args: StorageDepositCallArgs = serde_json::from_slice(&io.read_input().to_vec()) + let input = serde_json::from_slice::(&io.read_input().to_vec()) + .and_then(|args| { + serde_json::to_vec(&( + io.predecessor_account_id(), + args.account_id, + args.registration_only, + )) + }) .map_err(Into::::into) .sdk_unwrap(); - let predecessor_account_id = io.predecessor_account_id(); - let amount = Yocto::new(io.attached_deposit()); - let maybe_promise = EthConnectorContract::init_instance(io) + + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .storage_deposit(predecessor_account_id, amount, args) - .sdk_unwrap(); - if let Some(promise) = maybe_promise { - // Safety: This call is safe. It is only a transfer back to the user in the case - // that they over paid for their deposit. - unsafe { io.promise_create_batch(&promise) }; - } + .storage_deposit(input, io.attached_deposit()); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + + io.promise_return(promise_id); } #[no_mangle] @@ -1038,110 +976,109 @@ mod contract { let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); io.assert_one_yocto().sdk_unwrap(); - let predecessor_account_id = io.predecessor_account_id(); - let force = serde_json::from_slice::(&io.read_input().to_vec()) - .ok() - .and_then(|args| args["force"].as_bool()); - let maybe_promise = EthConnectorContract::init_instance(io) - .sdk_unwrap() - .storage_unregister(predecessor_account_id, force) + + let input = serde_json::from_slice(&io.read_input().to_vec()) + .and_then(|args: StorageUnregisterCallArgs| { + serde_json::to_vec(&(io.predecessor_account_id(), args.force)) + }) + .map_err(Into::::into) .sdk_unwrap(); - if let Some(promise) = maybe_promise { - // Safety: This call is safe. It is only a transfer back to the user for their deposit. - unsafe { io.promise_create_batch(&promise) }; - } + + let promise_args = EthConnectorContract::init_instance(io) + .sdk_unwrap() + .storage_unregister(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn storage_withdraw() { - let io = Runtime; + let mut io = Runtime; require_running(&state::get_state(&io).sdk_unwrap()); io.assert_one_yocto().sdk_unwrap(); - let args: StorageWithdrawCallArgs = serde_json::from_slice(&io.read_input().to_vec()) + + let input = serde_json::from_slice::(&io.read_input().to_vec()) + .and_then(|args| serde_json::to_vec(&(io.predecessor_account_id(), args.amount))) .map_err(Into::::into) .sdk_unwrap(); - let predecessor_account_id = io.predecessor_account_id(); - EthConnectorContract::init_instance(io) + + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .storage_withdraw(&predecessor_account_id, &args) - .sdk_unwrap(); + .storage_withdraw(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn storage_balance_of() { - let io = Runtime; - let args: parameters::StorageBalanceOfCallArgs = - serde_json::from_slice(&io.read_input().to_vec()) - .map_err(Into::::into) - .sdk_unwrap(); - EthConnectorContract::init_instance(io) + let mut io = Runtime; + let input = io.read_input().to_vec(); + let promise_args = EthConnectorContract::init_instance(io) .sdk_unwrap() - .storage_balance_of(&args); + .storage_balance_of(input); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] - pub extern "C" fn get_paused_flags() { + pub extern "C" fn get_eth_connector_contract_account() { let mut io = Runtime; - let paused_flags = EthConnectorContract::init_instance(io) + let account = EthConnectorContract::init_instance(io) .sdk_unwrap() - .get_paused_flags(); - let data = paused_flags.try_to_vec().expect(ERR_FAILED_PARSE); - io.return_output(&data[..]); + .get_eth_connector_contract_account(); + let data = account.try_to_vec().expect(ERR_FAILED_PARSE); + io.return_output(&data); } #[no_mangle] - pub extern "C" fn set_paused_flags() { + pub extern "C" fn set_eth_connector_contract_account() { let io = Runtime; let state = state::get_state(&io).sdk_unwrap(); require_running(&state); let is_private = io.assert_private_call(); + if is_private.is_err() { require_owner_only(&state, &io.predecessor_account_id()); } - let args: PauseEthConnectorCallArgs = io.read_input_borsh().sdk_unwrap(); + + let args: SetEthConnectorContractAccountArgs = io.read_input_borsh().sdk_unwrap(); + EthConnectorContract::init_instance(io) .sdk_unwrap() - .set_paused_flags(&args); + .set_eth_connector_contract_account(&args.account); } #[no_mangle] - pub extern "C" fn get_accounts_counter() { + pub extern "C" fn disable_legacy_nep141() { let io = Runtime; + let state = state::get_state(&io).sdk_unwrap(); + require_owner_only(&state, &io.predecessor_account_id()); + EthConnectorContract::init_instance(io) .sdk_unwrap() - .get_accounts_counter(); + .disable_legacy_nep141(); } #[no_mangle] - pub extern "C" fn get_erc20_from_nep141() { - let mut io = Runtime; - let args: GetErc20FromNep141CallArgs = io.read_input_borsh().sdk_unwrap(); - - io.return_output( - engine::get_erc20_from_nep141(&io, &args.nep141) - .sdk_unwrap() - .as_slice(), - ); - } - - #[no_mangle] - pub extern "C" fn get_nep141_from_erc20() { + pub extern "C" fn get_paused_flags() { let mut io = Runtime; - let erc20_address: engine::ERC20Address = io.read_input().to_vec().try_into().sdk_unwrap(); - io.return_output( - engine::nep141_erc20_map(io) - .lookup_right(&erc20_address) - .sdk_expect("ERC20_NOT_FOUND") - .as_ref(), - ); + let promise_args = EthConnectorContract::init_instance(io) + .sdk_unwrap() + .get_paused_flags(); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[no_mangle] pub extern "C" fn ft_metadata() { let mut io = Runtime; - let metadata: FungibleTokenMetadata = connector::get_metadata(&io).unwrap_or_default(); - let bytes = serde_json::to_vec(&metadata).unwrap_or_default(); - io.return_output(&bytes); + let promise_args = EthConnectorContract::init_instance(io) + .sdk_unwrap() + .get_metadata(); + let promise_id = unsafe { io.promise_create_call(&promise_args) }; + io.promise_return(promise_id); } #[cfg(feature = "integration-test")] @@ -1157,13 +1094,10 @@ mod contract { #[cfg(feature = "integration-test")] #[no_mangle] pub extern "C" fn mint_account() { - use crate::connector::ZERO_ATTACHED_BALANCE; use crate::prelude::{NEP141Wei, U256}; use evm::backend::ApplyBackend; - const GAS_FOR_VERIFY: NearGas = NearGas::new(20_000_000_000_000); - const GAS_FOR_FINISH: NearGas = NearGas::new(50_000_000_000_000); - let mut io = Runtime; + let io = Runtime; let args: ([u8; 20], u64, u64) = io.read_input_borsh().sdk_expect(errors::ERR_ARGS); let address = Address::from_array(args.0); let nonce = U256::from(args.1); @@ -1182,41 +1116,31 @@ mod contract { reset_storage: false, }; engine.apply(core::iter::once(state_change), core::iter::empty(), false); + } - // Call "finish_deposit" to mint the corresponding - // nETH NEP-141 tokens as well - let aurora_account_id = io.current_account_id(); - let args = crate::parameters::FinishDepositCallArgs { - new_owner_id: aurora_account_id.clone(), - amount: balance, - proof_key: crate::prelude::String::new(), - relayer_id: aurora_account_id.clone(), - fee: 0.into(), - msg: None, - }; - let verify_call = aurora_engine_types::parameters::PromiseCreateArgs { - target_account_id: aurora_account_id.clone(), - method: crate::prelude::String::from("verify_log_entry"), - args: crate::prelude::Vec::new(), - attached_balance: ZERO_ATTACHED_BALANCE, - attached_gas: GAS_FOR_VERIFY, - }; - let finish_call = aurora_engine_types::parameters::PromiseCreateArgs { - target_account_id: aurora_account_id, - method: crate::prelude::String::from("finish_deposit"), - args: args.try_to_vec().unwrap(), - attached_balance: ZERO_ATTACHED_BALANCE, - attached_gas: GAS_FOR_FINISH, - }; - // Safety: this call is safe because it is only used in integration tests. - unsafe { - io.promise_create_with_callback( - &aurora_engine_types::parameters::PromiseWithCallbackArgs { - base: verify_call, - callback: finish_call, - }, - ) - }; + #[no_mangle] + pub extern "C" fn get_erc20_from_nep141() { + let mut io = Runtime; + let args: GetErc20FromNep141CallArgs = io.read_input_borsh().sdk_unwrap(); + + io.return_output( + engine::get_erc20_from_nep141(&io, &args.nep141) + .sdk_unwrap() + .as_slice(), + ); + } + + #[no_mangle] + pub extern "C" fn get_nep141_from_erc20() { + let mut io = Runtime; + let erc20_address: crate::engine::ERC20Address = + io.read_input().to_vec().try_into().sdk_unwrap(); + io.return_output( + engine::nep141_erc20_map(io) + .lookup_right(&erc20_address) + .sdk_expect("ERC20_NOT_FOUND") + .as_ref(), + ); } /// @@ -1259,14 +1183,8 @@ mod contract { fn predecessor_address(predecessor_account_id: &AccountId) -> Address { near_account_to_evm_address(predecessor_account_id.as_bytes()) } - - mod exports { - extern "C" { - pub(crate) fn value_return(value_len: u64, value_ptr: u64); - } - } } pub trait AuroraState { - fn add_promise(&mut self, promise: PromiseCreateArgs); + fn add_promise(&mut self, promise: aurora_engine_types::parameters::PromiseCreateArgs); }