From e3e673e31f9e72d757d68979bb6796a0b7f9c8bc Mon Sep 17 00:00:00 2001 From: Mad <46090742+DefiCake@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:11:38 +0200 Subject: [PATCH] Upgradable L2 contract (#164) Adds support for upgradability to the bridge contract Closes #159 --------- Co-authored-by: Cameron Carstens Co-authored-by: SwayStar123 Co-authored-by: SwayStar123 <46050679+SwayStar123@users.noreply.github.com> Co-authored-by: K1-R1 <77465250+K1-R1@users.noreply.github.com> --- .changeset/strong-pots-pump.md | 8 + Cargo.lock | 299 ++++++----- Forc.lock | 26 +- Forc.toml | 4 +- docker/fuel-core/Dockerfile | 28 +- packages/base-asset/Cargo.toml | 4 +- packages/base-asset/Forc.toml | 2 +- .../bridge-fungible-token/Cargo.toml | 1 + .../{ => implementation}/Forc.toml | 5 +- .../src/data_structures.sw | 0 .../src/data_structures/constants.sw | 0 .../src/data_structures/deposit_message.sw | 0 .../src/data_structures/message_data.sw | 0 .../src/data_structures/metadata_message.sw | 0 .../{ => implementation}/src/errors.sw | 0 .../{ => implementation}/src/events.sw | 0 .../{ => implementation}/src/main.sw | 4 +- .../{ => implementation}/src/utils.sw | 0 .../bridge-fungible-token/interface/Forc.toml | 7 + .../interface => interface/src}/bridge.sw | 0 .../interface.sw => interface/src/lib.sw} | 0 .../{src/interface => interface/src}/src7.sw | 0 .../bridge-fungible-token/proxy/Forc.toml | 10 + .../bridge-fungible-token/proxy/src/proxy.sw | 94 ++++ .../tests/functions/bridge/mod.rs | 120 +++-- .../tests/functions/message_receiver/mod.rs | 204 +++++--- .../tests/functions/mod.rs | 1 + .../tests/functions/proxy/mod.rs | 488 ++++++++++++++++++ .../tests/functions/src20/mod.rs | 39 +- .../tests/utils/builder.rs | 8 +- .../tests/utils/constants.rs | 3 +- .../tests/utils/interface/bridge/mod.rs | 18 +- .../tests/utils/interface/src20/mod.rs | 16 +- .../tests/utils/setup.rs | 150 +++--- packages/fungible-token/exports/index.ts | 12 +- .../integration-tests/scripts/bridgeERC20.ts | 4 +- .../integration-tests/tests/bridge_erc20.ts | 35 +- .../integration-tests/tests/bridge_erc721.ts | 7 +- .../integration-tests/tests/bridge_proxy.ts | 179 +++++++ .../tests/utils/builder.rs | 1 - .../tests/utils/environment.rs | 4 +- .../fuels/getOrDeployFuelTokenContract.ts | 105 ---- .../src/utils/fuels/getOrDeployL2Bridge.ts | 187 +++++++ packages/test-utils/src/utils/fuels/index.ts | 2 +- .../src/utils/fuels/relayCommonMessage.ts | 62 ++- pnpm-lock.yaml | 8 +- 46 files changed, 1650 insertions(+), 495 deletions(-) create mode 100644 .changeset/strong-pots-pump.md rename packages/fungible-token/bridge-fungible-token/{ => implementation}/Forc.toml (50%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/data_structures.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/data_structures/constants.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/data_structures/deposit_message.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/data_structures/message_data.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/data_structures/metadata_message.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/errors.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/events.sw (100%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/main.sw (99%) rename packages/fungible-token/bridge-fungible-token/{ => implementation}/src/utils.sw (100%) create mode 100644 packages/fungible-token/bridge-fungible-token/interface/Forc.toml rename packages/fungible-token/bridge-fungible-token/{src/interface => interface/src}/bridge.sw (100%) rename packages/fungible-token/bridge-fungible-token/{src/interface.sw => interface/src/lib.sw} (100%) rename packages/fungible-token/bridge-fungible-token/{src/interface => interface/src}/src7.sw (100%) create mode 100644 packages/fungible-token/bridge-fungible-token/proxy/Forc.toml create mode 100644 packages/fungible-token/bridge-fungible-token/proxy/src/proxy.sw create mode 100644 packages/fungible-token/bridge-fungible-token/tests/functions/proxy/mod.rs create mode 100644 packages/integration-tests/tests/bridge_proxy.ts delete mode 100644 packages/test-utils/src/utils/fuels/getOrDeployFuelTokenContract.ts create mode 100644 packages/test-utils/src/utils/fuels/getOrDeployL2Bridge.ts diff --git a/.changeset/strong-pots-pump.md b/.changeset/strong-pots-pump.md new file mode 100644 index 00000000..460f3c13 --- /dev/null +++ b/.changeset/strong-pots-pump.md @@ -0,0 +1,8 @@ +--- +'@fuel-bridge/message-predicates': minor +'@fuel-bridge/solidity-contracts': minor +'@fuel-bridge/fungible-token': minor +'@fuel-bridge/test-utils': minor +--- + +Added upgradability to bridge contracts diff --git a/Cargo.lock b/Cargo.lock index 754e9415..5fd6c2d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arrayref" @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -204,25 +204,25 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure 0.12.6", + "syn 2.0.66", + "synstructure", ] [[package]] name = "asn1-rs-impl" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -353,7 +353,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -364,7 +364,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -432,7 +432,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -490,9 +490,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -629,6 +629,7 @@ dependencies = [ name = "bridge-fungible-token-contract" version = "0.3.0" dependencies = [ + "anyhow", "ethers", "fuel-core-types", "fuels", @@ -731,9 +732,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", @@ -827,7 +828,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -937,9 +938,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.4" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -1039,9 +1040,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1073,9 +1074,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1159,7 +1160,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1271,7 +1272,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1304,7 +1305,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core 0.20.9", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1345,9 +1346,9 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.2.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ "asn1-rs", "displaydoc", @@ -1468,7 +1469,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1610,7 +1611,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1630,7 +1631,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1785,7 +1786,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.64", + "syn 2.0.66", "toml", "walkdir", ] @@ -1803,7 +1804,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1829,7 +1830,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.64", + "syn 2.0.66", "tempfile", "thiserror", "tiny-keccak", @@ -2122,7 +2123,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.64", + "syn 2.0.66", "thiserror", ] @@ -2496,8 +2497,8 @@ checksum = "d8d6e66d1b68eb916640c12a1c6c40880e11fcf569359b04483d5e18237c5229" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", - "synstructure 0.13.1", + "syn 2.0.66", + "synstructure", ] [[package]] @@ -2644,7 +2645,7 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2685,7 +2686,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2825,7 +2826,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2838,6 +2839,17 @@ dependencies = [ "rustls 0.21.12", ] +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls 0.23.8", + "rustls-pki-types", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -2911,9 +2923,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -2932,9 +2944,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -3443,7 +3455,7 @@ dependencies = [ "autocfg", "impl-tools-lib", "proc-macro-error", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3455,7 +3467,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3533,9 +3545,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -3696,9 +3708,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -3999,9 +4011,9 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0375cdfee57b47b313ef1f0fdb625b78aed770d33a40cf1c294a371ff5e6666" +checksum = "c67296ad4e092e23f92aea3d2bdb6f24eab79c0929ed816dfb460ea2f4567d2b" dependencies = [ "bytes", "futures", @@ -4013,8 +4025,8 @@ dependencies = [ "parking_lot", "quinn", "rand", - "ring 0.16.20", - "rustls 0.21.12", + "ring 0.17.8", + "rustls 0.23.8", "socket2", "thiserror", "tokio", @@ -4074,7 +4086,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -4096,18 +4108,18 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ce7e3c2e7569d685d08ec795157981722ff96e9e9f9eae75df3c29d02b07a5" +checksum = "251b17aebdd29df7e8f80e4d94b782fae42e934c49086e1a81ba23b60a8314f2" dependencies = [ "futures", - "futures-rustls", + "futures-rustls 0.26.0", "libp2p-core", "libp2p-identity", "rcgen", - "ring 0.16.20", - "rustls 0.21.12", - "rustls-webpki", + "ring 0.17.8", + "rustls 0.23.8", + "rustls-webpki 0.101.7", "thiserror", "x509-parser", "yasna", @@ -4137,7 +4149,7 @@ checksum = "f4846d51afd08180e164291c3754ba30dd4fbac6fac65571be56403c16431a5e" dependencies = [ "either", "futures", - "futures-rustls", + "futures-rustls 0.24.0", "libp2p-core", "libp2p-identity", "parking_lot", @@ -4310,9 +4322,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -4595,7 +4607,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -4606,18 +4618,18 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "oid-registry" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" dependencies = [ "asn1-rs", ] @@ -4717,9 +4729,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -4869,7 +4881,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -4907,7 +4919,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5064,7 +5076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5134,9 +5146,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -5161,7 +5173,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5226,9 +5238,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +checksum = "904e3d3ba178131798c6d9375db2b13b34337d489b089fc5ba0825a2ff1bee73" dependencies = [ "bytes", "futures-io", @@ -5236,7 +5248,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.21.12", + "rustls 0.23.8", "thiserror", "tokio", "tracing", @@ -5244,15 +5256,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.6" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +checksum = "e974563a4b1c2206bbc61191ca4da9c22e4308b4c455e8906751cc7828393f08" dependencies = [ "bytes", "rand", - "ring 0.16.20", + "ring 0.17.8", "rustc-hash", - "rustls 0.21.12", + "rustls 0.23.8", "slab", "thiserror", "tinyvec", @@ -5261,15 +5273,15 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +checksum = "e4f0def2590301f4f667db5a77f9694fb004f82796dc1a8b1508fafa3d0e8b72" dependencies = [ - "bytes", "libc", + "once_cell", "socket2", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5616,10 +5628,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct 0.7.1", ] +[[package]] +name = "rustls" +version = "0.23.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79adb16721f56eb2d843e67676896a61ce7a0fa622dc18d3e372477a029d2740" +dependencies = [ + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.5.0" @@ -5653,6 +5679,12 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -5663,6 +5695,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -5889,22 +5932,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5966,7 +6009,7 @@ dependencies = [ "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6253,7 +6296,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6266,7 +6309,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6308,9 +6351,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.64" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -6323,18 +6366,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "synstructure" version = "0.13.1" @@ -6343,7 +6374,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6413,22 +6444,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6523,7 +6554,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6650,7 +6681,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -6722,7 +6753,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6982,7 +7013,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -7016,7 +7047,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7280,9 +7311,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -7345,9 +7376,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ "asn1-rs", "data-encoding", @@ -7438,14 +7469,14 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -7458,7 +7489,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] diff --git a/Forc.lock b/Forc.lock index 3ae14304..8f7fd8dc 100644 --- a/Forc.lock +++ b/Forc.lock @@ -2,7 +2,7 @@ name = "base-asset-contract" source = "member" dependencies = [ - "standards", + "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.5.0#348f7175df4c012b23c86cdb18aab79025ca1f18", "std", ] @@ -11,7 +11,8 @@ name = "bridge_fungible_token" source = "member" dependencies = [ "contract_message_receiver", - "src_20", + "interface", + "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.5.0#348f7175df4c012b23c86cdb18aab79025ca1f18", "std", "sway_libs", ] @@ -34,15 +35,30 @@ name = "core" source = "path+from-root-E19CE48B3E858B72" [[package]] -name = "src_20" -source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.2.2#6989cf8224b0d8aabea62f3d3c648fc754948705" +name = "interface" +source = "member" dependencies = ["std"] +[[package]] +name = "proxy" +source = "member" +dependencies = [ + "contract_message_receiver", + "interface", + "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.5.0#348f7175df4c012b23c86cdb18aab79025ca1f18", + "std", +] + [[package]] name = "standards" source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.4.3#6f63eb7dff2458a7d976184e565b5cbf26f61da2" dependencies = ["std"] +[[package]] +name = "standards" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.5.0#348f7175df4c012b23c86cdb18aab79025ca1f18" +dependencies = ["std"] + [[package]] name = "std" source = "git+https://github.com/fuellabs/sway?tag=v0.60.0#2f0392ee35a1e4dd80bd8034962d5b4083dfb8b6" @@ -52,7 +68,7 @@ dependencies = ["core"] name = "sway_libs" source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.21.0#6a227ed34c86fe1ebd334dbdfeccf66c43e3915b" dependencies = [ - "standards", + "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.4.3#6f63eb7dff2458a7d976184e565b5cbf26f61da2", "std", ] diff --git a/Forc.toml b/Forc.toml index 5c63be8b..b63b7874 100644 --- a/Forc.toml +++ b/Forc.toml @@ -1,7 +1,9 @@ [workspace] members = [ "packages/fungible-token/test-deposit-recipient-contract", - "packages/fungible-token/bridge-fungible-token", + "packages/fungible-token/bridge-fungible-token/interface", + "packages/fungible-token/bridge-fungible-token/proxy", + "packages/fungible-token/bridge-fungible-token/implementation", "packages/message-predicates/contract-message-predicate", "packages/message-predicates/contract-message-receiver", "packages/base-asset", diff --git a/docker/fuel-core/Dockerfile b/docker/fuel-core/Dockerfile index ace3eb38..3b1c0baf 100644 --- a/docker/fuel-core/Dockerfile +++ b/docker/fuel-core/Dockerfile @@ -1,4 +1,8 @@ -FROM ghcr.io/fuellabs/fuel-core:v0.27.0 +# IMPORTANT! When upgrading fuel-core version, +# Make sure to check +# https://github.com/FuelLabs/chain-configuration/tree/master/upgradelog/ignition +# and apply the latest state_transition_function and consensus_parameter +FROM ghcr.io/fuellabs/fuel-core:v0.28.0 ARG FUEL_IP=0.0.0.0 ARG FUEL_PORT=4001 @@ -13,12 +17,28 @@ WORKDIR /fuel COPY ./genesis_coins.json . -RUN git clone https://github.com/FuelLabs/chain-configuration.git /chain-configuration +RUN git clone \ + https://github.com/FuelLabs/chain-configuration.git \ + /chain-configuration +# Copy the base local configuration RUN cp -R /chain-configuration/local/* ./ -# merge genesis_coins.json into state_config.json -RUN jq '.coins = input' state_config.json genesis_coins.json > state_config_tmp.json && mv state_config_tmp.json state_config.json +# Copy the testnet consensus parameters and state transition bytecode +RUN cp /chain-configuration/upgradelog/ignition/consensus_parameters/3.json \ + ./latest_consensus_parameters.json +RUN cp /chain-configuration/upgradelog/ignition/state_transition_function/2.wasm \ + ./state_transition_bytecode.wasm + +# update local state_config with custom genesis coins config +RUN jq '.coins = input' \ + state_config.json genesis_coins.json > tmp.json \ + && mv tmp.json state_config.json + +# update local state_config with testnet consensus parameters +RUN jq '.consensus_parameters = input' \ + state_config.json latest_consensus_parameters.json > tmp.json \ + && mv tmp.json state_config.json # expose fuel node port ENV FUEL_IP="${FUEL_IP}" diff --git a/packages/base-asset/Cargo.toml b/packages/base-asset/Cargo.toml index a55a9736..fda53305 100644 --- a/packages/base-asset/Cargo.toml +++ b/packages/base-asset/Cargo.toml @@ -8,8 +8,8 @@ license = "Apache-2.0" [dev-dependencies] fuels = { workspace = true, features = ["fuel-core-lib"] } tokio = { workspace = true, features = ["rt", "macros"] } -hex = { workspace = true, version = "0.4.3" } -sha2 = { workspace = true, version = "0.10" } +hex = { workspace = true } +sha2 = { workspace = true } [[test]] harness = true diff --git a/packages/base-asset/Forc.toml b/packages/base-asset/Forc.toml index 59ff1fd4..de35c456 100644 --- a/packages/base-asset/Forc.toml +++ b/packages/base-asset/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "base-asset-contract" [dependencies] -standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.4.3" } +standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.5.0" } diff --git a/packages/fungible-token/bridge-fungible-token/Cargo.toml b/packages/fungible-token/bridge-fungible-token/Cargo.toml index 18bbf0b5..b04d2b3c 100644 --- a/packages/fungible-token/bridge-fungible-token/Cargo.toml +++ b/packages/fungible-token/bridge-fungible-token/Cargo.toml @@ -10,6 +10,7 @@ rust-version = { workspace = true } publish = false [dependencies] +anyhow = "1.0.86" ethers = {version = "=2.0.14"} # Dependencies from fuel-core repository: fuel-core-types = { workspace = true } diff --git a/packages/fungible-token/bridge-fungible-token/Forc.toml b/packages/fungible-token/bridge-fungible-token/implementation/Forc.toml similarity index 50% rename from packages/fungible-token/bridge-fungible-token/Forc.toml rename to packages/fungible-token/bridge-fungible-token/implementation/Forc.toml index f4f669be..38b13b8b 100644 --- a/packages/fungible-token/bridge-fungible-token/Forc.toml +++ b/packages/fungible-token/bridge-fungible-token/implementation/Forc.toml @@ -5,6 +5,7 @@ license = "Apache-2.0" name = "bridge_fungible_token" [dependencies] -contract_message_receiver = { path = "../../message-predicates/contract-message-receiver" } -src_20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.2.2" } +contract_message_receiver = { path = "../../../message-predicates/contract-message-receiver" } +interface = { path = "../interface" } +standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.5.0" } sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.21.0" } diff --git a/packages/fungible-token/bridge-fungible-token/src/data_structures.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/data_structures.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/data_structures.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/data_structures.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/data_structures/constants.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/constants.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/data_structures/constants.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/constants.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/data_structures/deposit_message.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/deposit_message.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/data_structures/deposit_message.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/deposit_message.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/data_structures/message_data.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/message_data.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/data_structures/message_data.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/message_data.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/data_structures/metadata_message.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/metadata_message.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/data_structures/metadata_message.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/data_structures/metadata_message.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/errors.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/errors.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/errors.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/errors.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/events.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/events.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/events.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/events.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/main.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/main.sw similarity index 99% rename from packages/fungible-token/bridge-fungible-token/src/main.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/main.sw index 09b9da7d..ae078b2b 100644 --- a/packages/fungible-token/bridge-fungible-token/src/main.sw +++ b/packages/fungible-token/bridge-fungible-token/implementation/src/main.sw @@ -12,7 +12,6 @@ contract; mod data_structures; mod errors; mod events; -mod interface; mod utils; use contract_message_receiver::MessageReceiver; @@ -62,7 +61,7 @@ use utils::{ encode_data, encode_register_calldata, }; -use src_20::SRC20; +use standards::src20::SRC20; const FUEL_ASSET_DECIMALS: u8 = 9u8; const ZERO_U256 = 0x00u256; @@ -71,6 +70,7 @@ configurable { BRIDGED_TOKEN_GATEWAY: b256 = 0x00000000000000000000000096c53cd98B7297564716a8f2E1de2C83928Af2fe, } +#[namespace(bridge)] storage { asset_to_sub_id: StorageMap = StorageMap {}, asset_to_token_id: StorageMap = StorageMap {}, diff --git a/packages/fungible-token/bridge-fungible-token/src/utils.sw b/packages/fungible-token/bridge-fungible-token/implementation/src/utils.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/utils.sw rename to packages/fungible-token/bridge-fungible-token/implementation/src/utils.sw diff --git a/packages/fungible-token/bridge-fungible-token/interface/Forc.toml b/packages/fungible-token/bridge-fungible-token/interface/Forc.toml new file mode 100644 index 00000000..a688892a --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/interface/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "interface" + +[dependencies] diff --git a/packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw b/packages/fungible-token/bridge-fungible-token/interface/src/bridge.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw rename to packages/fungible-token/bridge-fungible-token/interface/src/bridge.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/interface.sw b/packages/fungible-token/bridge-fungible-token/interface/src/lib.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/interface.sw rename to packages/fungible-token/bridge-fungible-token/interface/src/lib.sw diff --git a/packages/fungible-token/bridge-fungible-token/src/interface/src7.sw b/packages/fungible-token/bridge-fungible-token/interface/src/src7.sw similarity index 100% rename from packages/fungible-token/bridge-fungible-token/src/interface/src7.sw rename to packages/fungible-token/bridge-fungible-token/interface/src/src7.sw diff --git a/packages/fungible-token/bridge-fungible-token/proxy/Forc.toml b/packages/fungible-token/bridge-fungible-token/proxy/Forc.toml new file mode 100644 index 00000000..63603a1a --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/proxy/Forc.toml @@ -0,0 +1,10 @@ +[project] +authors = ["Fuel Labs "] +entry = "proxy.sw" +license = "Apache-2.0" +name = "proxy" + +[dependencies] +contract_message_receiver = { path = "../../../message-predicates/contract-message-receiver" } +interface = { path = "../interface" } +standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.5.0" } diff --git a/packages/fungible-token/bridge-fungible-token/proxy/src/proxy.sw b/packages/fungible-token/bridge-fungible-token/proxy/src/proxy.sw new file mode 100644 index 00000000..e10613a3 --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/proxy/src/proxy.sw @@ -0,0 +1,94 @@ +contract; + +use std::execution::run_external; +use standards::{src14::SRC14, src5::{AccessError, State}}; + +pub enum ProxyErrors { + IdentityZero: (), +} + +abi Proxy { + #[storage(read)] + fn _proxy_owner() -> State; + + #[storage(read)] + fn _proxy_target() -> ContractId; + + #[storage(read, write)] + fn _proxy_change_owner(new_owner: Identity); + + #[storage(read, write)] + fn _proxy_revoke_ownership(); +} + +configurable { + INITIAL_OWNER: State = State::Uninitialized, + INITIAL_TARGET: ContractId = ContractId::zero(), +} + +#[namespace(SRC14)] +storage { + // target is at sha256("storage_SRC14_0") + target: Option = None, + // owner is at sha256("storage_SRC14_1") + owner: State = State::Uninitialized, +} + +impl SRC14 for Contract { + #[storage(write)] + fn set_proxy_target(new_target: ContractId) { + only_owner(); + require(new_target.bits() != b256::zero(), ProxyErrors::IdentityZero); + storage.target.write(Some(new_target)); + } +} + +#[fallback] +#[storage(read)] +fn fallback() { + // pass through any other method call to the target + run_external(storage.target.read().unwrap_or(INITIAL_TARGET)) +} + +#[storage(read)] +fn only_owner() { + let owner = match storage.owner.read() { + State::Uninitialized => INITIAL_OWNER, + state => state, + }; + + require( + owner == State::Initialized(msg_sender().unwrap()), + AccessError::NotOwner, + ); +} + +impl Proxy for Contract { + #[storage(read)] + fn _proxy_owner() -> State { + let owner = storage.owner.read(); + + match owner { + State::Uninitialized => INITIAL_OWNER, + _ => owner, + } + } + + #[storage(read)] + fn _proxy_target() -> ContractId { + storage.target.read().unwrap_or(INITIAL_TARGET) + } + + #[storage(read, write)] + fn _proxy_change_owner(new_owner: Identity) { + only_owner(); + require(new_owner.bits() != b256::zero(), ProxyErrors::IdentityZero); + storage.owner.write(State::Initialized(new_owner)); + } + + #[storage(read, write)] + fn _proxy_revoke_ownership() { + only_owner(); + storage.owner.write(State::Revoked); + } +} diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs index 14e57296..2719c728 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs @@ -4,8 +4,8 @@ use crate::utils::{ }, interface::bridge::withdraw, setup::{ - create_deposit_message, create_token, create_wallet, decode_hex, encode_hex, - parse_output_message_data, relay_message_to_contract, setup_environment, wallet_balance, + create_deposit_message, create_wallet, decode_hex, encode_hex, parse_output_message_data, + relay_message_to_contract, setup_environment, wallet_balance, BridgeFungibleTokenContractConfigurables, BridgingConfig, }, }; @@ -16,12 +16,9 @@ mod success { use super::*; use crate::utils::{ - constants::BRIDGED_TOKEN_GATEWAY, - interface::{ - bridge::{bridged_token_gateway, claim_refund}, - src20::total_supply, - }, - setup::{get_asset_id, ClaimRefundEvent, RefundRegisteredEvent}, + constants::{BRIDGED_TOKEN_GATEWAY, MESSAGE_SENDER_ADDRESS}, + interface::{bridge::claim_refund, src20::total_supply}, + setup::{get_asset_id, get_contract_ids, ClaimRefundEvent, RefundRegisteredEvent}, }; use fuels::{prelude::Address, programs::contract::SettableContract, tx::Receipt, types::U256}; use primitive_types::H160; @@ -40,6 +37,9 @@ mod success { hex::encode([vec![0u8; 12], H160::random().to_fixed_bytes().to_vec()].concat()) ); + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( &token_address, BRIDGED_TOKEN_ID, @@ -47,13 +47,13 @@ mod success { *wallet.address().hash(), deposit_amount, BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -106,6 +106,7 @@ mod success { let response = claim_refund( &bridge, + implementation_contract_id, Bits256::from_hex_str(FROM).unwrap(), Bits256::from_hex_str(&token_address).unwrap(), Bits256::from_hex_str(BRIDGED_TOKEN_ID).unwrap(), @@ -172,6 +173,9 @@ mod success { ); let deposit_amount = U256::from(1); + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (topping_message, coin, deposit_contract) = create_deposit_message( &token_address, BRIDGED_TOKEN_ID, @@ -179,7 +183,7 @@ mod success { *wallet.address().hash(), U256::from(u64::MAX), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) @@ -192,13 +196,13 @@ mod success { *wallet.address().hash(), deposit_amount, BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![topping_message, refundable_message], @@ -253,6 +257,7 @@ mod success { let response = claim_refund( &bridge, + implementation_contract_id, Bits256::from_hex_str(FROM).unwrap(), Bits256::from_hex_str(&token_address).unwrap(), Bits256::from_hex_str(BRIDGED_TOKEN_ID).unwrap(), @@ -314,6 +319,9 @@ mod success { let amount = 10u64; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -321,13 +329,13 @@ mod success { *wallet.address().hash(), U256::from(amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -361,8 +369,7 @@ mod success { } } - let balance = - wallet_balance(&wallet, &get_asset_id(bridge.contract_id(), BRIDGED_TOKEN)).await; + let balance = wallet_balance(&wallet, &get_asset_id(&proxy_id.into(), BRIDGED_TOKEN)).await; // Check that wallet now has bridged coins assert_eq!(balance, amount); @@ -371,7 +378,8 @@ mod success { let gas = 200_000; let to = Bits256(*wallet.address().hash()); - let call_response = withdraw(&bridge, to, amount, gas).await; + let call_response = + withdraw(&bridge, implementation_contract_id.clone(), to, amount, gas).await; let message_receipt = call_response .receipts @@ -401,22 +409,70 @@ mod success { assert_eq!(amount, amount); // Check that supply has decreased by withdrawal_amount - let supply = total_supply(&bridge, get_asset_id(bridge.contract_id(), BRIDGED_TOKEN)) - .await - .unwrap(); + let supply = total_supply( + &implementation_contract_id, + &bridge, + get_asset_id(bridge.contract_id(), BRIDGED_TOKEN), + ) + .await + .unwrap(); assert_eq!(supply, 0); } #[tokio::test] - async fn check_bridged_token_gateway() { - let contract = create_token().await; + async fn bridge_bridged_token_gateway() -> anyhow::Result<()> { + // perform successful deposit first, verify it, then withdraw and verify balances + let mut wallet = create_wallet(); + let configurables: Option = None; - let response = bridged_token_gateway(&contract).await; + let amount = 10u64; - assert_eq!( - response, - Bits256(*Address::from_str(BRIDGED_TOKEN_GATEWAY).unwrap()) + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + dbg!(&proxy_id); + + let (message, coin, deposit_contract) = create_deposit_message( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + U256::from(amount), + BRIDGED_TOKEN_DECIMALS, + proxy_id, + false, + None, + ) + .await; + + let (implementation_contract_id, bridge, _) = setup_environment( + &mut wallet, + vec![coin], + vec![message], + deposit_contract, + None, + configurables, ) + .await; + + dbg!(&implementation_contract_id); + + let bridged_token_gateway: Bits256 = bridge + .methods() + .bridged_token_gateway() + .with_contract_ids(&[implementation_contract_id.clone()]) + .call() + .await + .unwrap() + .value; + + let hex_bridged_token_gateway = format!("0x{}", hex::encode(bridged_token_gateway.0)); + assert_eq!( + hex_bridged_token_gateway, + MESSAGE_SENDER_ADDRESS.to_ascii_lowercase() + ); + + Ok(()) } } @@ -443,13 +499,13 @@ mod revert { *wallet.address().hash(), config.overflow.two, BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + Default::default(), false, None, ) .await; - let (bridge, _) = setup_environment( + let (implementation_contract_id, bridge, _) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -462,6 +518,7 @@ mod revert { bridge .methods() .asset_to_sub_id(AssetId::from_str(incorrect_asset_id).unwrap()) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap(); @@ -488,13 +545,13 @@ mod revert { *wallet.address().hash(), config.overflow.two, BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + Default::default(), false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -519,6 +576,7 @@ mod revert { Bits256::from_hex_str(wrong_token).unwrap(), Bits256::from_hex_str(BRIDGED_TOKEN_ID).unwrap(), ) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap(); diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs index 664a1585..b0bd4e93 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs @@ -12,10 +12,12 @@ use std::str::FromStr; mod success { use super::*; + use crate::utils::interface::src20::total_supply; + use crate::utils::setup::{ contract_balance, create_metadata_message, create_recipient_contract, encode_hex, - get_asset_id, precalculate_deposit_id, wallet_balance, MetadataEvent, + get_asset_id, get_contract_ids, precalculate_deposit_id, wallet_balance, MetadataEvent, RefundRegisteredEvent, }; use fuel_core_types::fuel_types::canonical::Deserialize; @@ -48,6 +50,9 @@ mod success { .with_BRIDGED_TOKEN_GATEWAY(Bits256::from_hex_str(message_sender).unwrap()) .unwrap(); + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, Some(configurables.clone())); + let (message, coin, deposit_contract) = create_deposit_message( token_address, token_id, @@ -55,13 +60,13 @@ mod success { *recipient, U256::from(amount), BRIDGED_TOKEN_DECIMALS, - Some(configurables.clone()), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -84,7 +89,7 @@ mod success { let tx_status = wallet.provider().unwrap().tx_status(&_tx_id).await.unwrap(); assert!(matches!(tx_status, TxStatus::Success { .. })); - let asset_id = get_asset_id(bridge.contract_id(), token_address); + let asset_id = get_asset_id(&proxy_id.into(), token_address); let asset_balance = provider .get_asset_balance(&recipient_bech32, asset_id) .await @@ -97,6 +102,7 @@ mod success { let registered_l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap() @@ -113,6 +119,9 @@ mod success { let mut wallet = create_wallet(); let configurables: Option = None; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let deposit_amount = u64::MAX / 2; let (first_deposit_message, coin, deposit_contract) = create_deposit_message( @@ -122,7 +131,7 @@ mod success { *wallet.address().hash(), U256::from(deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) @@ -135,13 +144,13 @@ mod success { *wallet.address().hash(), U256::from(deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![first_deposit_message, second_deposit_message], @@ -154,14 +163,16 @@ mod success { let asset_id = get_asset_id(bridge.contract_id(), BRIDGED_TOKEN); // Get the balance for the deposit contract before - assert!(total_supply(&bridge, asset_id).await.is_none()); + assert!(total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .is_none()); //////////////////// // First deposit // //////////////////// // Relay the test message to the bridge contract - let _receipts = relay_message_to_contract( + let _tx_id = relay_message_to_contract( &wallet, utxo_inputs.message[0].clone(), utxo_inputs.contract.clone(), @@ -173,7 +184,9 @@ mod success { // Check that wallet now has bridged coins assert_eq!(balance, deposit_amount); - let supply = total_supply(&bridge, asset_id).await.unwrap(); + let supply = total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .unwrap(); assert_eq!(supply, deposit_amount); //////////////////// @@ -191,13 +204,16 @@ mod success { let balance = wallet_balance(&wallet, &asset_id).await; assert_eq!(balance, deposit_amount * 2); - let supply = total_supply(&bridge, asset_id).await.unwrap(); + let supply = total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .unwrap(); assert_eq!(supply, deposit_amount * 2); // Verify that a L1 token has been registered let registered_l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap() @@ -216,6 +232,9 @@ mod success { let max_deposit_amount = u64::MAX; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (first_deposit_message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -223,7 +242,7 @@ mod success { *wallet.address().hash(), U256::from(max_deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) @@ -236,13 +255,13 @@ mod success { *wallet.address().hash(), U256::from(max_deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![first_deposit_message, second_deposit_message], @@ -255,7 +274,9 @@ mod success { let asset_id = get_asset_id(bridge.contract_id(), BRIDGED_TOKEN); // Get the balance for the deposit contract before - assert!(total_supply(&bridge, asset_id).await.is_none()); + assert!(total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .is_none()); //////////////////// // First deposit // @@ -274,7 +295,9 @@ mod success { // Check that wallet now has bridged coins assert_eq!(balance, max_deposit_amount); - let supply = total_supply(&bridge, asset_id).await.unwrap(); + let supply = total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .unwrap(); assert_eq!(supply, max_deposit_amount); //////////////////// @@ -307,7 +330,9 @@ mod success { assert_eq!(utxos.len(), 1); assert_eq!(utxos[0].amount, max_deposit_amount); - let supply = total_supply(&bridge, asset_id).await.unwrap(); + let supply = total_supply(&implementation_contract_id, &bridge, asset_id) + .await + .unwrap(); assert_eq!(supply, max_deposit_amount); let refund_registered_events = bridge @@ -339,6 +364,9 @@ mod success { let deposit_amount = u64::MAX; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -346,13 +374,13 @@ mod success { *deposit_contract_id, U256::from(deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, true, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (_implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -388,6 +416,9 @@ mod success { let configurables: Option = None; let amount = u64::MAX; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -395,13 +426,13 @@ mod success { *deposit_contract_id, U256::from(amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, true, Some(vec![11u8, 42u8, 69u8]), ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (_implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -445,6 +476,9 @@ mod success { let configurables: Option = None; let amount = u64::MAX; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -452,13 +486,13 @@ mod success { *deposit_contract_id, U256::from(amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, true, Some(vec![11u8, 42u8, 69u8]), ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (_implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -504,6 +538,9 @@ mod success { let symbol = "TKN".to_string(); let decimals = 18; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let amount: u64 = u64::MAX; let (deposit_message, coin, _) = create_deposit_message( BRIDGED_TOKEN, @@ -512,22 +549,17 @@ mod success { *wallet.address().hash(), U256::from(amount), decimals, - configurables.clone(), + proxy_id, false, None, ) .await; - let metadata_message = create_metadata_message( - BRIDGED_TOKEN, - BRIDGED_TOKEN_ID, - &name, - &symbol, - configurables.clone(), - ) - .await; + let metadata_message = + create_metadata_message(BRIDGED_TOKEN, BRIDGED_TOKEN_ID, &name, &symbol, proxy_id) + .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![deposit_message, (0, metadata_message)], @@ -553,6 +585,7 @@ mod success { let l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -562,6 +595,7 @@ mod success { let l2_decimals: u8 = bridge .methods() .decimals(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -593,13 +627,21 @@ mod success { Bits256::from_hex_str(BRIDGED_TOKEN).unwrap() ); - let registered_name = bridge.methods().name(asset_id).call().await.unwrap().value; + let registered_name = bridge + .methods() + .name(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) + .call() + .await + .unwrap() + .value; assert_eq!(name, registered_name.unwrap()); let registered_symbol = bridge .methods() .symbol(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -617,6 +659,9 @@ mod success { let symbol = "TKN".to_string(); let decimals = 9; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let amount: u64 = u64::MAX; let (deposit_message, coin, _) = create_deposit_message( BRIDGED_TOKEN, @@ -625,22 +670,17 @@ mod success { *wallet.address().hash(), U256::from(amount), decimals, - configurables.clone(), + proxy_id, false, None, ) .await; - let metadata_message = create_metadata_message( - BRIDGED_TOKEN, - BRIDGED_TOKEN_ID, - &name, - &symbol, - configurables.clone(), - ) - .await; + let metadata_message = + create_metadata_message(BRIDGED_TOKEN, BRIDGED_TOKEN_ID, &name, &symbol, proxy_id) + .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![deposit_message, (0, metadata_message)], @@ -666,6 +706,7 @@ mod success { let l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -675,6 +716,7 @@ mod success { let l2_decimals: u8 = bridge .methods() .decimals(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -706,12 +748,20 @@ mod success { Bits256::from_hex_str(BRIDGED_TOKEN).unwrap() ); - let registered_name = bridge.methods().name(asset_id).call().await.unwrap().value; + let registered_name = bridge + .methods() + .name(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) + .call() + .await + .unwrap() + .value; assert_eq!(name, registered_name.unwrap()); let registered_symbol = bridge .methods() .symbol(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -729,6 +779,9 @@ mod success { let symbol = "TKN".to_string(); let decimals = 6; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let amount: u64 = u64::MAX; let (deposit_message, coin, _) = create_deposit_message( BRIDGED_TOKEN, @@ -737,22 +790,17 @@ mod success { *wallet.address().hash(), U256::from(amount), decimals, - configurables.clone(), + proxy_id, false, None, ) .await; - let metadata_message = create_metadata_message( - BRIDGED_TOKEN, - BRIDGED_TOKEN_ID, - &name, - &symbol, - configurables.clone(), - ) - .await; + let metadata_message = + create_metadata_message(BRIDGED_TOKEN, BRIDGED_TOKEN_ID, &name, &symbol, proxy_id) + .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![deposit_message, (0, metadata_message)], @@ -778,6 +826,7 @@ mod success { let l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -787,6 +836,7 @@ mod success { let l2_decimals: u8 = bridge .methods() .decimals(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -817,13 +867,21 @@ mod success { Bits256::from_hex_str(BRIDGED_TOKEN).unwrap() ); - let registered_name = bridge.methods().name(asset_id).call().await.unwrap().value; + let registered_name = bridge + .methods() + .name(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) + .call() + .await + .unwrap() + .value; assert_eq!(name, registered_name.unwrap()); let registered_symbol = bridge .methods() .symbol(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -839,6 +897,9 @@ mod success { let deposit_amount: u128 = u64::MAX as u128 + 1; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -846,13 +907,13 @@ mod success { *wallet.address().hash(), U256::from(deposit_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], @@ -892,7 +953,7 @@ mod success { assert_eq!(utxos.len(), 0); - let supply = total_supply(&bridge, asset_id).await; + let supply = total_supply(&implementation_contract_id, &bridge, asset_id).await; assert!(supply.is_none()); let refund_registered_events = bridge @@ -927,6 +988,9 @@ mod success { let token_one_amount: u64 = 1; let token_two_amount: u64 = 2; + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + let (message_one, coin, deposit_contract) = create_deposit_message( token_one, BRIDGED_TOKEN_ID, @@ -934,7 +998,7 @@ mod success { *wallet.address().hash(), U256::from(token_one_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) @@ -947,13 +1011,13 @@ mod success { *wallet.address().hash(), U256::from(token_two_amount), BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (bridge, utxo_inputs) = setup_environment( + let (implementation_contract_id, bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message_one, message_two], @@ -987,7 +1051,7 @@ mod success { assert!(matches!(tx_status, TxStatus::Success { .. })); // Token one checks - let asset_id = get_asset_id(bridge.contract_id(), token_one); + let asset_id = get_asset_id(&proxy_id.into(), token_one); let asset_balance = wallet_balance(&wallet, &asset_id).await; // Check that wallet now has bridged coins @@ -997,6 +1061,7 @@ mod success { let token_one_registered_l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -1018,6 +1083,7 @@ mod success { let token_two_registered_l1_address: Bits256 = bridge .methods() .asset_to_l1_address(asset_id) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap() @@ -1037,6 +1103,8 @@ mod success { mod revert { use fuels::types::tx_status::TxStatus; + use crate::utils::setup::get_contract_ids; + use super::*; #[tokio::test] @@ -1044,8 +1112,10 @@ mod revert { let mut wallet = create_wallet(); let configurables: Option = None; let config = BridgingConfig::new(BRIDGED_TOKEN_DECIMALS, PROXY_TOKEN_DECIMALS); - let bad_sender: &str = - "0x5555550000000000000000000000000000000000000000000000000005555555"; + let bad_sender: &str = "0x5555550000000000000000000000000000000000000000000000000005555555"; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, @@ -1054,13 +1124,13 @@ mod revert { *Address::from_str(TO).unwrap(), config.amount.min, BRIDGED_TOKEN_DECIMALS, - configurables.clone(), + proxy_id, false, None, ) .await; - let (_test_contract, utxo_inputs) = setup_environment( + let (_implementation_contract_id, _bridge, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message], diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs index 7b47fb37..5a696f54 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs @@ -1,3 +1,4 @@ pub mod bridge; pub mod message_receiver; +pub mod proxy; pub mod src20; diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/proxy/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/proxy/mod.rs new file mode 100644 index 00000000..dd269d41 --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/proxy/mod.rs @@ -0,0 +1,488 @@ +mod tests { + use crate::utils::setup::{ + create_wallet, get_contract_ids, setup_environment, + BridgeFungibleTokenContractConfigurables, BridgeProxy, State, + }; + use ethers::core::rand::{self, Rng}; + use fuels::{ + accounts::{wallet::WalletUnlocked, Account}, + prelude::AssetId, + test_helpers::DEFAULT_COIN_AMOUNT, + types::{ + bech32::Bech32Address, + errors::{transaction::Reason, Error}, + ContractId, + }, + }; + + #[tokio::test] + async fn proxy_owner_and_target() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let owner = proxy + .methods() + ._proxy_owner() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert!( + matches!(owner, + State::Initialized(fuels::types::Identity::Address(address)) + if address == wallet.address().clone().into() + ), + "Ownership was not initialized or owner is not the expected address. Value: {:?}", + owner + ); + + let target = proxy + .methods() + ._proxy_target() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert_eq!(target, _implementation_contract_id); + + Ok(()) + } + + #[tokio::test] + async fn proxy_change_owner() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let new_owner = WalletUnlocked::new_random(None); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let _tx_id = proxy + .methods() + ._proxy_change_owner(new_owner.address().into()) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await? + .tx_id + .unwrap(); + + let owner = proxy + .methods() + ._proxy_owner() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert!( + matches!(owner, + State::Initialized(fuels::types::Identity::Address(address)) + if address == new_owner.address().clone().into() + ), + "Ownership was not initialized or owner is not the expected address. Value: {:?}", + owner + ); + + Ok(()) + } + + #[tokio::test] + async fn proxy_revoke_ownership() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let _tx_id = proxy + .methods() + ._proxy_revoke_ownership() + .with_contract_ids(&[proxy_id.into()]) + .call() + .await? + .tx_id + .unwrap(); + + let owner = proxy + .methods() + ._proxy_owner() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert_eq!(owner, State::Revoked); + + Ok(()) + } + + #[tokio::test] + async fn proxy_revoke_ownership_only_owner() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let mut mallory = WalletUnlocked::new_random(None); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let _ = wallet + .transfer( + mallory.address(), + DEFAULT_COIN_AMOUNT / 2, + Default::default(), + Default::default(), + ) + .await?; + + let provider = wallet.provider().clone().unwrap().clone(); + mallory.set_provider(provider); + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), mallory.clone()); + + let error_receipt = proxy + .methods() + ._proxy_revoke_ownership() + .with_contract_ids(&[proxy_id.into()]) + .call() + .await + .unwrap_err(); + + assert!( + matches!(error_receipt, + Error::Transaction(Reason::Reverted {reason, ..}) + if reason == "NotOwner".to_string() + ), + "Transaction did not revert or reverted with a wrong reason" + ); + + Ok(()) + } + + #[tokio::test] + async fn proxy_change_owner_cannot_be_zero() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let new_owner = Bech32Address::default(); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let error_receipt = proxy + .methods() + ._proxy_change_owner(new_owner.into()) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await + .unwrap_err(); + + assert!( + matches!(error_receipt, + Error::Transaction(Reason::Reverted {reason, ..}) + if reason == "IdentityZero".to_string() + ), + "Transaction did not revert or reverted with a wrong reason" + ); + + Ok(()) + } + + #[tokio::test] + async fn proxy_change_owner_only_owner() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let mut mallory = WalletUnlocked::new_random(None); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let _ = wallet + .transfer( + mallory.address(), + DEFAULT_COIN_AMOUNT / 2, + Default::default(), + Default::default(), + ) + .await?; + + let provider = wallet.provider().clone().unwrap().clone(); + mallory.set_provider(provider); + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), mallory.clone()); + + let error_receipt = proxy + .methods() + ._proxy_change_owner(mallory.address().into()) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await + .unwrap_err(); + + assert!( + matches!(error_receipt, + Error::Transaction(Reason::Reverted {reason, ..}) + if reason == "NotOwner".to_string() + ), + "Transaction did not revert or reverted with a wrong reason" + ); + + let owner = proxy + .methods() + ._proxy_owner() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert!( + matches!(owner, + State::Initialized(fuels::types::Identity::Address(address)) + if address == wallet.address().clone().into() + ), + "Ownership was not initialized or owner is not the expected address" + ); + + Ok(()) + } + + #[tokio::test] + async fn proxy_set_target() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + + let mut rng = rand::thread_rng(); + let random_bytes: [u8; 32] = rng.gen(); + let random_contract_id = ContractId::new(random_bytes); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let _tx_id = proxy + .methods() + .set_proxy_target(random_contract_id) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await? + .tx_id + .unwrap(); + + let target = proxy + .methods() + ._proxy_target() + .with_contract_ids(&[proxy_id.into()]) + .simulate() + .await? + .value; + + assert_eq!(target, random_contract_id); + + Ok(()) + } + + #[tokio::test] + async fn proxy_set_target_only_owner() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + let mut mallory = WalletUnlocked::new_random(None); + + let mut rng = rand::thread_rng(); + let random_bytes: [u8; 32] = rng.gen(); + let random_contract_id = ContractId::new(random_bytes); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let _ = wallet + .transfer( + mallory.address(), + DEFAULT_COIN_AMOUNT / 2, + Default::default(), + Default::default(), + ) + .await?; + + let provider = wallet.provider().clone().unwrap().clone(); + mallory.set_provider(provider); + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), mallory.clone()); + + let error_receipt = proxy + .methods() + .set_proxy_target(random_contract_id) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await + .unwrap_err(); + + assert!( + matches!(error_receipt, + Error::Transaction(Reason::Reverted {reason, ..}) + if reason == "NotOwner".to_string() + ), + "Transaction did not revert or reverted with a wrong reason" + ); + + Ok(()) + } + + #[tokio::test] + async fn proxy_set_target_id_cannot_be_zero() -> anyhow::Result<()> { + let mut wallet = create_wallet(); + + let new_target = ContractId::default(); + + let configurables: Option = None; + + let (proxy_id, _implementation_contract_id) = + get_contract_ids(&wallet, configurables.clone()); + + let wallet_funds = (DEFAULT_COIN_AMOUNT, AssetId::default()); + + let (_, bridge, _) = setup_environment( + &mut wallet, + vec![wallet_funds], + vec![], + None, + None, + configurables, + ) + .await; + + let proxy = BridgeProxy::new(bridge.contract_id().clone(), wallet.clone()); + + let error_receipt = proxy + .methods() + .set_proxy_target(new_target) + .with_contract_ids(&[proxy_id.into()]) + .call() + .await + .unwrap_err(); + + assert!( + matches!(error_receipt, + Error::Transaction(Reason::Reverted {reason, ..}) + if reason == "IdentityZero".to_string() + ), + "Transaction did not revert or reverted with a wrong reason" + ); + + Ok(()) + } +} diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs index 5daab0c7..7480d513 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs @@ -7,50 +7,61 @@ mod success { #[tokio::test] async fn check_total_supply() { - let contract = setup_test().await; - let asset_id = get_asset_id(contract.contract_id(), BRIDGED_TOKEN); + let (implementation_contract_id, proxy_contract) = setup_test().await; + let asset_id = get_asset_id(proxy_contract.contract_id(), BRIDGED_TOKEN); let expected_total_supply: u64 = u64::MAX; assert_eq!( - total_supply(&contract, asset_id).await.unwrap(), + total_supply(&implementation_contract_id, &proxy_contract, asset_id) + .await + .unwrap(), expected_total_supply ); } #[tokio::test] async fn check_total_assets() { - let contract = setup_test().await; + let (implementation_contract_id, proxy_contract) = setup_test().await; - assert_eq!(total_assets(&contract).await, 1); + assert_eq!( + total_assets(&implementation_contract_id, &proxy_contract).await, + 1 + ); } #[tokio::test] async fn check_name() { - let contract = setup_test().await; - let asset_id = get_asset_id(contract.contract_id(), BRIDGED_TOKEN); + let (implementation_contract_id, proxy_contract) = setup_test().await; + let asset_id = get_asset_id(proxy_contract.contract_id(), BRIDGED_TOKEN); - let response = name(&contract, asset_id).await.unwrap(); + let response = name(&implementation_contract_id, &proxy_contract, asset_id) + .await + .unwrap(); assert_eq!(response, String::from("Token")); } #[tokio::test] async fn check_symbol() { - let contract = setup_test().await; - let asset_id = get_asset_id(contract.contract_id(), BRIDGED_TOKEN); + let (implementation_contract_id, proxy_contract) = setup_test().await; + let asset_id = get_asset_id(proxy_contract.contract_id(), BRIDGED_TOKEN); - let response = symbol(&contract, asset_id).await.unwrap(); + let response = symbol(&implementation_contract_id, &proxy_contract, asset_id) + .await + .unwrap(); assert_eq!(response, String::from("TKN")); } #[tokio::test] async fn check_decimals() { - let contract = setup_test().await; - let asset_id = get_asset_id(contract.contract_id(), BRIDGED_TOKEN); + let (implementation_contract_id, proxy_contract) = setup_test().await; + let asset_id = get_asset_id(proxy_contract.contract_id(), BRIDGED_TOKEN); - let response = decimals(&contract, asset_id).await.unwrap(); + let response = decimals(&implementation_contract_id, &proxy_contract, asset_id) + .await + .unwrap(); assert_eq!(u64::from(response), PROXY_TOKEN_DECIMALS) } diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs index ac9dc9c4..ba393bbd 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs @@ -41,13 +41,13 @@ pub async fn build_contract_message_tx( for contract in contracts { tx_inputs.push(contract); } - // Start building tx list of outputs tx_outputs.push(Output::contract(1u16, Bytes32::zeroed(), Bytes32::zeroed())); + tx_outputs.push(Output::contract(2u16, Bytes32::zeroed(), Bytes32::zeroed())); - // If there is more than 1 contract input, it means this is a deposit to contract. - if number_of_contracts > 1usize { - tx_outputs.push(Output::contract(2u16, Bytes32::zeroed(), Bytes32::zeroed())); + // If there is more than 2 contract inputs, it means this is a deposit to a contract. + if number_of_contracts > 2usize { + tx_outputs.push(Output::contract(3u16, Bytes32::zeroed(), Bytes32::zeroed())); }; // Build a change output for the owner of the first provided coin input diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs index 5dc65410..35542d70 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs @@ -3,9 +3,10 @@ pub(crate) const CONTRACT_MESSAGE_PREDICATE_BINARY: &str = pub(crate) const MESSAGE_SENDER_ADDRESS: &str = "0x00000000000000000000000096c53cd98B7297564716a8f2E1de2C83928Af2fe"; pub(crate) const BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY: &str = - "../bridge-fungible-token/out/release/bridge_fungible_token.bin"; + "../bridge-fungible-token/implementation/out/release/bridge_fungible_token.bin"; pub(crate) const DEPOSIT_RECIPIENT_CONTRACT_BINARY: &str = "../test-deposit-recipient-contract/out/release/test_deposit_recipient_contract.bin"; +pub(crate) const BRIDGE_PROXY_BINARY: &str = "../bridge-fungible-token/proxy/out/release/proxy.bin"; pub(crate) const BRIDGED_TOKEN: &str = "0x00000000000000000000000000000000000000000000000000000000deadbeef"; diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/bridge/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/bridge/mod.rs index 426ce8ec..78fa9a91 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/bridge/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/bridge/mod.rs @@ -6,11 +6,12 @@ use fuels::{ accounts::wallet::WalletUnlocked, prelude::{CallParameters, TxPolicies}, programs::call_response::FuelCallResponse, - types::Bits256, + types::{bech32::Bech32ContractId, Bits256}, }; pub(crate) async fn claim_refund( contract: &BridgeFungibleTokenContract, + implementation_contract_id: Bech32ContractId, originator: Bits256, token_address: Bits256, token_id: Bits256, @@ -18,6 +19,7 @@ pub(crate) async fn claim_refund( contract .methods() .claim_refund(originator, token_address, token_id) + .with_contract_ids(&[implementation_contract_id]) .call() .await .unwrap() @@ -25,6 +27,7 @@ pub(crate) async fn claim_refund( pub(crate) async fn withdraw( contract: &BridgeFungibleTokenContract, + implementation_contract_id: Bech32ContractId, to: Bits256, amount: u64, gas: u64, @@ -37,6 +40,7 @@ pub(crate) async fn withdraw( contract .methods() .withdraw(to) + .with_contract_ids(&[implementation_contract_id]) .with_tx_policies(tx_policies) .call_params(call_params) .expect("Call param Error") @@ -44,15 +48,3 @@ pub(crate) async fn withdraw( .await .unwrap() } - -pub(crate) async fn bridged_token_gateway( - contract: &BridgeFungibleTokenContract, -) -> Bits256 { - contract - .methods() - .bridged_token_gateway() - .call() - .await - .unwrap() - .value -} diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs index fd448f5f..6c95a131 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs @@ -1,23 +1,29 @@ use crate::utils::setup::BridgeFungibleTokenContract; -use fuels::{accounts::wallet::WalletUnlocked, types::AssetId}; +use fuels::{accounts::wallet::WalletUnlocked, prelude::Bech32ContractId, types::AssetId}; pub(crate) async fn total_supply( + implementation_contract_id: &Bech32ContractId, contract: &BridgeFungibleTokenContract, asset_id: AssetId, ) -> Option { contract .methods() .total_supply(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() .value } -pub(crate) async fn total_assets(contract: &BridgeFungibleTokenContract) -> u64 { +pub(crate) async fn total_assets( + implementation_contract_id: &Bech32ContractId, + contract: &BridgeFungibleTokenContract, +) -> u64 { contract .methods() .total_assets() + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -25,12 +31,14 @@ pub(crate) async fn total_assets(contract: &BridgeFungibleTokenContract, asset_id: AssetId, ) -> Option { contract .methods() .name(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -38,12 +46,14 @@ pub(crate) async fn name( } pub(crate) async fn symbol( + implementation_contract_id: &Bech32ContractId, contract: &BridgeFungibleTokenContract, asset_id: AssetId, ) -> Option { contract .methods() .symbol(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() @@ -51,12 +61,14 @@ pub(crate) async fn symbol( } pub(crate) async fn decimals( + implementation_contract_id: &Bech32ContractId, contract: &BridgeFungibleTokenContract, asset_id: AssetId, ) -> Option { contract .methods() .decimals(asset_id) + .with_contract_ids(&[implementation_contract_id.clone()]) .call() .await .unwrap() diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs index 321fc3d1..61532278 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs @@ -16,9 +16,8 @@ use fuel_core_types::{ use fuels::{ accounts::{predicate::Predicate, wallet::WalletUnlocked, ViewOnlyAccount}, prelude::{ - abigen, launch_provider_and_get_wallet, setup_custom_assets_coins, setup_test_provider, - Address, AssetConfig, AssetId, Bech32ContractId, Contract, ContractId, LoadConfiguration, - Provider, TxPolicies, + abigen, setup_custom_assets_coins, setup_test_provider, Address, AssetConfig, AssetId, + Bech32ContractId, Contract, ContractId, LoadConfiguration, Provider, TxPolicies, }, test_helpers::{setup_single_message, DEFAULT_COIN_AMOUNT}, types::{coin::Coin, input::Input, message::Message, tx_status::TxStatus, Bits256, U256}, @@ -27,20 +26,24 @@ use sha2::Digest; use std::{mem::size_of, num::ParseIntError, result::Result as StdResult, str::FromStr}; use super::constants::{ - BRIDGED_TOKEN, BRIDGED_TOKEN_ID, DEPOSIT_TO_ADDRESS_FLAG, DEPOSIT_TO_CONTRACT_FLAG, - DEPOSIT_WITH_DATA_FLAG, FROM, METADATA_MESSAGE_FLAG, + BRIDGED_TOKEN, BRIDGED_TOKEN_ID, BRIDGE_PROXY_BINARY, DEPOSIT_TO_ADDRESS_FLAG, + DEPOSIT_TO_CONTRACT_FLAG, DEPOSIT_WITH_DATA_FLAG, FROM, METADATA_MESSAGE_FLAG, }; abigen!( Contract( name = "BridgeFungibleTokenContract", - abi = "packages/fungible-token/bridge-fungible-token/out/release/bridge_fungible_token-abi.json", + abi = "packages/fungible-token/bridge-fungible-token/implementation/out/release/bridge_fungible_token-abi.json", ), Contract( name = "DepositRecipientContract", abi = "packages/fungible-token/test-deposit-recipient-contract/out/release/test_deposit_recipient_contract-abi.json", ), + Contract( + name = "BridgeProxy", + abi = "packages/fungible-token/bridge-fungible-token/proxy/out/release/proxy-abi.json", + ) ); /// Used for setting up tests with various message values @@ -168,7 +171,11 @@ pub(crate) async fn setup_environment( deposit_contract: Option, sender: Option<&str>, configurables: Option, -) -> (BridgeFungibleTokenContract, UTXOInputs) { +) -> ( + Bech32ContractId, + BridgeFungibleTokenContract, + UTXOInputs, +) { // Generate coins for wallet let asset_configs: Vec = coins .iter() @@ -210,19 +217,33 @@ pub(crate) async fn setup_environment( wallet.set_provider(provider); // Set up the bridge contract instance - let load_configuration = match configurables { + let implementation_config = match configurables { Some(config) => LoadConfiguration::default().with_configurables(config), None => LoadConfiguration::default(), }; - let test_contract_id = - Contract::load_from(BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, load_configuration) + let implementation_contract_id = + Contract::load_from(BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, implementation_config) .unwrap() .deploy(&wallet.clone(), TxPolicies::default()) .await .unwrap(); - let bridge = BridgeFungibleTokenContract::new(test_contract_id.clone(), wallet.clone()); + let proxy_configurables = BridgeProxyConfigurables::default() + .with_INITIAL_OWNER(State::Initialized(wallet.address().into())) + .unwrap() + .with_INITIAL_TARGET(implementation_contract_id.clone().into()) + .unwrap(); + + let proxy_config = LoadConfiguration::default().with_configurables(proxy_configurables); + + let proxy_contract_id = Contract::load_from(BRIDGE_PROXY_BINARY, proxy_config) + .unwrap() + .deploy(&wallet.clone(), TxPolicies::default()) + .await + .unwrap(); + + let proxy_bridge = BridgeFungibleTokenContract::new(proxy_contract_id.clone(), wallet.clone()); // Build inputs for provided coins let coin_inputs = all_coins @@ -248,9 +269,17 @@ pub(crate) async fn setup_environment( Bytes32::zeroed(), Bytes32::zeroed(), TxPointer::default(), - test_contract_id.into(), + proxy_contract_id.into(), )]; + contract_inputs.push(Input::contract( + UtxoId::new(Bytes32::zeroed(), 0u16), + Bytes32::zeroed(), + Bytes32::zeroed(), + TxPointer::default(), + implementation_contract_id.clone().into(), + )); + if let Some(id) = deposit_contract { contract_inputs.push(Input::contract( UtxoId::new(Bytes32::zeroed(), 0u16), @@ -262,7 +291,8 @@ pub(crate) async fn setup_environment( } ( - bridge, + implementation_contract_id, + proxy_bridge, UTXOInputs { contract: contract_inputs, coin: coin_inputs, @@ -315,48 +345,14 @@ pub(crate) async fn precalculate_deposit_id() -> ContractId { } /// Prefixes the given bytes with the test contract ID -pub(crate) async fn prefix_contract_id( - mut data: Vec, - config: Option, -) -> Vec { - // Compute the test contract ID - let compiled_contract = match config { - Some(c) => Contract::load_from( - BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, - LoadConfiguration::default().with_configurables(c), - ) - .unwrap(), - None => Contract::load_from( - BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, - LoadConfiguration::default(), - ) - .unwrap(), - }; - - let test_contract_id = compiled_contract.contract_id(); - +pub(crate) fn prefix_contract_id(mut data: Vec, contract_id: ContractId) -> Vec { // Turn contract id into array with the given data appended to it - let test_contract_id: [u8; 32] = test_contract_id.into(); + let test_contract_id: [u8; 32] = contract_id.into(); let mut test_contract_id = test_contract_id.to_vec(); test_contract_id.append(&mut data); test_contract_id } -pub(crate) async fn create_token() -> BridgeFungibleTokenContract { - let wallet = launch_provider_and_get_wallet().await.unwrap(); - - let id = Contract::load_from( - BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, - LoadConfiguration::default(), - ) - .unwrap() - .deploy(&wallet, TxPolicies::default()) - .await - .unwrap(); - - BridgeFungibleTokenContract::new(id, wallet) -} - pub(crate) async fn create_recipient_contract( wallet: WalletUnlocked, ) -> DepositRecipientContract { @@ -395,7 +391,7 @@ pub(crate) async fn create_deposit_message( to: [u8; 32], amount: U256, decimals: u64, - config: Option, + message_recipient: ContractId, deposit_to_contract: bool, extra_data: Option>, ) -> ((u64, Vec), (u64, AssetId), Option) { @@ -426,7 +422,7 @@ pub(crate) async fn create_deposit_message( message_data.append(&mut data); }; - let message_data = prefix_contract_id(message_data, config).await; + let message_data = prefix_contract_id(message_data, message_recipient); let message = (MESSAGE_AMOUNT, message_data); let coin = (DEFAULT_COIN_AMOUNT, AssetId::default()); @@ -438,7 +434,7 @@ pub(crate) async fn create_metadata_message( token_id: &str, token_name: &str, token_symbol: &str, - config: Option, + contract_recipient: ContractId, ) -> Vec { let mut message_data: Vec = vec![]; message_data.append(&mut encode_hex(U256::from(METADATA_MESSAGE_FLAG)).to_vec()); @@ -452,7 +448,7 @@ pub(crate) async fn create_metadata_message( let mut payload = ethers::abi::encode(&items); message_data.append(&mut payload); - prefix_contract_id(message_data, config).await + prefix_contract_id(message_data, contract_recipient) } pub(crate) fn parse_output_message_data(data: &[u8]) -> (Vec, Bits256, Bits256, U256, Bits256) { @@ -502,11 +498,16 @@ pub(crate) fn get_asset_id(contract_id: &Bech32ContractId, token: &str) -> Asset } /// This setup mints tokens so that they are registered as minted assets in the bridge -pub(crate) async fn setup_test() -> BridgeFungibleTokenContract { +pub(crate) async fn setup_test() -> ( + Bech32ContractId, + BridgeFungibleTokenContract, +) { let mut wallet = create_wallet(); + let configurables = None; - let amount = u64::MAX; + let (proxy_id, _implementation_contract_id) = get_contract_ids(&wallet, configurables.clone()); + let amount = u64::MAX; let (message, coin, deposit_contract) = create_deposit_message( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, @@ -514,22 +515,22 @@ pub(crate) async fn setup_test() -> BridgeFungibleTokenContract *wallet.address().hash(), U256::from(amount), BRIDGED_TOKEN_DECIMALS, - None, + proxy_id, false, None, ) .await; let metadata_message = - create_metadata_message(BRIDGED_TOKEN, BRIDGED_TOKEN_ID, "Token", "TKN", None).await; + create_metadata_message(BRIDGED_TOKEN, BRIDGED_TOKEN_ID, "Token", "TKN", proxy_id).await; - let (contract, utxo_inputs) = setup_environment( + let (implementation_contract_id, proxy_contract, utxo_inputs) = setup_environment( &mut wallet, vec![coin], vec![message, (0, metadata_message)], deposit_contract, None, - None, + configurables, ) .await; @@ -552,5 +553,34 @@ pub(crate) async fn setup_test() -> BridgeFungibleTokenContract let tx_status = wallet.provider().unwrap().tx_status(&tx_id).await.unwrap(); assert!(matches!(tx_status, TxStatus::Success { .. })); - contract + (implementation_contract_id, proxy_contract) +} + +pub(crate) fn get_contract_ids( + proxy_owner: &WalletUnlocked, + implementation_configurables: Option, +) -> (ContractId, ContractId) { + // Set up the bridge contract instance + let implementation_config = match implementation_configurables { + Some(config) => LoadConfiguration::default().with_configurables(config), + None => LoadConfiguration::default(), + }; + + let implementation_contract_id: ContractId = + Contract::load_from(BRIDGE_FUNGIBLE_TOKEN_CONTRACT_BINARY, implementation_config) + .unwrap() + .contract_id(); + + let proxy_configurables = BridgeProxyConfigurables::default() + .with_INITIAL_OWNER(State::Initialized(proxy_owner.address().clone().into())) + .unwrap() + .with_INITIAL_TARGET(implementation_contract_id.clone()) + .unwrap(); + + let proxy_config = LoadConfiguration::default().with_configurables(proxy_configurables); + let proxy_contract_id = Contract::load_from(BRIDGE_PROXY_BINARY, proxy_config) + .unwrap() + .contract_id(); + + (proxy_contract_id, implementation_contract_id) } diff --git a/packages/fungible-token/exports/index.ts b/packages/fungible-token/exports/index.ts index cb6317ce..faf854b5 100644 --- a/packages/fungible-token/exports/index.ts +++ b/packages/fungible-token/exports/index.ts @@ -1,5 +1,11 @@ -export { default as fungibleTokenABI } from '../bridge-fungible-token/out/release/bridge_fungible_token-abi.json'; -export { default as fungibleTokenStorageSlots } from '../bridge-fungible-token/out/release/bridge_fungible_token-storage_slots.json'; -import _fungibleTokenBinary from '../bridge-fungible-token/out/release/bridge_fungible_token.bin'; +export { default as fungibleTokenABI } from '../bridge-fungible-token/implementation/out/release/bridge_fungible_token-abi.json'; +export { default as fungibleTokenStorageSlots } from '../bridge-fungible-token/implementation/out/release/bridge_fungible_token-storage_slots.json'; + +export { default as bridgeProxyABI } from '../bridge-fungible-token/proxy/out/release/proxy-abi.json'; +export { default as bridgeProxyStorageSlots } from '../bridge-fungible-token/proxy/out/release/proxy-storage_slots.json'; + +import _fungibleTokenBinary from '../bridge-fungible-token/implementation/out/release/bridge_fungible_token.bin'; +import _bridgeProxyBinary from '../bridge-fungible-token/proxy/out/release/proxy.bin'; export const fungibleTokenBinary = _fungibleTokenBinary; +export const bridgeProxyBinary = _bridgeProxyBinary; diff --git a/packages/integration-tests/scripts/bridgeERC20.ts b/packages/integration-tests/scripts/bridgeERC20.ts index fa23c97f..40be5685 100644 --- a/packages/integration-tests/scripts/bridgeERC20.ts +++ b/packages/integration-tests/scripts/bridgeERC20.ts @@ -9,7 +9,7 @@ import { createRelayMessageParams, getOrDeployECR20Contract, mintECR20, - getOrDeployFuelTokenContract, + getOrDeployL2Bridge, validateFundgibleContracts, getMessageOutReceipt, FUEL_MESSAGE_TIMEOUT_MS, @@ -48,7 +48,7 @@ const TOKEN_AMOUNT = '10'; const ethTestToken = await getOrDeployECR20Contract(env); // load Fuel side fungible token contract - const fuelTestToken = await getOrDeployFuelTokenContract( + const fuelTestToken = await getOrDeployL2Bridge( env, ethTestToken, env.eth.fuelERC20Gateway, diff --git a/packages/integration-tests/tests/bridge_erc20.ts b/packages/integration-tests/tests/bridge_erc20.ts index 4a13f64a..c007110d 100644 --- a/packages/integration-tests/tests/bridge_erc20.ts +++ b/packages/integration-tests/tests/bridge_erc20.ts @@ -6,7 +6,7 @@ import { waitForMessage, createRelayMessageParams, getOrDeployECR20Contract, - getOrDeployFuelTokenContract, + getOrDeployL2Bridge, FUEL_TX_PARAMS, getMessageOutReceipt, fuel_to_eth_address, @@ -39,8 +39,9 @@ describe('Bridging ERC20 tokens', async function () { let eth_testToken: Token; let eth_testTokenAddress: string; let eth_erc20GatewayAddress: string; - let fuel_testToken: Contract; - let fuel_testContractId: string; + let fuel_bridge: Contract; + let fuel_bridgeImpl: Contract; + let fuel_bridgeContractId: string; let fuel_testAssetId: string; // override the default test timeout from 2000ms @@ -53,18 +54,23 @@ describe('Bridging ERC20 tokens', async function () { ).toLowerCase(); eth_testToken = await getOrDeployECR20Contract(env); eth_testTokenAddress = (await eth_testToken.getAddress()).toLowerCase(); - fuel_testToken = await getOrDeployFuelTokenContract( + + const { contract, implementation } = await getOrDeployL2Bridge( env, env.eth.fuelERC20Gateway, FUEL_TX_PARAMS ); - fuel_testContractId = fuel_testToken.id.toHexString(); - await env.eth.fuelERC20Gateway.setAssetIssuerId(fuel_testContractId); - fuel_testAssetId = getTokenId(fuel_testToken, eth_testTokenAddress); + fuel_bridge = contract; + fuel_bridgeImpl = implementation; + + fuel_bridgeContractId = fuel_bridge.id.toHexString(); + await env.eth.fuelERC20Gateway.setAssetIssuerId(fuel_bridgeContractId); + fuel_testAssetId = getTokenId(fuel_bridge, eth_testTokenAddress); - const { value: expectedGatewayContractId } = await fuel_testToken.functions + const { value: expectedGatewayContractId } = await fuel_bridge.functions .bridged_token_gateway() + .addContracts([fuel_bridge, fuel_bridgeImpl]) .txParams(FUEL_CALL_TX_PARAMS) .dryRun(); @@ -161,6 +167,7 @@ describe('Bridging ERC20 tokens', async function () { const tx = await relayCommonMessage(env.fuel.deployer, message, { gasLimit: 30000000, maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], }); const txResult = await tx.waitForResult(); @@ -177,12 +184,14 @@ describe('Bridging ERC20 tokens', async function () { }); it('Check metadata was registered', async () => { - await fuel_testToken.functions + await fuel_bridge.functions .asset_to_l1_address({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) .call(); - const { value: l2_decimals } = await fuel_testToken.functions + const { value: l2_decimals } = await fuel_bridge.functions .decimals({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) .get(); expect(l2_decimals).to.be.equal(9); @@ -232,6 +241,7 @@ describe('Bridging ERC20 tokens', async function () { const tx = await relayCommonMessage(env.fuel.deployer, message, { ...FUEL_TX_PARAMS, maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], }); const txResult = await tx.waitForResult(); @@ -258,14 +268,15 @@ describe('Bridging ERC20 tokens', async function () { it('Bridge ERC20 via Fuel token contract', async () => { // withdraw tokens back to the base chain - fuel_testToken.account = fuelTokenSender; + fuel_bridge.account = fuelTokenSender; const paddedAddress = '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); const fuelTokenSenderBalance = await fuelTokenSender.getBalance( fuel_testAssetId ); - const transactionRequest = await fuel_testToken.functions + const transactionRequest = await fuel_bridge.functions .withdraw(paddedAddress) + .addContracts([fuel_bridge, fuel_bridgeImpl]) .txParams({ tip: 0, gasLimit: 1_000_000, diff --git a/packages/integration-tests/tests/bridge_erc721.ts b/packages/integration-tests/tests/bridge_erc721.ts index 5e0cdc3e..43f378f2 100644 --- a/packages/integration-tests/tests/bridge_erc721.ts +++ b/packages/integration-tests/tests/bridge_erc721.ts @@ -5,7 +5,7 @@ import { relayCommonMessage, waitForMessage, createRelayMessageParams, - getOrDeployFuelTokenContract, + getOrDeployL2Bridge, FUEL_TX_PARAMS, getMessageOutReceipt, fuel_to_eth_address, @@ -54,12 +54,15 @@ describe.skip('Bridging ERC721 tokens', async function () { eth_erc721GatewayAddress = ( await env.eth.fuelERC721Gateway.getAddress() ).toLowerCase(); - fuel_testToken = await getOrDeployFuelTokenContract( + + const { contract } = await getOrDeployL2Bridge( env, env.eth.fuelERC721Gateway, FUEL_TX_PARAMS, 0 ); + + fuel_testToken = contract; fuel_testContractId = fuel_testToken.id.toHexString(); const { value: expectedTokenContractId } = await fuel_testToken.functions diff --git a/packages/integration-tests/tests/bridge_proxy.ts b/packages/integration-tests/tests/bridge_proxy.ts new file mode 100644 index 00000000..99494906 --- /dev/null +++ b/packages/integration-tests/tests/bridge_proxy.ts @@ -0,0 +1,179 @@ +import type { TestEnvironment } from '@fuel-bridge/test-utils'; +import { + setupEnvironment, + getOrDeployL2Bridge, + FUEL_TX_PARAMS, +} from '@fuel-bridge/test-utils'; +import chai from 'chai'; +import type { Contract, FuelError } from 'fuels'; + +const { expect } = chai; + +describe('Proxy', async function () { + // override the default test timeout from 2000ms + const DEFAULT_TIMEOUT_MS: number = 400_000; + this.timeout(DEFAULT_TIMEOUT_MS); + + let env: TestEnvironment; + let fuel_bridgeImpl: Contract; + let fuel_proxy: Contract; + + before(async () => { + env = await setupEnvironment({}); + + const { proxy, implementation } = await getOrDeployL2Bridge( + env, + env.eth.fuelERC20Gateway, + FUEL_TX_PARAMS + ); + + fuel_proxy = proxy; + fuel_bridgeImpl = implementation; + }); + + describe('_proxy_owner()', () => { + it('correctly initializes the proxy owner', async () => { + const { value } = await fuel_proxy.functions._proxy_owner().dryRun(); + expect(value).to.have.property('Initialized'); + expect(value.Initialized.Address.bits).to.be.equal( + env.fuel.deployer.address.toHexString() + ); + }); + }); + + describe('_proxy_change_owner()', () => { + it('rejects unauthorized calls', async () => { + const mallory = env.fuel.signers[0]; + fuel_proxy.account = mallory; + + const addressInput = { bits: mallory.address.toB256() }; + const addressIdentityInput = { Address: addressInput }; + + const tx = fuel_proxy.functions + ._proxy_change_owner(addressIdentityInput) + .call(); + const [txResult] = await Promise.allSettled([tx]); + + if (txResult.status === 'fulfilled') { + throw new Error('Transaction did not revert'); + } + const { message } = txResult.reason as FuelError; + + expect(message).contains('NotOwner'); + }); + + it('changes the owner', async () => { + const oldOwner = env.fuel.deployer; + const newOwner = env.fuel.signers[0]; + + { + fuel_proxy.account = oldOwner; + const addressInput = { bits: newOwner.address.toB256() }; + const addressIdentityInput = { Address: addressInput }; + const tx = await fuel_proxy.functions + ._proxy_change_owner(addressIdentityInput) + .call(); + const result = await tx.transactionResponse.waitForResult(); + expect(result.status).to.equal('success'); + + const { value } = await fuel_proxy.functions._proxy_owner().dryRun(); + expect(value).to.have.property('Initialized'); + expect(value.Initialized.Address.bits).to.be.equal( + newOwner.address.toHexString() + ); + } + + { + fuel_proxy.account = newOwner; + const addressInput = { bits: oldOwner.address.toB256() }; + const addressIdentityInput = { Address: addressInput }; + const tx = await fuel_proxy.functions + ._proxy_change_owner(addressIdentityInput) + .call(); + const result = await tx.transactionResponse.waitForResult(); + expect(result.status).to.equal('success'); + + const { value } = await fuel_proxy.functions._proxy_owner().dryRun(); + expect(value).to.have.property('Initialized'); + expect(value.Initialized.Address.bits).to.be.equal( + oldOwner.address.toHexString() + ); + } + }); + }); + + describe('_proxy_target()', () => { + it('correctly initializes the proxy target', async () => { + fuel_proxy.account = env.fuel.deployer; + const { value } = await fuel_proxy.functions._proxy_target().dryRun(); + expect(value.bits).to.be.equal(fuel_bridgeImpl.id.toHexString()); + }); + }); + + describe('set_proxy_target', () => { + const contractId = + '0x7296ff960b5eb86b5f79aa587d7ebe1bae147c7cac046a16d062fbd7f3a753ec'; + const contractIdentityInput = { bits: contractId.toString() }; + + it('rejects unauthorized calls', async () => { + const mallory = env.fuel.signers[0]; + fuel_proxy.account = mallory; + + const tx = fuel_proxy.functions + .set_proxy_target(contractIdentityInput) + .call(); + const [txResult] = await Promise.allSettled([tx]); + + if (txResult.status === 'fulfilled') { + throw new Error('Transaction did not revert'); + } + const { message } = txResult.reason as FuelError; + + expect(message).contains('NotOwner'); + }); + + it('correctly changes the target', async () => { + fuel_proxy.account = env.fuel.deployer; + + const tx = await fuel_proxy.functions + .set_proxy_target(contractIdentityInput) + .call(); + const result = await tx.transactionResponse.waitForResult(); + expect(result.status).to.equal('success'); + + const { value } = await fuel_proxy.functions._proxy_target().dryRun(); + expect(value.bits).to.be.equal(contractId); + }); + }); + + describe('_proxy_revoke_ownership()', () => { + const contractId = + '0x7296ff960b5eb86b5f79aa587d7ebe1bae147c7cac046a16d062fbd7f3a753ec'; + const contractIdentityInput = { bits: contractId.toString() }; + + it('revokes ownership', async () => { + fuel_proxy.account = env.fuel.deployer; + const tx = await fuel_proxy.functions._proxy_revoke_ownership().call(); + const result = await tx.transactionResponse.waitForResult(); + expect(result.status).to.equal('success'); + + const { value } = await fuel_proxy.functions._proxy_owner().dryRun(); + expect(value).to.have.property('Revoked'); + }); + + it('disallows proxy upgrades', async () => { + fuel_proxy.account = env.fuel.deployer; + const tx = fuel_proxy.functions + .set_proxy_target(contractIdentityInput) + .call(); + const [txResult] = await Promise.allSettled([tx]); + + if (txResult.status === 'fulfilled') { + throw new Error('Transaction did not revert'); + } + const { message } = txResult.reason as FuelError; + + expect(message).contains('NotOwner'); + }); + }); +}); diff --git a/packages/message-predicates/contract-message-predicate/tests/utils/builder.rs b/packages/message-predicates/contract-message-predicate/tests/utils/builder.rs index c0f81016..91d8e91e 100644 --- a/packages/message-predicates/contract-message-predicate/tests/utils/builder.rs +++ b/packages/message-predicates/contract-message-predicate/tests/utils/builder.rs @@ -43,7 +43,6 @@ pub async fn build_contract_message_tx( .unwrap(); let funding_utx0 = fetched_gas_coins.first().unwrap().to_owned(); - tx_inputs.push(Input::resource_signed(CoinType::Coin(funding_utx0.clone()))); tx_outputs.push(Output::Change { to: wallet.address().into(), diff --git a/packages/message-predicates/contract-message-predicate/tests/utils/environment.rs b/packages/message-predicates/contract-message-predicate/tests/utils/environment.rs index 7d5f805f..332fc648 100644 --- a/packages/message-predicates/contract-message-predicate/tests/utils/environment.rs +++ b/packages/message-predicates/contract-message-predicate/tests/utils/environment.rs @@ -8,9 +8,7 @@ use fuels::{ LoadConfiguration, TxPolicies, }, test_helpers::{setup_single_message, setup_test_provider}, - types::{ - coin_type::CoinType, input::Input, message::Message, - }, + types::{coin_type::CoinType, input::Input, message::Message}, }; use fuel_tx::{Bytes32, TxId, TxPointer, UtxoId, Word}; diff --git a/packages/test-utils/src/utils/fuels/getOrDeployFuelTokenContract.ts b/packages/test-utils/src/utils/fuels/getOrDeployFuelTokenContract.ts deleted file mode 100644 index 8fb38e3d..00000000 --- a/packages/test-utils/src/utils/fuels/getOrDeployFuelTokenContract.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - fungibleTokenBinary, - fungibleTokenABI, -} from '@fuel-bridge/fungible-token'; -import type { AddressLike } from 'ethers'; -import type { TxParams } from 'fuels'; -import { ContractFactory, Contract } from 'fuels'; - -import { debug } from '../logs'; -import { eth_address_to_b256 } from '../parsers'; -import type { TestEnvironment } from '../setup'; - -const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; - -export async function getOrDeployFuelTokenContract( - env: TestEnvironment, - ethTokenGateway: AddressLike, - fuelTxParams: TxParams, - DECIMALS?: number -) { - if (typeof ethTokenGateway !== 'string') { - ethTokenGateway = - 'then' in ethTokenGateway - ? await ethTokenGateway - : await ethTokenGateway.getAddress(); - } - - const tokenGateway = ethTokenGateway.replace('0x', ''); - const fuelAcct = env.fuel.signers[1]; - - let fuelTestToken: Contract = null; - if (FUEL_FUNGIBLE_TOKEN_ADDRESS) { - try { - fuelTestToken = new Contract( - FUEL_FUNGIBLE_TOKEN_ADDRESS, - fungibleTokenABI as any, - fuelAcct - ); - await fuelTestToken.functions.name().dryRun(); - } catch (e) { - fuelTestToken = null; - debug( - `The Fuel fungible token contract could not be found at the provided address ${FUEL_FUNGIBLE_TOKEN_ADDRESS}.` - ); - } - } - if (!fuelTestToken) { - debug(`Creating Fuel fungible token contract to test with...`); - const bytecodeHex = fungibleTokenBinary; - debug('Replace ECR20 contract id'); - debug('Deploy contract on Fuel'); - const factory = new ContractFactory( - bytecodeHex, - fungibleTokenABI as any, - env.fuel.deployer - ); - - const configurableConstants: any = { - BRIDGED_TOKEN_GATEWAY: eth_address_to_b256(tokenGateway), - }; - - if (DECIMALS !== undefined) configurableConstants['DECIMALS'] = DECIMALS; - - // Set the token gateway and token address in the contract - factory.setConfigurableConstants(configurableConstants); - - const { contractId, transactionRequest } = factory.createTransactionRequest( - { - ...fuelTxParams, - storageSlots: [], - } - ); - const { requiredQuantities } = await fuelAcct.provider.getTransactionCost( - transactionRequest - ); - - await fuelAcct.fund(transactionRequest, { - requiredQuantities, - estimatedPredicates: [], - addedSignatures: 0, - }); - // send transaction - const response = await fuelAcct.sendTransaction(transactionRequest); - await response.wait(); - // create contract instance - fuelTestToken = new Contract( - contractId, - factory.interface, - factory.account - ); - debug( - `Fuel fungible token contract created at ${fuelTestToken.id.toHexString()}.` - ); - - const [fuelSigner] = env.fuel.signers; - fuelTestToken.account = fuelSigner; - - debug('Set up bridge contract'); - } - fuelTestToken.account = fuelAcct; - const fuelTestTokenId = fuelTestToken.id.toHexString(); - debug(`Testing with Fuel fungible token contract at ${fuelTestTokenId}.`); - - return fuelTestToken; -} diff --git a/packages/test-utils/src/utils/fuels/getOrDeployL2Bridge.ts b/packages/test-utils/src/utils/fuels/getOrDeployL2Bridge.ts new file mode 100644 index 00000000..a1d39742 --- /dev/null +++ b/packages/test-utils/src/utils/fuels/getOrDeployL2Bridge.ts @@ -0,0 +1,187 @@ +import { + fungibleTokenBinary, + fungibleTokenABI, + bridgeProxyBinary, + bridgeProxyABI, + bridgeProxyStorageSlots, +} from '@fuel-bridge/fungible-token'; +import type { AddressLike } from 'ethers'; +import type { TxParams } from 'fuels'; +import { ContractFactory, Contract } from 'fuels'; + +import { debug } from '../logs'; +import { eth_address_to_b256 } from '../parsers'; +import type { TestEnvironment } from '../setup'; + +const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; + +export async function getOrDeployL2Bridge( + env: TestEnvironment, + ethTokenGateway: AddressLike, + fuelTxParams: TxParams, + DECIMALS?: number +) { + if (typeof ethTokenGateway !== 'string') { + ethTokenGateway = + 'then' in ethTokenGateway + ? await ethTokenGateway + : await ethTokenGateway.getAddress(); + } + + const tokenGateway = ethTokenGateway.replace('0x', ''); + const fuelAcct = env.fuel.signers[1]; + + let l2Bridge: Contract; + let proxy: Contract; + let implementation: Contract; + + if (FUEL_FUNGIBLE_TOKEN_ADDRESS) { + try { + proxy = new Contract( + FUEL_FUNGIBLE_TOKEN_ADDRESS, + bridgeProxyABI as any, + fuelAcct + ); + + const { value: implementationContractId } = await proxy.functions + ._proxy_target() + .dryRun(); + + implementation = new Contract( + implementationContractId.bits, + fungibleTokenABI as any, + fuelAcct + ); + + l2Bridge = new Contract( + FUEL_FUNGIBLE_TOKEN_ADDRESS, + fungibleTokenABI as any, + fuelAcct + ); + } catch (e) { + l2Bridge = null; + debug( + `The Fuel bridge contract could not be found at the provided address ${FUEL_FUNGIBLE_TOKEN_ADDRESS}.` + ); + } + } + if (!l2Bridge) { + debug(`Creating Fuel bridge contract to test with...`); + const bytecodeHex = fungibleTokenBinary; + const implFactory = new ContractFactory( + bytecodeHex, + fungibleTokenABI as any, + env.fuel.deployer + ); + + const implConfigurables: any = { + BRIDGED_TOKEN_GATEWAY: eth_address_to_b256(tokenGateway), + }; + + if (DECIMALS !== undefined) implConfigurables['DECIMALS'] = DECIMALS; + + // Set the token gateway and token address in the contract + implFactory.setConfigurableConstants(implConfigurables); + + const { + contractId: implContractId, + transactionRequest: implCreateTxRequest, + } = implFactory.createTransactionRequest({ + ...fuelTxParams, + storageSlots: [], + }); + + { + const { requiredQuantities } = await fuelAcct.provider.getTransactionCost( + implCreateTxRequest + ); + + await fuelAcct.fund(implCreateTxRequest, { + requiredQuantities, + estimatedPredicates: [], + addedSignatures: 0, + }); + + // send transaction + + debug('Deploying implementation contract...'); + const response = await fuelAcct.sendTransaction(implCreateTxRequest); + await response.wait(); + debug(`Implementation contract deployed at ${implContractId}.`); + } + + debug('Creating proxy contract'); + const proxyFactory = new ContractFactory( + bridgeProxyBinary, + bridgeProxyABI, + env.fuel.deployer + ); + + const proxyConfigurables: any = { + INITIAL_TARGET: { bits: implContractId }, + INITIAL_OWNER: { + Initialized: { + Address: { bits: env.fuel.deployer.address.toHexString() }, + }, + }, + }; + + proxyFactory.setConfigurableConstants(proxyConfigurables); + + const { + contractId: proxyContractId, + transactionRequest: proxyCreateTxRequest, + } = proxyFactory.createTransactionRequest({ + ...fuelTxParams, + storageSlots: bridgeProxyStorageSlots, + }); + + { + const { requiredQuantities } = await fuelAcct.provider.getTransactionCost( + proxyCreateTxRequest + ); + + await fuelAcct.fund(proxyCreateTxRequest, { + requiredQuantities, + estimatedPredicates: [], + addedSignatures: 0, + }); + + // send deployment transaction + debug('Deploying proxy contract...'); + const response = await fuelAcct.sendTransaction(proxyCreateTxRequest); + await response.wait(); + debug(`Proxy contract deployed at ${proxyContractId}.`); + } + + // create contract instance + l2Bridge = new Contract( + proxyContractId, + implFactory.interface, + implFactory.account + ); + + proxy = new Contract( + proxyContractId, + proxyFactory.interface, + proxyFactory.account + ); + implementation = new Contract( + implContractId, + implFactory.interface, + implFactory.account + ); + + const [fuelSigner] = env.fuel.signers; + l2Bridge.account = fuelSigner; + + debug('Finished setting up bridge'); + } + + l2Bridge.account = fuelAcct; + debug( + `Testing with Fuel fungible token contract at ${l2Bridge.id.toHexString()}.` + ); + + return { contract: l2Bridge, proxy, implementation }; +} diff --git a/packages/test-utils/src/utils/fuels/index.ts b/packages/test-utils/src/utils/fuels/index.ts index f250f15a..3371f41c 100644 --- a/packages/test-utils/src/utils/fuels/index.ts +++ b/packages/test-utils/src/utils/fuels/index.ts @@ -1,6 +1,6 @@ export * from './getBlock'; export * from './getMessageOutReceipt'; -export * from './getOrDeployFuelTokenContract'; +export * from './getOrDeployL2Bridge'; export * from './getTokenId'; export * from './relayCommonMessage'; export * from './transaction'; diff --git a/packages/test-utils/src/utils/fuels/relayCommonMessage.ts b/packages/test-utils/src/utils/fuels/relayCommonMessage.ts index 1f51b696..9cb1c8f6 100644 --- a/packages/test-utils/src/utils/fuels/relayCommonMessage.ts +++ b/packages/test-utils/src/utils/fuels/relayCommonMessage.ts @@ -10,6 +10,7 @@ import type { TransactionResponse, Provider, ScriptTransactionRequestLike, + BytesLike, } from 'fuels'; import { ZeroBytes32, @@ -26,6 +27,26 @@ import { debug } from '../logs'; import { resourcesToInputs } from './transaction'; +type RelayMessageOptions = Pick< + ScriptTransactionRequestLike, + 'gasLimit' | 'maturity' | 'maxFee' +> & { + contractIds?: BytesLike[]; +}; + +type CommonMessageDetails = { + name: string; + predicateRoot: string; + predicate: string; + script: string; + buildTx: ( + relayer: FuelWallet, + message: Message, + details: CommonMessageDetails, + opts?: RelayMessageOptions + ) => Promise; +}; + // Details for relaying common messages with certain predicate roots function getCommonRelayableMessages(provider: Provider) { // Create a predicate for common messages @@ -46,7 +67,8 @@ function getCommonRelayableMessages(provider: Provider) { buildTx: async ( relayer: FuelWallet, message: Message, - details: CommonMessageDetails + details: CommonMessageDetails, + opts?: RelayMessageOptions ): Promise => { const script = arrayify(details.script); const predicateBytecode = arrayify(details.predicate); @@ -80,17 +102,35 @@ function getCommonRelayableMessages(provider: Provider) { nonce: message.nonce, predicate: predicateBytecode, }); + transaction.inputs.push({ type: InputType.Contract, txPointer: ZeroBytes32, contractId, }); + + for (const additionalContractId of opts.contractIds || []) { + transaction.inputs.push({ + type: InputType.Contract, + txPointer: ZeroBytes32, + contractId: additionalContractId, + }); + } + transaction.inputs.push(...spendableInputs); transaction.outputs.push({ type: OutputType.Contract, inputIndex: 1, }); + + for (const [index] of (opts.contractIds || []).entries()) { + transaction.outputs.push({ + type: OutputType.Contract, + inputIndex: 2 + index, + }); + } + transaction.outputs.push({ type: OutputType.Change, to: relayer.address.toB256(), @@ -125,27 +165,11 @@ function getCommonRelayableMessages(provider: Provider) { return relayableMessages; } -type CommonMessageDetails = { - name: string; - predicateRoot: string; - predicate: string; - script: string; - buildTx: ( - relayer: FuelWallet, - message: Message, - details: CommonMessageDetails, - txParams: Pick - ) => Promise; -}; - // Relay commonly used messages with predicates spendable by anyone export async function relayCommonMessage( relayer: FuelWallet, message: Message, - txParams?: Pick< - ScriptTransactionRequestLike, - 'gasLimit' | 'maturity' | 'maxFee' - > + opts?: RelayMessageOptions ): Promise { // find the relay details for the specified message let messageRelayDetails: CommonMessageDetails = null; @@ -165,7 +189,7 @@ export async function relayCommonMessage( relayer, message, messageRelayDetails, - txParams || {} + opts || {} ); const estimated_tx = await relayer.provider.estimatePredicates(transaction); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dec8d1c7..fcbf8ed8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ importers: version: 2.8.8 tsup: specifier: ^7.2.0 - version: 7.3.0(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5))(typescript@5.4.5) + version: 7.3.0(ts-node@10.9.2(typescript@5.4.5))(typescript@5.4.5) tsx: specifier: ^3.12.7 version: 3.14.0 @@ -9367,7 +9367,7 @@ snapshots: optionalDependencies: ts-node: 10.9.2(@types/node@18.19.31)(typescript@4.9.5) - postcss-load-config@4.0.2(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)): + postcss-load-config@4.0.2(ts-node@10.9.2(typescript@5.4.5)): dependencies: lilconfig: 3.1.1 yaml: 2.4.1 @@ -10222,7 +10222,7 @@ snapshots: - supports-color - ts-node - tsup@7.3.0(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5))(typescript@5.4.5): + tsup@7.3.0(ts-node@10.9.2(typescript@5.4.5))(typescript@5.4.5): dependencies: bundle-require: 4.0.2(esbuild@0.19.12) cac: 6.7.14 @@ -10232,7 +10232,7 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)) + postcss-load-config: 4.0.2(ts-node@10.9.2(typescript@5.4.5)) resolve-from: 5.0.0 rollup: 4.14.3 source-map: 0.8.0-beta.0