diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e51cf345108..7354088ec0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -596,13 +596,11 @@ jobs: strategy: matrix: tool: - - test: cargo test --locked --release -p forc-client -- --test-threads 1 - test: cargo test --locked --release -p forc-debug - - test: cargo test --locked --release -p forc-client -- --test-threads 1 - test: cargo test --locked --release -p forc-mcp -- --test-threads 1 - test: cargo nextest run --locked --release -p sway-lsp --no-capture --profile ci --config-file sway-lsp/tests/nextest.toml - test: cargo test --locked --release -p forc -- --nocapture - - test: cargo test --locked --release --workspace --exclude forc-debug --exclude sway-lsp --exclude forc-client --exclude forc-mcp --exclude forc + - test: cargo test --locked --release --workspace --exclude forc-debug --exclude sway-lsp --exclude forc-mcp --exclude forc steps: - name: Checkout sources uses: actions/checkout@v4 @@ -871,13 +869,13 @@ jobs: - name: Strip release binaries x86_64-linux-gnu if: matrix.job.target == 'x86_64-unknown-linux-gnu' run: | - for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-tx forc-submit forc-mcp forc-migrate forc-publish forc-call; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-doc forc-tx forc-mcp forc-migrate forc-publish; do strip "target/${{ matrix.job.target }}/release/$BINARY" done - name: Strip release binaries aarch64-linux-gnu if: matrix.job.target == 'aarch64-unknown-linux-gnu' run: | - for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-tx forc-submit forc-mcp forc-migrate forc-publish forc-call; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-doc forc-tx forc-mcp forc-migrate forc-publish; do docker run --rm -v \ "$PWD/target:/target:Z" \ ghcr.io/cross-rs/${{ matrix.job.target }}:main \ @@ -887,7 +885,7 @@ jobs: - name: Strip release binaries mac if: matrix.job.os == 'macos-latest' run: | - for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-tx forc-submit forc-mcp forc-migrate forc-publish forc-call; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-doc forc-tx forc-mcp forc-migrate forc-publish; do strip -x "target/${{ matrix.job.target }}/release/$BINARY" done @@ -901,7 +899,7 @@ jobs: ZIP_FILE_NAME=forc-binaries-${{ env.PLATFORM_NAME }}_${{ env.ARCH }}.tar.gz echo "ZIP_FILE_NAME=$ZIP_FILE_NAME" >> $GITHUB_ENV mkdir -pv ./forc-binaries - for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-tx forc-submit forc-mcp forc-migrate forc-publish forc-call; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-doc forc-tx forc-mcp forc-migrate forc-publish; do cp "target/${{ matrix.job.target }}/release/$BINARY" ./forc-binaries done tar -czvf $ZIP_FILE_NAME ./forc-binaries diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 67c427314d4..d7d0be2d970 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -32,7 +32,7 @@ jobs: - name: Install Forc plugins run: | - cargo install --locked --debug --path ./forc-plugins/forc-client + cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-client cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-crypto cargo install --locked --debug --path ./forc-plugins/forc-debug cargo install --locked --debug --path ./forc-plugins/forc-fmt diff --git a/.github/workflows/scripts/verify_tag.sh b/.github/workflows/scripts/verify_tag.sh index 5c88e7da8cc..5c2613897ac 100755 --- a/.github/workflows/scripts/verify_tag.sh +++ b/.github/workflows/scripts/verify_tag.sh @@ -48,7 +48,6 @@ for toml_path in \ "workspace.dependencies.forc-test.version" \ "workspace.dependencies.forc-util.version" \ "workspace.dependencies.forc-plugins.version" \ - "workspace.dependencies.forc-client.version" \ "workspace.dependencies.forc-debug.version" \ "workspace.dependencies.forc-doc.version" \ "workspace.dependencies.forc-fmt.version" \ diff --git a/Cargo.lock b/Cargo.lock index d9013cc8c31..feee725e639 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2160,7 +2160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] @@ -2391,6 +2391,15 @@ dependencies = [ "dirs-sys 0.4.1", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -2408,7 +2417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -2420,10 +2429,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.1", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -2431,7 +2452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -2742,7 +2763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.1", ] [[package]] @@ -3028,23 +3049,23 @@ dependencies = [ "clap_complete", "clap_complete_fig", "completest-pty", - "forc-pkg", - "forc-test", + "forc-pkg 0.70.3", + "forc-test 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "fs_extra", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-asm 0.65.0", "hex", "rexpect", "serde", "serde_json", - "sway-core", - "sway-error", - "sway-features", - "sway-ir", - "sway-types", - "sway-utils", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-ir 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", "term-table", "tokio", "toml 0.8.23", @@ -3056,8 +3077,48 @@ dependencies = [ ] [[package]] -name = "forc-client" +name = "forc" version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbb13624c61a3ff5eef66e9e029a2e47ae185e69004dbb61562fcafaca10b04" +dependencies = [ + "annotate-snippets", + "ansiterm", + "anyhow", + "clap", + "clap_complete", + "clap_complete_fig", + "forc-pkg 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-test 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-tracing 0.71.1", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fs_extra", + "fuel-abi-types", + "fuel-asm 0.65.0", + "hex", + "serde", + "serde_json", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-ir 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "term-table", + "tokio", + "toml 0.8.23", + "toml_edit 0.22.27", + "tracing", + "url", + "walkdir", + "whoami", +] + +[[package]] +name = "forc-client" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefecb62ef5d811cf1f9530382ce02c006475905f70e178bd0f6fe348c19b152" dependencies = [ "ansiterm", "anyhow", @@ -3069,14 +3130,14 @@ dependencies = [ "devault", "dialoguer", "either", - "forc", - "forc-debug", - "forc-pkg", - "forc-tracing 0.71.1", - "forc-tx", - "forc-util", + "forc 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-debug 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-pkg 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-tracing 0.72.0", + "forc-tx 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "forc-wallet", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-core-client 0.47.1", "fuel-core-storage 0.47.1", "fuel-core-types 0.47.1", @@ -3089,21 +3150,16 @@ dependencies = [ "futures", "hex", "k256", - "portpicker", - "pretty_assertions", "rand 0.8.5", "regex", "reqwest", - "rexpect", "rpassword", "serde", "serde_json", - "sway-ast", - "sway-core", - "sway-features", - "sway-parse", - "sway-types", - "sway-utils", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile", "tokio", "toml_edit 0.22.27", @@ -3120,11 +3176,11 @@ dependencies = [ "dap", "dirs 5.0.1", "escargot", - "forc-pkg", - "forc-test", + "forc-pkg 0.70.3", + "forc-test 0.70.3", "forc-tracing 0.71.1", - "forc-util", - "fuel-abi-types 0.15.3", + "forc-util 0.70.3", + "fuel-abi-types", "fuel-core-client 0.47.1", "fuel-tx 0.65.0", "fuel-types 0.65.0", @@ -3136,9 +3192,41 @@ dependencies = [ "serde", "serde_json", "strsim 0.11.1", - "sway-core", - "sway-features", - "sway-types", + "sway-core 0.70.3", + "sway-features 0.70.3", + "sway-types 0.70.3", + "thiserror 1.0.69", + "tokio", + "walkdir", +] + +[[package]] +name = "forc-debug" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7604849d881beec8a1bae31b966df1ea01b9a5372a51bf64230a468b0926191b" +dependencies = [ + "anyhow", + "clap", + "dap", + "dirs 5.0.1", + "forc-pkg 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-test 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-tracing 0.71.1", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuel-abi-types", + "fuel-core-client 0.47.1", + "fuel-tx 0.65.0", + "fuel-types 0.65.0", + "fuel-vm 0.65.0", + "rayon", + "rustyline", + "serde", + "serde_json", + "strsim 0.11.1", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", "tokio", "walkdir", @@ -3154,9 +3242,9 @@ dependencies = [ "comrak", "dir_indexer", "expect-test", - "forc-pkg", + "forc-pkg 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "horrorshow", "include_dir", "minifier", @@ -3164,12 +3252,12 @@ dependencies = [ "rayon", "serde", "serde_json", - "sway-ast", - "sway-core", - "sway-features", + "sway-ast 0.70.3", + "sway-core 0.70.3", + "sway-features 0.70.3", "sway-lsp", - "sway-types", - "swayfmt", + "sway-types 0.70.3", + "swayfmt 0.70.3", ] [[package]] @@ -3178,14 +3266,14 @@ version = "0.70.3" dependencies = [ "anyhow", "clap", - "forc-pkg", + "forc-pkg 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "prettydiff", - "sway-core", - "sway-features", - "sway-utils", - "swayfmt", + "sway-core 0.70.3", + "sway-features 0.70.3", + "sway-utils 0.70.3", + "swayfmt 0.70.3", "taplo", "tracing", ] @@ -3211,7 +3299,6 @@ dependencies = [ "chrono", "clap", "forc-client", - "forc-tx", "fuel-crypto 0.65.0", "fuels 0.76.0", "fuels-accounts 0.76.0", @@ -3236,18 +3323,18 @@ dependencies = [ "anyhow", "clap", "duplicate", - "forc-pkg", + "forc-pkg 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "itertools 0.13.0", "num-bigint", "sha2", - "sway-ast", - "sway-core", - "sway-error", - "sway-features", - "sway-types", - "swayfmt", + "sway-ast 0.70.3", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-types 0.70.3", + "swayfmt 0.70.3", ] [[package]] @@ -3260,8 +3347,8 @@ dependencies = [ "cid", "flate2", "forc-tracing 0.71.1", - "forc-util", - "fuel-abi-types 0.15.3", + "forc-util 0.70.3", + "fuel-abi-types", "futures", "git2", "gix-url", @@ -3277,12 +3364,12 @@ dependencies = [ "serde_ignored", "serde_json", "serde_with", - "sway-core", - "sway-error", - "sway-features", - "sway-types", - "sway-utils", - "sysinfo", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", + "sysinfo 0.29.11", "tar", "tempfile", "tokio", @@ -3294,6 +3381,50 @@ dependencies = [ "walkdir", ] +[[package]] +name = "forc-pkg" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697aec428136e05f570590bddd3a0fe604755509ee609db071c26c60626de9e4" +dependencies = [ + "ansiterm", + "anyhow", + "byte-unit", + "cid", + "flate2", + "forc-tracing 0.71.1", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuel-abi-types", + "futures", + "git2", + "gix-url", + "hex", + "ipfs-api-backend-hyper", + "once_cell", + "petgraph", + "reqwest", + "scopeguard", + "semver 1.0.27", + "serde", + "serde_ignored", + "serde_json", + "serde_with", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sysinfo 0.29.11", + "tar", + "tokio", + "toml 0.8.23", + "toml_edit 0.22.27", + "tracing", + "url", + "vec1", + "walkdir", +] + [[package]] name = "forc-publish" version = "0.70.3" @@ -3301,9 +3432,9 @@ dependencies = [ "anyhow", "clap", "flate2", - "forc-pkg", + "forc-pkg 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "futures-util", "regex", "reqwest", @@ -3328,17 +3459,37 @@ name = "forc-test" version = "0.70.3" dependencies = [ "anyhow", - "forc-pkg", - "forc-util", - "fuel-abi-types 0.15.3", + "forc-pkg 0.70.3", + "forc-util 0.70.3", + "fuel-abi-types", "fuel-tx 0.65.0", "fuel-vm 0.65.0", "rand 0.8.5", "rayon", "serde_json", - "sway-core", - "sway-features", - "sway-types", + "sway-core 0.70.3", + "sway-features 0.70.3", + "sway-types 0.70.3", +] + +[[package]] +name = "forc-test" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53993500d3a6574528665f78079dcbe563b8f4e20ce1c16f54de9187d8fcac17" +dependencies = [ + "anyhow", + "forc-pkg 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuel-abi-types", + "fuel-tx 0.65.0", + "fuel-vm 0.65.0", + "rand 0.8.5", + "rayon", + "serde_json", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3365,6 +3516,20 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "forc-tracing" +version = "0.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d752bca8a010d9e997bd8a947e4b18dad9bd628a3905aa4a0ce4a5f0277557d2" +dependencies = [ + "ansiterm", + "anyhow", + "fuel-telemetry", + "regex", + "tracing", + "tracing-subscriber", +] + [[package]] name = "forc-tx" version = "0.70.3" @@ -3372,7 +3537,25 @@ dependencies = [ "anyhow", "clap", "devault", - "forc-util", + "forc-util 0.70.3", + "fuel-tx 0.65.0", + "fuel-types 0.65.0", + "fuels-core 0.76.0", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "forc-tx" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52305baa3e13a9921a9ac6b6ff4113ed641de717d19045c613f2ad911b1f193b" +dependencies = [ + "anyhow", + "clap", + "devault", + "forc-util 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuel-tx 0.65.0", "fuel-types 0.65.0", "fuels-core 0.76.0", @@ -3392,7 +3575,7 @@ dependencies = [ "dirs 5.0.1", "fd-lock", "forc-tracing 0.71.1", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-asm 0.65.0", "fuel-tx 0.65.0", "fuels-core 0.76.0", @@ -3403,10 +3586,42 @@ dependencies = [ "serde", "serde_json", "sha2", - "sway-core", - "sway-error", - "sway-types", - "sway-utils", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", + "tracing", + "tracing-subscriber", + "unicode-xid", +] + +[[package]] +name = "forc-util" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd68e30ab3c232e33d2f3ee2a8e8a14d8e06536e194781fac8d7809dc9edd0e" +dependencies = [ + "annotate-snippets", + "ansiterm", + "anyhow", + "clap", + "dirs 5.0.1", + "fd-lock", + "forc-tracing 0.71.1", + "fuel-abi-types", + "fuel-asm 0.65.0", + "fuel-tx 0.65.0", + "fuels-core 0.76.0", + "hex", + "paste", + "regex", + "serde", + "serde_json", + "sha2", + "sway-core 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "tracing-subscriber", "unicode-xid", @@ -3414,15 +3629,15 @@ dependencies = [ [[package]] name = "forc-wallet" -version = "0.15.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe26faaf6b845bb47672ed9d9213a3031e694d55357d7642fe376d38380ada14" +checksum = "3ceec80d4309704913c928fb78f82a80016cad2c68f79fac47f901bb57e22075" dependencies = [ "anyhow", "clap", "eth-keystore", "forc-tracing 0.68.9", - "fuels 0.74.0", + "fuels 0.75.1", "futures", "hex", "home", @@ -3464,23 +3679,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "fuel-abi-types" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94916fee0e005fe1b9581c12edb7ad9b02c5b121b939716438d69ffcdd45e855" -dependencies = [ - "itertools 0.10.5", - "lazy_static", - "proc-macro2", - "quote", - "regex", - "serde", - "serde_json", - "syn 2.0.106", - "thiserror 1.0.69", -] - [[package]] name = "fuel-abi-types" version = "0.15.3" @@ -3536,15 +3734,15 @@ dependencies = [ [[package]] name = "fuel-core-chain-config" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83173aa86199e86d8be9c0cfbb348808a2b1fb6b6e13db3daf31a151853c3030" +checksum = "a22eafbabed0229dfc8af85619855887fc662a185c58cb3d578a63c8a372d327" dependencies = [ "anyhow", "bech32", "educe 0.6.0", - "fuel-core-storage 0.44.0", - "fuel-core-types 0.44.0", + "fuel-core-storage 0.46.0", + "fuel-core-types 0.46.0", "itertools 0.12.1", "postcard", "rand 0.8.5", @@ -3576,20 +3774,21 @@ dependencies = [ [[package]] name = "fuel-core-client" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea7fcdc7a51f5fbf403feb06fb45078e309bb2d4ec42b735744f2f1da59e657" +checksum = "a9543159741956f6a44d5f82127064c05a688eb6f1508a43806b9e60ffb36efe" dependencies = [ "anyhow", "base64 0.22.1", "cynic", "derive_more 0.99.20", "eventsource-client", - "fuel-core-types 0.44.0", + "fuel-core-types 0.46.0", "futures", "hex", "hyper-rustls 0.24.2", "itertools 0.12.1", + "postcard", "reqwest", "schemafy_lib", "serde", @@ -3627,9 +3826,9 @@ dependencies = [ [[package]] name = "fuel-core-metrics" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0412e48072faab1ea533feb8ef45c80d533c688524bd298c8f518c54209ccd" +checksum = "83bbaf694c40665c0cd0232a0feae9004c2c5bbd9e377dcda39ce84aa2f27686" dependencies = [ "once_cell", "parking_lot", @@ -3659,19 +3858,19 @@ dependencies = [ [[package]] name = "fuel-core-poa" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e35411b14efaa725f337d3d9d1145eac1ee2338ee78dd1cd9e0f689e7f61fe" +checksum = "e192252556882c140aed056f6de69c79aebf0416dd492605315870b28bd48e3d" dependencies = [ "anyhow", "async-trait", - "fuel-core-chain-config 0.44.0", - "fuel-core-services 0.44.0", - "fuel-core-storage 0.44.0", - "fuel-core-types 0.44.0", + "fuel-core-chain-config 0.46.0", + "fuel-core-services 0.46.0", + "fuel-core-storage 0.46.0", + "fuel-core-types 0.46.0", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -3699,13 +3898,13 @@ dependencies = [ [[package]] name = "fuel-core-services" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c78c38923404ddacb253113bd096fc009ccdcce4aeb53110a0974159b9b93f0e" +checksum = "e59554b05ce13920349ca3b16169537366c2fff8e3f048f091c73c5e648c13fe" dependencies = [ "anyhow", "async-trait", - "fuel-core-metrics 0.44.0", + "fuel-core-metrics 0.46.0", "futures", "parking_lot", "pin-project-lite", @@ -3731,14 +3930,14 @@ dependencies = [ [[package]] name = "fuel-core-storage" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecc0a879234546d38f234943e7e7253b9d4c169cc52862595ba5b26aaea40b9" +checksum = "9794ba715ae21e0fae1c7d27715d2b9e5141d19741bd16f31ec4e39bc3d4c270" dependencies = [ "anyhow", "derive_more 0.99.20", "enum-iterator", - "fuel-core-types 0.44.0", + "fuel-core-types 0.46.0", "fuel-vm 0.62.0", "impl-tools", "itertools 0.12.1", @@ -3775,9 +3974,9 @@ dependencies = [ [[package]] name = "fuel-core-types" -version = "0.44.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d720fc5e985b5fbb73fe5e948daf4839aab25c76666a87c0dfe9b5aee51505c" +checksum = "78ca27da74919063824f0826a6f707637d26db7b24ac237bf6cc1af5a866bb3a" dependencies = [ "anyhow", "bs58", @@ -3971,6 +4170,40 @@ version = "0.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b46be43225f70c17f9e028f80a583c275fb2dec1e2a4e9100aa6385a2c86a" +[[package]] +name = "fuel-telemetry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b6ff154c1f80b4b1a0ca31a4bf1883f48d30eef66ee2c47917b05ab9c44c5c" +dependencies = [ + "base64 0.22.1", + "chrono", + "dirs 6.0.0", + "fuel-telemetry-macros", + "influxdb-line-protocol", + "libc", + "nix 0.29.0", + "regex", + "reqwest", + "sysinfo 0.33.1", + "thiserror 2.0.17", + "tracing", + "tracing-appender", + "tracing-subscriber", + "uuid 1.18.1", +] + +[[package]] +name = "fuel-telemetry-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b81c8bba9a5be76590fabfb8741f9027bf593710ecce22d13e1b404c1d4ef692" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "fuel-tx" version = "0.62.0" @@ -4113,18 +4346,18 @@ dependencies = [ [[package]] name = "fuels" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e13622e2d44e7814daa8242a77fb0b88ae3210b2c90108550c373fe0695c5c4" +checksum = "71925fa65411ca3d3b15f659e502b722a4f23772a1ba707ca1a47a0bca71140e" dependencies = [ - "fuel-core-client 0.44.0", + "fuel-core-client 0.46.0", "fuel-crypto 0.62.0", "fuel-tx 0.62.0", - "fuels-accounts 0.74.0", - "fuels-core 0.74.0", - "fuels-macros 0.74.0", - "fuels-programs 0.74.0", - "fuels-test-helpers 0.74.0", + "fuels-accounts 0.75.1", + "fuels-core 0.75.1", + "fuels-macros 0.75.1", + "fuels-programs 0.75.1", + "fuels-test-helpers 0.75.1", ] [[package]] @@ -4145,19 +4378,19 @@ dependencies = [ [[package]] name = "fuels-accounts" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feb69d1125ea27b8f3020086e6394fe19867e137309da45faddce4cde98d9c3" +checksum = "053a093a9b8c5f008eacb8e86ae079631723bede0d795fafc5821d2f63184961" dependencies = [ "async-trait", "chrono", "cynic", - "fuel-core-client 0.44.0", - "fuel-core-types 0.44.0", + "fuel-core-client 0.46.0", + "fuel-core-types 0.46.0", "fuel-crypto 0.62.0", "fuel-tx 0.62.0", "fuel-types 0.62.0", - "fuels-core 0.74.0", + "fuels-core 0.75.1", "futures", "itertools 0.12.1", "k256", @@ -4197,12 +4430,12 @@ dependencies = [ [[package]] name = "fuels-code-gen" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2bf80dac7255cd5b3ed2a69745622946151fc85b92fe7b1c3e0bbeecba6322e" +checksum = "d264cafc426d0c69bef87921d32e2078679b304b375ee203bca61b82f8992982" dependencies = [ "Inflector", - "fuel-abi-types 0.12.0", + "fuel-abi-types", "itertools 0.12.1", "proc-macro2", "quote", @@ -4218,7 +4451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5d6a0c6d15b5bbba10b5862488c34fbeb06e82036409b6e663dedd68430aec9" dependencies = [ "Inflector", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "itertools 0.12.1", "proc-macro2", "quote", @@ -4229,23 +4462,24 @@ dependencies = [ [[package]] name = "fuels-core" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51b11f3d1a01839c84a71fcd486382bd626042faa5c4950436edd0355016c89" +checksum = "7b5206ac929045d956700b1dcd51a4ff65a3684a8c784bf07a7b2c9754ccecbf" dependencies = [ "async-trait", "auto_impl", "chrono", - "fuel-abi-types 0.12.0", + "fuel-abi-types", "fuel-asm 0.62.0", - "fuel-core-chain-config 0.44.0", - "fuel-core-client 0.44.0", - "fuel-core-types 0.44.0", + "fuel-core-chain-config 0.46.0", + "fuel-core-client 0.46.0", + "fuel-core-types 0.46.0", "fuel-crypto 0.62.0", "fuel-tx 0.62.0", "fuel-types 0.62.0", "fuel-vm 0.62.0", - "fuels-macros 0.74.0", + "fuels-code-gen 0.75.1", + "fuels-macros 0.75.1", "hex", "itertools 0.12.1", "postcard", @@ -4265,7 +4499,7 @@ dependencies = [ "async-trait", "auto_impl", "chrono", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-asm 0.65.0", "fuel-core-chain-config 0.47.1", "fuel-core-client 0.47.1", @@ -4288,11 +4522,11 @@ dependencies = [ [[package]] name = "fuels-macros" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0424e09f6c0b4ec2dd204af8195f74a4fb72879383de32541b71da0e1ab9b01a" +checksum = "f2e6fb142046e3ea6008dbef19028e2f14fbb2bf3ce8b8b5a68444bd34d597f5" dependencies = [ - "fuels-code-gen 0.74.0", + "fuels-code-gen 0.75.1", "itertools 0.12.1", "proc-macro2", "quote", @@ -4314,17 +4548,17 @@ dependencies = [ [[package]] name = "fuels-programs" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595f83f274003d1a75b1e065433e933286375aa8cde91d14787ec803bbc91b26" +checksum = "e6d8633dded2082dfaf004ea89b3313e342c1aeb232906a3f8ae3aec0b8ad946" dependencies = [ "async-trait", - "fuel-abi-types 0.12.0", + "fuel-abi-types", "fuel-asm 0.62.0", "fuel-tx 0.62.0", "fuel-types 0.62.0", - "fuels-accounts 0.74.0", - "fuels-core 0.74.0", + "fuels-accounts 0.75.1", + "fuels-core 0.75.1", "itertools 0.12.1", "rand 0.8.5", "serde_json", @@ -4338,7 +4572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d94a8919bc58ac98bd4376ad24b2137b75fad05eb096bf5dffe1d5438d684d1b" dependencies = [ "async-trait", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-asm 0.65.0", "fuel-tx 0.65.0", "fuel-types 0.65.0", @@ -4352,20 +4586,20 @@ dependencies = [ [[package]] name = "fuels-test-helpers" -version = "0.74.0" +version = "0.75.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95d72b29baad3bd8111085785e90357b2b9300bde65eabfbf142a8d8570f44d" +checksum = "acc96b30b63b228c87a18bcd61a9a8e5bcd1fca62f4cc5729346c3538b363e48" dependencies = [ - "fuel-core-chain-config 0.44.0", - "fuel-core-client 0.44.0", - "fuel-core-poa 0.44.0", - "fuel-core-services 0.44.0", - "fuel-core-types 0.44.0", + "fuel-core-chain-config 0.46.0", + "fuel-core-client 0.46.0", + "fuel-core-poa 0.46.0", + "fuel-core-services 0.46.0", + "fuel-core-types 0.46.0", "fuel-crypto 0.62.0", "fuel-tx 0.62.0", "fuel-types 0.62.0", - "fuels-accounts 0.74.0", - "fuels-core 0.74.0", + "fuels-accounts 0.75.1", + "fuels-core 0.75.1", "futures", "portpicker", "rand 0.8.5", @@ -5100,7 +5334,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.1", ] [[package]] @@ -5356,6 +5590,19 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +[[package]] +name = "influxdb-line-protocol" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fa7ee6be451ea0b1912b962c91c8380835e97cf1584a77e18264e908448dcb" +dependencies = [ + "bytes", + "log", + "nom", + "smallvec", + "snafu", +] + [[package]] name = "inout" version = "0.1.4" @@ -5387,17 +5634,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipfs-api-backend-hyper" version = "0.6.0" @@ -7377,6 +7613,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.17", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -7473,6 +7720,7 @@ dependencies = [ "cookie", "cookie_store", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2 0.4.12", @@ -7933,7 +8181,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.52.0", + "windows-sys 0.61.1", ] [[package]] @@ -8890,8 +9138,22 @@ dependencies = [ "num-bigint", "num-traits", "serde", - "sway-error", - "sway-types", + "sway-error 0.70.3", + "sway-types 0.70.3", +] + +[[package]] +name = "sway-ast" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1660b704771c6cbe1e83555737968722cf4d6516ff36df8e39c91ca852c38eac" +dependencies = [ + "extension-trait", + "num-bigint", + "num-traits", + "serde", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -8901,7 +9163,7 @@ dependencies = [ "clap", "dirs 5.0.1", "either", - "fuel-abi-types 0.15.3", + "fuel-abi-types", "fuel-ethabi", "fuel-etk-asm", "fuel-etk-ops", @@ -8925,15 +9187,63 @@ dependencies = [ "serde_json", "sha2", "strum 0.26.3", - "sway-ast", - "sway-error", - "sway-features", - "sway-ir", - "sway-parse", - "sway-types", - "sway-utils", - "swayfmt", - "sysinfo", + "sway-ast 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-ir 0.70.3", + "sway-parse 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", + "swayfmt 0.70.3", + "sysinfo 0.29.11", + "thiserror 1.0.69", + "toml 0.8.23", + "tracing", + "uint", + "vec1", +] + +[[package]] +name = "sway-core" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f34977b5a6bd24dbff3a7f179df6c9c820cd8a57008fdba19f37ff3dd5706c8" +dependencies = [ + "clap", + "dirs 5.0.1", + "either", + "fuel-abi-types", + "fuel-ethabi", + "fuel-etk-asm", + "fuel-etk-ops", + "fuel-vm 0.65.0", + "gimli 0.31.1", + "graph-cycles", + "hashbrown 0.14.5", + "im", + "indexmap 2.11.4", + "itertools 0.13.0", + "lazy_static", + "object 0.36.7", + "parking_lot", + "paste", + "pest", + "pest_derive", + "petgraph", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "sha2", + "strum 0.26.3", + "sway-ast 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-ir 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-parse 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "swayfmt 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sysinfo 0.29.11", "thiserror 1.0.69", "toml 0.8.23", "tracing", @@ -8950,7 +9260,22 @@ dependencies = [ "num-traits", "smallvec", "strsim 0.11.1", - "sway-types", + "sway-types 0.70.3", + "thiserror 1.0.69", +] + +[[package]] +name = "sway-error" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a6b77954972107afe7088a1d18425f35f280fd033a3b21aeac085a499ccc38" +dependencies = [ + "either", + "in_definite", + "num-traits", + "smallvec", + "strsim 0.11.1", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", ] @@ -8960,8 +9285,20 @@ version = "0.70.3" dependencies = [ "clap", "paste", - "sway-error", - "sway-types", + "sway-error 0.70.3", + "sway-types 0.70.3", +] + +[[package]] +name = "sway-features" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e21ef31e4c98372f445b20820c1bb28f69e2380da230558a5a2562ab011a35bf" +dependencies = [ + "clap", + "paste", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -8979,15 +9316,50 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "slotmap", - "sway-features", - "sway-ir-macros", - "sway-types", - "sway-utils", + "sway-features 0.70.3", + "sway-ir-macros 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", +] + +[[package]] +name = "sway-ir" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97af87f0a8c947ca096bbe85271152db8859f20c9b2d672d03305817d688c5f3" +dependencies = [ + "anyhow", + "downcast-rs", + "filecheck", + "indexmap 2.11.4", + "itertools 0.13.0", + "once_cell", + "peg", + "prettydiff", + "rustc-hash 1.1.0", + "serde", + "slotmap", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-ir-macros 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sway-ir-macros" +version = "0.70.3" +dependencies = [ + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] name = "sway-ir-macros" version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a54268eaa72876dadae10fc48b66b888d9db5620c6052ce236fbcf2442362b" dependencies = [ "itertools 0.13.0", "proc-macro2", @@ -9006,9 +9378,9 @@ dependencies = [ "dashmap 6.1.0", "dirs 5.0.1", "fd-lock", - "forc-pkg", + "forc-pkg 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "futures", "lsp-types", "parking_lot", @@ -9021,15 +9393,15 @@ dependencies = [ "regex", "serde", "serde_json", - "sway-ast", - "sway-core", - "sway-error", - "sway-features", + "sway-ast 0.70.3", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", "sway-lsp-test-utils", - "sway-parse", - "sway-types", - "sway-utils", - "swayfmt", + "sway-parse 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", + "swayfmt 0.70.3", "syn 2.0.106", "tempfile", "thiserror 1.0.69", @@ -9067,10 +9439,29 @@ dependencies = [ "num-bigint", "num-traits", "phf", - "sway-ast", - "sway-error", - "sway-features", - "sway-types", + "sway-ast 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-types 0.70.3", + "thiserror 1.0.69", + "unicode-bidi", + "unicode-xid", +] + +[[package]] +name = "sway-parse" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bddc3120cce9b374a12c092ae46883320c6cb2b4516ba32d6bba94f4eba3a84" +dependencies = [ + "extension-trait", + "num-bigint", + "num-traits", + "phf", + "sway-ast 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", "unicode-bidi", "unicode-xid", @@ -9090,7 +9481,28 @@ dependencies = [ "parking_lot", "rustc-hash 1.1.0", "serde", - "sway-utils", + "sway-utils 0.70.3", + "thiserror 1.0.69", + "toml 0.8.23", +] + +[[package]] +name = "sway-types" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a02e6cba19844a10aac0339f0c10862df992dd9d1eff3395e3a4f7d33b0fc886" +dependencies = [ + "fuel-asm 0.65.0", + "fuel-crypto 0.65.0", + "fuel-tx 0.65.0", + "indexmap 2.11.4", + "lazy_static", + "num-bigint", + "num-traits", + "parking_lot", + "rustc-hash 1.1.0", + "serde", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", "toml 0.8.23", ] @@ -9103,6 +9515,16 @@ dependencies = [ "walkdir", ] +[[package]] +name = "sway-utils" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4908c677beb52c2405b32b6516180da944993bef6ab95828d754ffef9e688701" +dependencies = [ + "serde", + "walkdir", +] + [[package]] name = "swayfmt" version = "0.70.3" @@ -9116,17 +9538,39 @@ dependencies = [ "serde", "serde_ignored", "similar", - "sway-ast", - "sway-error", - "sway-features", - "sway-parse", - "sway-types", - "sway-utils", + "sway-ast 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-parse 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", "test-macros", "thiserror 1.0.69", "toml 0.8.23", ] +[[package]] +name = "swayfmt" +version = "0.70.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3585a036c2cdf86e835414493be6d4d755747105dfd647c28f1c67584504dc" +dependencies = [ + "anyhow", + "forc-tracing 0.71.1", + "indoc", + "ropey", + "serde", + "serde_ignored", + "sway-ast 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-error 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-features 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-parse 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-types 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sway-utils 0.70.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.69", + "toml 0.8.23", +] + [[package]] name = "syn" version = "1.0.109" @@ -9218,6 +9662,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "sysinfo" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -9296,7 +9754,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.52.0", + "windows-sys 0.61.1", ] [[package]] @@ -9363,11 +9821,11 @@ dependencies = [ "colored", "duct", "filecheck", - "forc", - "forc-pkg", - "forc-test", + "forc 0.70.3", + "forc-pkg 0.70.3", + "forc-test 0.70.3", "forc-tracing 0.71.1", - "forc-util", + "forc-util 0.70.3", "fuel-vm 0.65.0", "fuels 0.76.0", "fuels-accounts 0.76.0", @@ -9387,12 +9845,12 @@ dependencies = [ "rhai", "serde", "serde_json", - "sway-core", - "sway-error", - "sway-features", - "sway-ir", - "sway-types", - "sway-utils", + "sway-core 0.70.3", + "sway-error 0.70.3", + "sway-features 0.70.3", + "sway-ir 0.70.3", + "sway-types 0.70.3", + "sway-utils 0.70.3", "textwrap", "tokio", "toml 0.8.23", @@ -9586,22 +10044,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2 0.6.0", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] @@ -9616,9 +10071,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -9871,6 +10326,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.17", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.31" @@ -10433,7 +10900,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.1", ] [[package]] @@ -10442,19 +10909,52 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.1", + "windows-interface 0.59.2", "windows-link 0.2.0", "windows-result 0.4.0", "windows-strings 0.5.0", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "windows-implement" version = "0.60.1" @@ -10466,6 +10966,17 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "windows-interface" version = "0.59.2" @@ -10500,6 +11011,15 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.3.4" diff --git a/Cargo.toml b/Cargo.toml index e6298543584..87b7496871b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ forc-util = { path = "forc-util/", version = "0.70.3" } # Forc plugins forc-plugins = { path = "forc-plugins/", version = "0.70.3" } -forc-client = { path = "forc-plugins/forc-client/", version = "0.70.3" } +forc-client = "0.71" forc-debug = { path = "forc-plugins/forc-debug/", version = "0.70.3" } forc-doc = { path = "forc-plugins/forc-doc/", version = "0.70.3" } forc-fmt = { path = "forc-plugins/forc-fmt/", version = "0.70.3" } @@ -115,8 +115,6 @@ anyhow = "1.0" assert-json-diff = "2.0" assert_matches = "1.5" async-trait = "0.1" -aws-config = "1.5" -aws-sdk-kms = "1.44" axum = "0.8" byte-unit = "5.1" bytes = "1.7" @@ -170,7 +168,6 @@ indoc = "2.0" insta = "1.40" ipfs-api-backend-hyper = "0.6" itertools = "0.13" -k256 = "0.13" lazy_static = "1.4" libc = "0.2" libtest-mimic = "0.7" diff --git a/forc-plugins/forc-client/Cargo.toml b/forc-plugins/forc-client/Cargo.toml deleted file mode 100644 index 7326ffa624d..00000000000 --- a/forc-plugins/forc-client/Cargo.toml +++ /dev/null @@ -1,92 +0,0 @@ -[package] -name = "forc-client" -version.workspace = true -description = "A `forc` plugin for interacting with a Fuel node." -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true - -[lib] -path = "src/lib.rs" - -[[bin]] -name = "forc-deploy" -path = "src/bin/deploy.rs" - -[[bin]] -name = "forc-run" -path = "src/bin/run.rs" - -[[bin]] -name = "forc-submit" -path = "src/bin/submit.rs" - -[[bin]] -name = "forc-call" -path = "src/bin/call.rs" - -[dependencies] -ansiterm.workspace = true -anyhow.workspace = true -async-trait.workspace = true -aws-config.workspace = true -aws-sdk-kms.workspace = true -chrono = { workspace = true, features = ["std"] } -clap = { workspace = true, features = ["derive", "env"] } -devault.workspace = true -dialoguer.workspace = true -either.workspace = true -forc.workspace = true -forc-debug.workspace = true -forc-pkg.workspace = true -forc-tracing.workspace = true -forc-tx.workspace = true -forc-util = { workspace = true, features = ["tx"] } -forc-wallet.workspace = true -fuel-abi-types.workspace = true -fuel-core-client = { workspace = true, features = ["subscriptions"] } -fuel-core-storage = { workspace = true } -fuel-core-types.workspace = true -fuel-crypto.workspace = true -fuel-tx = { workspace = true, features = ["test-helpers"] } -fuel-vm.workspace = true -fuels.workspace = true -fuels-accounts.workspace = true -fuels-core.workspace = true -futures.workspace = true -hex.workspace = true -k256.workspace = true -rand.workspace = true -regex.workspace = true -reqwest = { workspace = true } -rpassword.workspace = true -serde.workspace = true -serde_json.workspace = true -sway-ast.workspace = true -sway-core.workspace = true -sway-features.workspace = true -sway-parse.workspace = true -sway-types.workspace = true -sway-utils.workspace = true -tempfile.workspace = true -tokio = { workspace = true, features = [ - "macros", - "process", - "rt-multi-thread", -] } -toml_edit.workspace = true -tracing.workspace = true -url.workspace = true - -[build-dependencies] -regex.workspace = true - -[dev-dependencies] -portpicker.workspace = true -pretty_assertions.workspace = true -rexpect.workspace = true - -[lints] -workspace = true diff --git a/forc-plugins/forc-client/build.rs b/forc-plugins/forc-client/build.rs deleted file mode 100644 index cb19fa43d8d..00000000000 --- a/forc-plugins/forc-client/build.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::fs; -use std::path::{Path, PathBuf}; - -fn minify_json(json: &str) -> String { - let mut result = String::with_capacity(json.len()); - let mut in_string = false; - let mut previous_char: Option = None; - - for c in json.chars() { - if in_string { - result.push(c); - if c == '"' && previous_char != Some('\\') { - in_string = false; - } - } else { - match c { - '"' => { - result.push(c); - in_string = true; - } - ' ' | '\n' | '\r' | '\t' => continue, // Skip whitespace - _ => result.push(c), - } - } - previous_char = Some(c); - } - result -} - -fn update_proxy_abi_decl_with_file(source_file_path: &Path, minified_json: &str) { - // Read the contents of the source file - let mut source_code = fs::read_to_string(source_file_path).expect("Unable to read source file"); - - // Prepare the replacement string for the `abigen!` macro - let escaped_json = minified_json.replace('\\', "\\\\").replace('"', "\\\""); - let new_abigen = - format!("abigen!(Contract(name = \"ProxyContract\", abi = \"{escaped_json}\",));"); - - // Use a regular expression to find and replace the `abigen!` macro - let re = regex::Regex::new(r#"abigen!\(Contract\(name = "ProxyContract", abi = ".*?",\)\);"#) - .expect("Invalid regex pattern"); - - // Replace the existing `abigen!` macro with the new one containing the updated ABI - if re.is_match(&source_code) { - source_code = re.replace(&source_code, new_abigen.as_str()).to_string(); - } else { - panic!("abigen! macro not found in the source file"); - } - - // Write the modified source code back to the source file - fs::write(source_file_path, source_code).expect("Unable to write back to the source file"); -} - -fn main() { - // Path to the JSON file in the root directory next to the `src` folder - let json_path = PathBuf::from("proxy_abi/proxy_contract-abi.json"); - // Read the contents of the JSON file - let json_content = - fs::read_to_string(json_path).expect("Unable to read proxy_contract-abi.json"); - - // Minify the JSON content - let minified_json = minify_json(&json_content); - - // If proxy_contract-abi.json is changed, re-run this script - println!("cargo:rerun-if-changed=proxy_abi/proxy_contract-abi.json"); - - // Path to the Rust source file that contains the `abigen!` macro that - // creates a `ProxyContract`. - let util_tx_path = PathBuf::from("src/util/tx.rs"); - update_proxy_abi_decl_with_file(&util_tx_path, &minified_json); - - let test_path = PathBuf::from("tests/deploy.rs"); - update_proxy_abi_decl_with_file(&test_path, &minified_json); - - let deploy_path = PathBuf::from("src/op/deploy.rs"); - update_proxy_abi_decl_with_file(&deploy_path, &minified_json); -} diff --git a/forc-plugins/forc-client/proxy_abi/README.md b/forc-plugins/forc-client/proxy_abi/README.md deleted file mode 100644 index 684e8ceff17..00000000000 --- a/forc-plugins/forc-client/proxy_abi/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Proxy Contract - -This folder contains pre-built version of the owned proxy contract, its abi and `storage-slots.json` file. - -*contract url*: [sway-standard-implementation/src-14/owned_proxy](https://github.com/FuelLabs/sway-standard-implementations/tree/174f5ed9c79c23a6aaf5db906fe27ecdb29c22eb). -*commit hash*: `174f5ed9c79c23a6aaf5db906fe27ecdb29c22eb` -*forc version*: `v0.63.6` -*build command*: `forc build --release` diff --git a/forc-plugins/forc-client/proxy_abi/proxy_contract-abi.json b/forc-plugins/forc-client/proxy_abi/proxy_contract-abi.json deleted file mode 100644 index 10134964f90..00000000000 --- a/forc-plugins/forc-client/proxy_abi/proxy_contract-abi.json +++ /dev/null @@ -1,724 +0,0 @@ -{ - "programType": "contract", - "specVersion": "1.1", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "()", - "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "type": "enum standards::src5::AccessError", - "concreteTypeId": "3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d", - "metadataTypeId": 1 - }, - { - "type": "enum standards::src5::State", - "concreteTypeId": "192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c", - "metadataTypeId": 2 - }, - { - "type": "enum std::option::Option", - "concreteTypeId": "0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8", - "metadataTypeId": 4, - "typeArguments": [ - "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" - ] - }, - { - "type": "enum sway_libs::ownership::errors::InitializationError", - "concreteTypeId": "1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893", - "metadataTypeId": 5 - }, - { - "type": "enum sway_libs::upgradability::errors::SetProxyOwnerError", - "concreteTypeId": "3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74", - "metadataTypeId": 6 - }, - { - "type": "str", - "concreteTypeId": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a" - }, - { - "type": "struct std::contract_id::ContractId", - "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", - "metadataTypeId": 9 - }, - { - "type": "struct sway_libs::upgradability::events::ProxyOwnerSet", - "concreteTypeId": "96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247", - "metadataTypeId": 10 - }, - { - "type": "struct sway_libs::upgradability::events::ProxyTargetSet", - "concreteTypeId": "1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8", - "metadataTypeId": 11 - } - ], - "metadataTypes": [ - { - "type": "b256", - "metadataTypeId": 0 - }, - { - "type": "enum standards::src5::AccessError", - "metadataTypeId": 1, - "components": [ - { - "name": "NotOwner", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - } - ] - }, - { - "type": "enum standards::src5::State", - "metadataTypeId": 2, - "components": [ - { - "name": "Uninitialized", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "name": "Initialized", - "typeId": 3 - }, - { - "name": "Revoked", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - } - ] - }, - { - "type": "enum std::identity::Identity", - "metadataTypeId": 3, - "components": [ - { - "name": "Address", - "typeId": 8 - }, - { - "name": "ContractId", - "typeId": 9 - } - ] - }, - { - "type": "enum std::option::Option", - "metadataTypeId": 4, - "components": [ - { - "name": "None", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "name": "Some", - "typeId": 7 - } - ], - "typeParameters": [ - 7 - ] - }, - { - "type": "enum sway_libs::ownership::errors::InitializationError", - "metadataTypeId": 5, - "components": [ - { - "name": "CannotReinitialized", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - } - ] - }, - { - "type": "enum sway_libs::upgradability::errors::SetProxyOwnerError", - "metadataTypeId": 6, - "components": [ - { - "name": "CannotUninitialize", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - } - ] - }, - { - "type": "generic T", - "metadataTypeId": 7 - }, - { - "type": "struct std::address::Address", - "metadataTypeId": 8, - "components": [ - { - "name": "bits", - "typeId": 0 - } - ] - }, - { - "type": "struct std::contract_id::ContractId", - "metadataTypeId": 9, - "components": [ - { - "name": "bits", - "typeId": 0 - } - ] - }, - { - "type": "struct sway_libs::upgradability::events::ProxyOwnerSet", - "metadataTypeId": 10, - "components": [ - { - "name": "new_proxy_owner", - "typeId": 2 - } - ] - }, - { - "type": "struct sway_libs::upgradability::events::ProxyTargetSet", - "metadataTypeId": 11, - "components": [ - { - "name": "new_target", - "typeId": 9 - } - ] - } - ], - "functions": [ - { - "inputs": [], - "name": "proxy_target", - "output": "0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8", - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Returns the target contract of the proxy contract." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Returns" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * [Option] - The new proxy contract to which all fallback calls will be passed or `None`." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Number of Storage Accesses" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Reads: `1`" - ] - }, - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [ - { - "name": "new_target", - "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" - } - ], - "name": "set_proxy_target", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Change the target contract of the proxy contract." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Additional Information" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " This method can only be called by the `proxy_owner`." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Arguments" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * `new_target`: [ContractId] - The new proxy contract to which all fallback calls will be passed." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Reverts" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * When not called by `proxy_owner`." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Number of Storage Accesses" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Reads: `1`" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Write: `1`" - ] - }, - { - "name": "storage", - "arguments": [ - "read", - "write" - ] - } - ] - }, - { - "inputs": [], - "name": "proxy_owner", - "output": "192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c", - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Returns the owner of the proxy contract." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Returns" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * [State] - Represents the state of ownership for this contract." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Number of Storage Accesses" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Reads: `1`" - ] - }, - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [], - "name": "initialize_proxy", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Initializes the proxy contract." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Additional Information" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " This method sets the storage values using the values of the configurable constants `INITIAL_TARGET` and `INITIAL_OWNER`." - ] - }, - { - "name": "doc-comment", - "arguments": [ - " This then allows methods that write to storage to be called." - ] - }, - { - "name": "doc-comment", - "arguments": [ - " This method can only be called once." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Reverts" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * When `storage::SRC14.proxy_owner` is not [State::Uninitialized]." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Number of Storage Accesses" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Writes: `2`" - ] - }, - { - "name": "storage", - "arguments": [ - "write" - ] - } - ] - }, - { - "inputs": [ - { - "name": "new_proxy_owner", - "concreteTypeId": "192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c" - } - ], - "name": "set_proxy_owner", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Changes proxy ownership to the passed State." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Additional Information" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " This method can be used to transfer ownership between Identities or to revoke ownership." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Arguments" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * `new_proxy_owner`: [State] - The new state of the proxy ownership." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Reverts" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * When the sender is not the current proxy owner." - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * When the new state of the proxy ownership is [State::Uninitialized]." - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " # Number of Storage Accesses" - ] - }, - { - "name": "doc-comment", - "arguments": [ - "" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Reads: `1`" - ] - }, - { - "name": "doc-comment", - "arguments": [ - " * Writes: `1`" - ] - }, - { - "name": "storage", - "arguments": [ - "write" - ] - } - ] - } - ], - "loggedTypes": [ - { - "logId": "4571204900286667806", - "concreteTypeId": "3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d" - }, - { - "logId": "2151606668983994881", - "concreteTypeId": "1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8" - }, - { - "logId": "2161305517876418151", - "concreteTypeId": "1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893" - }, - { - "logId": "4354576968059844266", - "concreteTypeId": "3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74" - }, - { - "logId": "10870989709723147660", - "concreteTypeId": "96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247" - }, - { - "logId": "10098701174489624218", - "concreteTypeId": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a" - } - ], - "messagesTypes": [], - "configurables": [ - { - "name": "INITIAL_TARGET", - "concreteTypeId": "0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8", - "offset": 13368 - }, - { - "name": "INITIAL_OWNER", - "concreteTypeId": "192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c", - "offset": 13320 - } - ] -} \ No newline at end of file diff --git a/forc-plugins/forc-client/proxy_abi/proxy_contract-storage_slots.json b/forc-plugins/forc-client/proxy_abi/proxy_contract-storage_slots.json deleted file mode 100644 index 72849c97055..00000000000 --- a/forc-plugins/forc-client/proxy_abi/proxy_contract-storage_slots.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "key": "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "key": "bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - } -] \ No newline at end of file diff --git a/forc-plugins/forc-client/proxy_abi/proxy_contract.bin b/forc-plugins/forc-client/proxy_abi/proxy_contract.bin deleted file mode 100644 index 9ce27196250..00000000000 Binary files a/forc-plugins/forc-client/proxy_abi/proxy_contract.bin and /dev/null differ diff --git a/forc-plugins/forc-client/src/bin/call.rs b/forc-plugins/forc-client/src/bin/call.rs deleted file mode 100644 index 724e90b404b..00000000000 --- a/forc-plugins/forc-client/src/bin/call.rs +++ /dev/null @@ -1,27 +0,0 @@ -use clap::Parser; -use forc_tracing::{init_tracing_subscriber, println_error, TracingSubscriberOptions}; - -#[tokio::main] -async fn main() { - let command = forc_client::cmd::Call::parse(); - - // Initialize tracing with verbosity from command - init_tracing_subscriber(TracingSubscriberOptions { - verbosity: Some(command.verbosity), - writer_mode: Some(command.output.clone().into()), - regex_filter: Some("forc_tracing".to_string()), - ..Default::default() - }); - - let operation = match command.validate_and_get_operation() { - Ok(operation) => operation, - Err(err) => { - println_error(&err); - std::process::exit(1); - } - }; - if let Err(err) = forc_client::op::call(operation, command).await { - println_error(&format!("{err}")); - std::process::exit(1); - } -} diff --git a/forc-plugins/forc-client/src/bin/deploy.rs b/forc-plugins/forc-client/src/bin/deploy.rs deleted file mode 100644 index 09fa473e98e..00000000000 --- a/forc-plugins/forc-client/src/bin/deploy.rs +++ /dev/null @@ -1,12 +0,0 @@ -use clap::Parser; -use forc_tracing::{init_tracing_subscriber, println_error}; - -#[tokio::main] -async fn main() { - init_tracing_subscriber(Default::default()); - let command = forc_client::cmd::Deploy::parse(); - if let Err(err) = forc_client::op::deploy(command).await { - println_error(&format!("{err}")); - std::process::exit(1); - } -} diff --git a/forc-plugins/forc-client/src/bin/run.rs b/forc-plugins/forc-client/src/bin/run.rs deleted file mode 100644 index 2f4367a30d5..00000000000 --- a/forc-plugins/forc-client/src/bin/run.rs +++ /dev/null @@ -1,12 +0,0 @@ -use clap::Parser; -use forc_tracing::{init_tracing_subscriber, println_error}; - -#[tokio::main] -async fn main() { - init_tracing_subscriber(Default::default()); - let command = forc_client::cmd::Run::parse(); - if let Err(err) = forc_client::op::run(command).await { - println_error(&format!("{err}")); - std::process::exit(1); - } -} diff --git a/forc-plugins/forc-client/src/bin/submit.rs b/forc-plugins/forc-client/src/bin/submit.rs deleted file mode 100644 index 460c8bb492e..00000000000 --- a/forc-plugins/forc-client/src/bin/submit.rs +++ /dev/null @@ -1,12 +0,0 @@ -use clap::Parser; -use forc_tracing::{init_tracing_subscriber, println_error}; - -#[tokio::main] -async fn main() { - init_tracing_subscriber(Default::default()); - let command = forc_client::cmd::Submit::parse(); - if let Err(err) = forc_client::op::submit(command).await { - println_error(&format!("{err}")); - std::process::exit(1); - } -} diff --git a/forc-plugins/forc-client/src/cmd/call.rs b/forc-plugins/forc-client/src/cmd/call.rs deleted file mode 100644 index d317d827234..00000000000 --- a/forc-plugins/forc-client/src/cmd/call.rs +++ /dev/null @@ -1,532 +0,0 @@ -use crate::NodeTarget; -use clap::{Parser, ValueEnum}; -use fuel_crypto::SecretKey; -use fuels::programs::calls::CallParameters; -use fuels_core::types::{Address, AssetId, ContractId}; -use std::{io::Write, path::PathBuf, str::FromStr}; -use url::Url; - -pub use forc::cli::shared::{BuildOutput, BuildProfile, Minify, Pkg, Print}; -pub use forc_tx::{Gas, Maturity}; - -#[derive(Debug, Clone)] -pub enum FuncType { - Selector(String), - // TODO: add support for function signatures - without ABI files - // ↳ gh issue: https://github.com/FuelLabs/sway/issues/6886 - // Signature(String), -} - -impl Default for FuncType { - fn default() -> Self { - FuncType::Selector(String::new()) - } -} - -impl FromStr for FuncType { - type Err = String; - fn from_str(s: &str) -> Result { - let s = s.trim().replace(' ', ""); - if s.is_empty() { - return Err("Function signature cannot be empty".to_string()); - } - Ok(FuncType::Selector(s.to_string())) - } -} - -/// Execution mode for contract calls -#[derive(Debug, Clone, PartialEq, Default, ValueEnum)] -#[clap(rename_all = "kebab-case")] -pub enum ExecutionMode { - /// Execute a dry run - no state changes, no gas fees, wallet is not used or validated - #[default] - DryRun, - /// Execute in simulation mode - no state changes, estimates gas, wallet is used but not validated - Simulate, - /// Execute live on chain - state changes, gas fees apply, wallet is used and validated - Live, -} - -/// Output format for call results -#[derive(Debug, Clone, PartialEq, Default, ValueEnum)] -#[clap(rename_all = "lowercase")] -pub enum OutputFormat { - /// Default formatted output - #[default] - Default, - /// Raw unformatted output - Raw, - /// JSON output with full tracing information (logs, errors, and result) - Json, -} - -impl Write for OutputFormat { - fn write(&mut self, buf: &[u8]) -> Result { - match self { - OutputFormat::Default => std::io::stdout().write(buf), - OutputFormat::Raw => std::io::stdout().write(buf), - OutputFormat::Json => Ok(buf.len()), // no-op for json - } - } - - fn flush(&mut self) -> Result<(), std::io::Error> { - match self { - OutputFormat::Default => std::io::stdout().flush(), - OutputFormat::Raw => std::io::stdout().flush(), - OutputFormat::Json => Ok(()), - } - } -} - -impl From for forc_tracing::TracingWriter { - fn from(format: OutputFormat) -> Self { - match format { - OutputFormat::Json => forc_tracing::TracingWriter::Json, - _ => forc_tracing::TracingWriter::Stdio, - } - } -} - -/// Flags for specifying the caller account -#[derive(Debug, Default, Clone, Parser, serde::Deserialize, serde::Serialize)] -pub struct Caller { - /// Derive an account from a secret key to make the call - #[clap(long, env = "SIGNING_KEY", help_heading = "ACCOUNT OPTIONS")] - pub signing_key: Option, - - /// Use forc-wallet to make the call - #[clap(long, default_value = "false", help_heading = "ACCOUNT OPTIONS")] - pub wallet: bool, -} - -/// Options for contract call parameters -#[derive(Debug, Default, Clone, Parser)] -pub struct CallParametersOpts { - /// Amount of native assets to forward with the call - #[clap( - long, - default_value = "0", - alias = "value", - help_heading = "CALL PARAMETERS" - )] - pub amount: u64, - - /// Asset ID to forward with the call - #[clap(long, help_heading = "CALL PARAMETERS")] - pub asset_id: Option, - - /// Amount of gas to forward with the call - #[clap(long, help_heading = "CALL PARAMETERS")] - pub gas_forwarded: Option, -} - -impl From for CallParameters { - fn from(opts: CallParametersOpts) -> Self { - let mut params = CallParameters::default(); - if opts.amount != 0 { - params = params.with_amount(opts.amount); - } - if let Some(asset_id) = opts.asset_id { - params = params.with_asset_id(asset_id); - } - if let Some(gas) = opts.gas_forwarded { - params = params.with_gas_forwarded(gas); - } - params - } -} - -/// Operation for the call command -#[derive(Debug, Clone, PartialEq)] -pub enum AbiSource { - /// ABI from file path - File(PathBuf), - /// ABI from URL - Url(Url), - /// ABI as raw string - String(String), -} - -impl std::fmt::Display for AbiSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AbiSource::File(path) => write!(f, "{}", path.display()), - AbiSource::Url(url) => write!(f, "{url}"), - AbiSource::String(s) => write!(f, "{s}"), - } - } -} - -impl TryFrom for AbiSource { - type Error = String; - - fn try_from(s: String) -> Result { - // First try to parse as URL - if let Ok(url) = Url::parse(&s) { - match url.scheme() { - "http" | "https" | "ipfs" => return Ok(AbiSource::Url(url)), - _ => {} // Continue to check other options - } - } - - // Check if it looks like a JSON string (starts with '{' or '[') - let trimmed = s.trim(); - if (trimmed.starts_with('{') && trimmed.ends_with('}')) - || (trimmed.starts_with('[') && trimmed.ends_with(']')) - { - return Ok(AbiSource::String(s)); - } - - // Default to treating as file path - Ok(AbiSource::File(PathBuf::from(s))) - } -} - -#[derive(Debug, Clone)] -pub enum Operation { - /// Call a specific contract function - CallFunction { - contract_id: ContractId, - abi: AbiSource, - function: FuncType, - function_args: Vec, - }, - /// List all functions in the contract - ListFunctions { - contract_id: ContractId, - abi: AbiSource, - }, - /// Direct transfer of assets to a contract - DirectTransfer { - recipient: Address, - amount: u64, - asset_id: Option, - }, -} - -/// Perform Fuel RPC calls from the comfort of your command line. -#[derive(Debug, Parser, Clone)] -#[clap(bin_name = "forc call", version)] -#[clap(after_help = r#" -## EXAMPLES: - -### Call a contract with function parameters -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - get_balance 0x0087675439e10a8351b1d5e4cf9d0ea6da77675623ff6b16470b5e3c58998423 -``` - -### Call a contract with function parameters; additionally print logs, receipts and script json -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - get_balance 0x0087675439e10a8351b1d5e4cf9d0ea6da77675623ff6b16470b5e3c58998423 \ - -vv -``` - -### Call a contract with address labels for better trace readability -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \ - --label 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97:MainContract \ - --label 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:TokenContract \ - -vv -``` - -### Call a contract without function parameters -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - get_name -``` - -### Call a contract that makes external contract calls -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \ - --contracts 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 -``` - -### Call a contract with additional contract ABIs for better tracing -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \ - --contract-abi 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:./external-abi.json \ - --contract-abi 0x1234:https://example.com/abi.json -``` - -### Call a contract in simulation mode -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - add 1 2 \ - --mode simulate -``` - -### Call a contract in dry-run mode on custom node URL using explicit signing-key -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --node-url "http://127.0.0.1:4000/v1/graphql" \ - --signing-key 0x... \ - --abi ./contract-abi.json \ - add 1 2 \ - --mode dry-run -``` - -### Call a contract in live mode which performs state changes on testnet using forc-wallet -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --testnet \ - --wallet \ - --abi ./contract-abi.json \ - add 1 2 \ - --mode live -``` - -### Call a contract payable function which transfers value of native asset on mainnet -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \ - --mode live \ - --amount 100 -``` - -### Call a contract payable function which transfers value of custom asset -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \ - --amount 100 \ - --asset-id 0x0087675439e10a8351b1d5e4cf9d0ea6da77675623ff6b16470b5e3c58998423 \ - --live -``` - -### List all available functions in a contract -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - --list-functions -``` - -### Call a contract with inline ABI JSON string -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi '{"functions":[{"inputs":[],"name":"get_balance","output":{"name":"","type":"u64","typeArguments":null}}]}' \ - get_balance -``` - -### Direct transfer of asset to a contract or address -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --amount 100 \ - --mode live -``` - -### Call a contract with interactive debugger after transaction -```sh -forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \ - --abi ./contract-abi.json \ - get_balance 0x0087675439e10a8351b1d5e4cf9d0ea6da77675623ff6b16470b5e3c58998423 \ - --debug -``` -"#)] -pub struct Command { - /// The contract ID to call - #[clap(help_heading = "CONTRACT")] - pub address: Address, - - /// Path, URI, or raw JSON string for the ABI - /// Required when making function calls or listing functions - /// Can be a file path, HTTP/HTTPS URL, or raw JSON string - #[clap(long, value_parser = |s: &str| AbiSource::try_from(s.to_string()))] - pub abi: Option, - - /// Additional contract IDs and their ABI paths for better tracing and debugging. - /// Format: contract_id:abi_path (can be used multiple times) - /// Example: --contract-abi 0x123:./abi1.json --contract-abi 0x456:https://example.com/abi2.json - /// Contract IDs can be provided with or without 0x prefix - #[clap(long = "contract-abi", value_parser = parse_contract_abi, action = clap::ArgAction::Append, help_heading = "CONTRACT")] - pub contract_abis: Option>, - - /// Label addresses in the trace output for better readability. - /// Format: address:label (can be used multiple times) - /// Example: --label 0x123:MainContract --label 0x456:TokenContract - /// Addresses can be provided with or without 0x prefix - #[clap(long, value_parser = parse_label, action = clap::ArgAction::Append, help_heading = "OUTPUT")] - pub label: Option>, - - /// The function selector to call. - /// The function selector is the name of the function to call (e.g. "transfer"). - /// Not required when --list-functions is specified or when --amount is provided for direct transfer - #[clap(help_heading = "FUNCTION")] - pub function: Option, - - /// Arguments to pass to the function - #[clap(help_heading = "FUNCTION")] - pub function_args: Vec, - - /// Network connection options - #[clap(flatten)] - pub node: NodeTarget, - - /// Caller account options - #[clap(flatten)] - pub caller: Caller, - - /// Call parameters - #[clap(flatten)] - pub call_parameters: CallParametersOpts, - - /// Execution mode - determines if state changes are applied - /// - `dry-run`: No state changes, no gas fees, wallet is not used or validated - /// - `simulate`: No state changes, estimates gas, wallet is used but not validated - /// - `live`: State changes, gas fees apply, wallet is used and validated - #[clap(long, default_value = "dry-run", help_heading = "EXECUTION")] - pub mode: ExecutionMode, - - /// List all available functions in the contract - #[clap( - long, - alias = "list-functions", - conflicts_with = "function", - help_heading = "OPERATION" - )] - pub list_functions: bool, - - /// The gas price to use for the call; defaults to 0 - #[clap(flatten)] - pub gas: Option, - - /// The external contract addresses to use for the call - /// If none are provided, the call will automatically populate external contracts by making dry-run calls - /// to the node, and extract the contract addresses based on the revert reason. - /// Use an empty string '' to explicitly specify no external contracts. - /// Multiple contract IDs can be provided separated by commas. - #[clap( - long, - alias = "contracts", - value_delimiter = ',', - help_heading = "CONTRACT" - )] - pub external_contracts: Option>, - - /// Output format for the call result - #[clap(long, short = 'o', default_value = "default", help_heading = "OUTPUT")] - pub output: OutputFormat, - - /// Contract call variable output count - #[clap(long, alias = "variable-output", help_heading = "VARIABLE OUTPUT")] - pub variable_output: Option, - - /// Set verbosity levels; currently only supports max 2 levels - /// - `-v=1`: Print decoded logs - /// - `-v=2`: Additionally print receipts and script json - #[clap(short = 'v', action = clap::ArgAction::Count, help_heading = "OUTPUT")] - pub verbosity: u8, - - /// Start interactive debugger after transaction execution - #[clap(long, short = 'd', help_heading = "DEBUG")] - pub debug: bool, -} - -impl Command { - /// Validate the command and determine the CLI operation - pub fn validate_and_get_operation(&self) -> Result { - // Case 1: List functions - if self.list_functions { - let Some(abi) = &self.abi else { - return Err("ABI is required when using --list-functions".to_string()); - }; - return Ok(Operation::ListFunctions { - contract_id: (*self.address).into(), - abi: abi.to_owned(), - }); - } - - // Case 2: Direct transfer with amount - if self.function.is_none() && self.call_parameters.amount > 0 { - if self.mode != ExecutionMode::Live { - return Err("Direct transfers are only supported in live mode".to_string()); - } - return Ok(Operation::DirectTransfer { - recipient: self.address, - amount: self.call_parameters.amount, - asset_id: self.call_parameters.asset_id, - }); - } - - // Case 3: Call function - if let Some(function) = &self.function { - let Some(abi) = &self.abi else { - return Err("ABI is required when calling a function".to_string()); - }; - let func_type = FuncType::from_str(function)?; - return Ok(Operation::CallFunction { - contract_id: (*self.address).into(), - abi: abi.to_owned(), - function: func_type, - function_args: self.function_args.to_owned(), - }); - } - - // No valid operation matched - Err("Either function selector, --list-functions flag, or non-zero --amount for direct transfers must be provided".to_string()) - } -} - -fn parse_contract_abi(s: &str) -> Result<(ContractId, AbiSource), String> { - let parts: Vec<&str> = s.trim().split(':').collect(); - let [contract_id_str, abi_path_str] = parts.try_into().map_err(|_| { - format!("Invalid contract ABI format: '{s}'. Expected format: contract_id:abi_path") - })?; - - let contract_id = - ContractId::from_str(&format!("0x{}", contract_id_str.trim_start_matches("0x"))) - .map_err(|e| format!("Invalid contract ID '{contract_id_str}': {e}"))?; - - let abi_path = AbiSource::try_from(abi_path_str.to_string()) - .map_err(|e| format!("Invalid ABI path '{abi_path_str}': {e}"))?; - - Ok((contract_id, abi_path)) -} - -fn parse_label(s: &str) -> Result<(ContractId, String), String> { - let parts: Vec<&str> = s.trim().split(':').collect(); - let [contract_id_str, label] = parts - .try_into() - .map_err(|_| format!("Invalid label format: '{s}'. Expected format: contract_id:label"))?; - - let contract_id = - ContractId::from_str(&format!("0x{}", contract_id_str.trim_start_matches("0x"))) - .map_err(|e| format!("Invalid contract ID '{contract_id_str}': {e}"))?; - - Ok((contract_id, label.to_string())) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_abi_source_try_from() { - let url_result = AbiSource::try_from("https://example.com/abi.json".to_string()).unwrap(); - assert!(matches!(url_result, AbiSource::Url(_))); - - let json_result = AbiSource::try_from(r#"{"functions": []}"#.to_string()).unwrap(); - assert!(matches!(json_result, AbiSource::String(_))); - - let array_result = AbiSource::try_from("[]".to_string()).unwrap(); - assert!(matches!(array_result, AbiSource::String(_))); - - let file_result = AbiSource::try_from("./contract-abi.json".to_string()).unwrap(); - assert!(matches!(file_result, AbiSource::File(_))); - - let file_url_result = AbiSource::try_from("file:///path/to/abi.json".to_string()).unwrap(); - assert!(matches!(file_url_result, AbiSource::File(_))); - } -} diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs deleted file mode 100644 index 8796531f317..00000000000 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::NodeTarget; -use clap::Parser; -use forc::cli::shared::IrCliOpt; -pub use forc::cli::shared::{BuildOutput, Minify, Pkg, Print}; -use forc_pkg::BuildProfile; -pub use forc_tx::{Gas, Maturity}; -pub use forc_util::tx_utils::Salt; -use fuel_crypto::SecretKey; - -forc_util::cli_examples! { - super::Command { - [ Deploy a single contract => "forc deploy bc09bfa7a11a04ce42b0a5abf04fd437387ee49bf4561d575177e2946468b408" ] - [ Deploy a single contract from a different path => "forc deploy bc09bfa7a11a04ce42b0a5abf04fd437387ee49bf4561d575177e2946468b408 --path {path}" ] - [ Deploy to a custom network => "forc deploy --node-url https://testnet.fuel.network/graphql" ] - } -} - -#[derive(Debug, Default, Parser)] -#[clap(bin_name = "forc deploy", version, after_help = help())] -pub struct Command { - #[clap(flatten)] - pub pkg: Pkg, - #[clap(flatten)] - pub minify: Minify, - #[clap(flatten)] - pub print: Print, - #[arg(long, value_parser = clap::builder::PossibleValuesParser::new(IrCliOpt::cli_options()))] - pub verify_ir: Option>, - #[clap(flatten)] - pub gas: Gas, - #[clap(flatten)] - pub maturity: Maturity, - #[clap(flatten)] - pub node: NodeTarget, - /// Optional 256-bit hexadecimal literal(s) to redeploy contracts. - /// - /// For a single contract, use `--salt `, eg.: forc deploy --salt 0x0000000000000000000000000000000000000000000000000000000000000001 - /// - /// For a workspace with multiple contracts, use `--salt :` - /// to specify a salt for each contract, eg.: - /// - /// forc deploy --salt contract_a:0x0000000000000000000000000000000000000000000000000000000000000001 - /// --salt contract_b:0x0000000000000000000000000000000000000000000000000000000000000002 - #[clap(long)] - pub salt: Option>, - /// Generate a default salt (0x0000000000000000000000000000000000000000000000000000000000000000) for the contract. - /// Useful for CI, to create reproducible deployments. - #[clap(long)] - pub default_salt: bool, - #[clap(flatten)] - pub build_output: BuildOutput, - /// The name of the build profile to use. - #[clap(long, default_value = BuildProfile::RELEASE)] - pub build_profile: String, - /// Sign the transaction with default signer that is pre-funded by fuel-core. Useful for testing against local node. - #[clap(long)] - pub default_signer: bool, - /// Deprecated in favor of `--default-signer`. - #[clap(long)] - pub unsigned: bool, - /// Submit the deployment transaction(s) without waiting for execution to complete. - #[clap(long)] - pub submit_only: bool, - /// Set the key to be used for signing. - pub signing_key: Option, - /// Sign the deployment transaction manually. - #[clap(long)] - pub manual_signing: bool, - /// Override storage slot initialization. - /// - /// By default, storage slots are initialized with the values defined in the storage block in - /// the contract. You can override the initialization by providing the file path to a JSON file - /// containing the overridden values. - /// - /// The file format and key values should match the compiler-generated `*-storage_slots.json` file in the output - /// directory of the compiled contract. - /// - /// Example: `forc deploy --override-storage-slots my_override.json` - /// - /// my_override.json: - /// [ - /// { - /// "key": "", - /// "value": "0000000000000000000000000000000000000000000000000000000000000001" - /// } - /// ] - #[clap(long, verbatim_doc_comment, name = "JSON_FILE_PATH")] - pub override_storage_slots: Option, - - #[clap(flatten)] - pub experimental: sway_features::CliFields, - - /// AWS KMS signer arn. If present forc-deploy will automatically use AWS KMS signer instead of forc-wallet. - #[clap(long)] - pub aws_kms_signer: Option, -} diff --git a/forc-plugins/forc-client/src/cmd/mod.rs b/forc-plugins/forc-client/src/cmd/mod.rs deleted file mode 100644 index 76c4667f970..00000000000 --- a/forc-plugins/forc-client/src/cmd/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod call; -pub mod deploy; -pub mod run; -pub mod submit; - -pub use call::Command as Call; -pub use deploy::Command as Deploy; -pub use run::Command as Run; -pub use submit::Command as Submit; diff --git a/forc-plugins/forc-client/src/cmd/run.rs b/forc-plugins/forc-client/src/cmd/run.rs deleted file mode 100644 index 564cffa7b17..00000000000 --- a/forc-plugins/forc-client/src/cmd/run.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::NodeTarget; -use clap::Parser; -use forc::cli::shared::IrCliOpt; -use fuel_crypto::SecretKey; - -pub use super::submit::Network; -pub use forc::cli::shared::{BuildOutput, BuildProfile, Minify, Pkg, Print}; -pub use forc_tx::{Gas, Maturity}; - -/// Run script project. -/// Crafts a script transaction then sends it to a running node. -#[derive(Debug, Default, Parser)] -#[clap(bin_name = "forc run", version)] -pub struct Command { - #[clap(flatten)] - pub pkg: Pkg, - #[clap(flatten)] - pub minify: Minify, - #[arg(long, value_parser = clap::builder::PossibleValuesParser::new(IrCliOpt::cli_options()))] - pub verify_ir: Option>, - #[clap(flatten)] - pub print: Print, - #[clap(flatten)] - pub gas: Gas, - #[clap(flatten)] - pub maturity: Maturity, - #[clap(flatten)] - pub build_output: BuildOutput, - #[clap(flatten)] - pub build_profile: BuildProfile, - #[clap(flatten)] - pub node: NodeTarget, - /// Hex string of data to input to script. - #[clap(short, long)] - pub data: Option, - /// Only craft transaction and print it out. - #[clap(long)] - pub dry_run: bool, - /// Pretty-print the outputs from the node. - #[clap(long = "pretty-print", short = 'r')] - pub pretty_print: bool, - /// 32-byte contract ID that will be called during the transaction. - #[clap(long = "contract")] - pub contract: Option>, - /// Execute the transaction and return the final mutated transaction along with receipts - /// (which includes whether the transaction reverted or not). The transaction is not inserted - /// in the node's view of the blockchain, (i.e. it does not affect the chain state). - #[clap(long)] - pub simulate: bool, - /// Sign the transaction with default signer that is pre-funded by fuel-core. Useful for testing against local node. - #[clap(long)] - pub default_signer: bool, - /// Deprecated in favor of `--default-signer`. - #[clap(long)] - pub unsigned: bool, - /// Set the key to be used for signing. - pub signing_key: Option, - /// Arguments to pass into main function with forc run. - #[clap(long)] - pub args: Option>, - /// Start interactive debugger after transaction execution - #[clap(long, help_heading = "DEBUG")] - pub debug: bool, - - #[clap(flatten)] - pub experimental: sway_features::CliFields, -} diff --git a/forc-plugins/forc-client/src/cmd/submit.rs b/forc-plugins/forc-client/src/cmd/submit.rs deleted file mode 100644 index 4251d78f2a2..00000000000 --- a/forc-plugins/forc-client/src/cmd/submit.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::NodeTarget; -use devault::Devault; -use std::path::PathBuf; - -forc_util::cli_examples! { - super::Command { - [ Submit a transaction from a json file => "forc submit {path}/mint.json" ] - [ Submit a transaction from a json file and wait for confirmation => "forc submit {path}/mint.json --await true" ] - [ Submit a transaction from a json file and get output in json => "forc submit {path}/mint.json --tx-status-json true" ] - [ Submit a transaction from a json file to testnet => "forc submit {path}/mint.json --testnet" ] - [ Submit a transaction from a json file to a local net => "forc submit {path}/mint.json --target local" ] - } -} - -/// Submit a transaction to the specified fuel node. -#[derive(Debug, Default, clap::Parser)] -#[clap(about, version, after_help = help())] -pub struct Command { - #[clap(flatten)] - pub network: Network, - #[clap(flatten)] - pub tx_status: TxStatus, - /// Path to the Transaction that is to be submitted to the Fuel node. - /// - /// Paths to files ending with `.json` will be deserialized from JSON. - /// Paths to files ending with `.bin` will be deserialized from bytes - /// using the `fuel_tx::Transaction::try_from_bytes` constructor. - pub tx_path: PathBuf, -} - -/// Options related to networking. -#[derive(Debug, Devault, clap::Args)] -pub struct Network { - #[clap(flatten)] - pub node: NodeTarget, - /// Whether or not to await confirmation that the transaction has been committed. - /// - /// When `true`, await commitment and output the transaction status. - /// When `false`, do not await confirmation and simply output the transaction ID. - #[clap(long = "await", default_value = "true", action(clap::ArgAction::Set))] - #[devault("true")] - pub await_: bool, -} - -/// Options related to the transaction status. -#[derive(Debug, Default, clap::Args)] -pub struct TxStatus { - /// Output the resulting transaction status as JSON rather than the default output. - #[clap( - long = "tx-status-json", - default_value = "false", - action(clap::ArgAction::Set) - )] - pub json: bool, -} diff --git a/forc-plugins/forc-client/src/constants.rs b/forc-plugins/forc-client/src/constants.rs deleted file mode 100644 index 8470c9f4362..00000000000 --- a/forc-plugins/forc-client/src/constants.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Default to localhost to favour the common case of testing. -pub const NODE_URL: &str = sway_utils::constants::DEFAULT_NODE_URL; - -pub const MAINNET_ENDPOINT_URL: &str = "https://mainnet.fuel.network"; -pub const TESTNET_ENDPOINT_URL: &str = "https://testnet.fuel.network"; -pub const DEVNET_ENDPOINT_URL: &str = "https://devnet.fuel.network"; - -pub const TESTNET_FAUCET_URL: &str = "https://faucet-testnet.fuel.network"; -pub const DEVNET_FAUCET_URL: &str = "https://faucet-devnet.fuel.network"; - -pub const MAINNET_EXPLORER_URL: &str = "https://app.fuel.network"; -pub const TESTNET_EXPLORER_URL: &str = "https://app-testnet.fuel.network"; - -/// Default PrivateKey to sign transactions submitted to local node. -pub const DEFAULT_PRIVATE_KEY: &str = - "0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c"; -/// The maximum time to wait for a transaction to be included in a block by the node -pub const TX_SUBMIT_TIMEOUT_MS: u64 = 30_000u64; diff --git a/forc-plugins/forc-client/src/lib.rs b/forc-plugins/forc-client/src/lib.rs deleted file mode 100644 index 31b35a468f9..00000000000 --- a/forc-plugins/forc-client/src/lib.rs +++ /dev/null @@ -1,268 +0,0 @@ -pub mod cmd; -pub mod constants; -pub mod op; -pub mod util; - -use clap::Parser; -use forc_pkg::manifest::Network; -use serde::{Deserialize, Serialize}; -use util::target::Target; - -/// Flags for specifying the node to target. -#[derive(Debug, Default, Clone, Parser, Deserialize, Serialize)] -pub struct NodeTarget { - /// The URL of the Fuel node to which we're submitting the transaction. - /// If unspecified, checks the manifest's `network` table, then falls back - /// to `http://127.0.0.1:4000` - /// - /// You can also use `--target`, `--devnet`, `--testnet`, or `--mainnet` to specify the Fuel node. - #[clap(long, env = "FUEL_NODE_URL")] - pub node_url: Option, - - /// Preset configurations for using a specific target. - /// - /// You can also use `--node-url`, `--devnet`, `--testnet`, or `--mainnet` to specify the Fuel node. - /// - /// Possible values are: [local, testnet, mainnet] - #[clap(long)] - pub target: Option, - - /// Use preset configuration for mainnet. - /// - /// You can also use `--node-url`, `--target`, or `--testnet` to specify the Fuel node. - #[clap(long)] - pub mainnet: bool, - - /// Use preset configuration for testnet. - /// - /// You can also use `--node-url`, `--target`, or `--mainnet` to specify the Fuel node. - #[clap(long)] - pub testnet: bool, - - /// Use preset configuration for devnet. - /// - /// You can also use `--node-url`, `--target`, or `--testnet` to specify the Fuel node. - #[clap(long)] - pub devnet: bool, -} - -impl NodeTarget { - /// Returns the URL to use for connecting to Fuel Core node. - pub fn get_node_url(&self, manifest_network: &Option) -> anyhow::Result { - let options_count = [ - self.mainnet, - self.testnet, - self.devnet, - self.target.is_some(), - self.node_url.is_some(), - ] - .iter() - .filter(|&&x| x) - .count(); - - // ensure at most one option is specified - if options_count > 1 { - anyhow::bail!("Only one of `--mainnet`, `--testnet`, `--devnet`, `--target`, or `--node-url` should be specified"); - } - - let node_url = match () { - _ if self.mainnet => Target::mainnet().target_url(), - _ if self.testnet => Target::testnet().target_url(), - _ if self.devnet => Target::devnet().target_url(), - _ if self.target.is_some() => self.target.as_ref().unwrap().target_url(), - _ if self.node_url.is_some() => self.node_url.as_ref().unwrap().clone(), - _ => manifest_network - .as_ref() - .map(|nw| &nw.url[..]) - .unwrap_or(crate::constants::NODE_URL) - .to_string(), - }; - - Ok(node_url) - } - - /// Returns the URL for explorer - pub fn get_explorer_url(&self) -> Option { - match ( - self.testnet, - self.mainnet, - self.devnet, - self.target.clone(), - self.node_url.clone(), - ) { - (true, false, _, None, None) => Target::testnet().explorer_url(), - (false, true, _, None, None) => Target::mainnet().explorer_url(), - (false, false, _, Some(target), None) => target.explorer_url(), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_explorer_url_mainnet() { - let node = NodeTarget { - target: Some(Target::Mainnet), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - let actual = node.get_explorer_url().unwrap(); - assert_eq!("https://app.fuel.network", actual); - } - - #[test] - fn test_get_explorer_url_testnet() { - let node = NodeTarget { - target: Some(Target::Testnet), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - let actual = node.get_explorer_url().unwrap(); - assert_eq!("https://app-testnet.fuel.network", actual); - } - - #[test] - fn test_get_explorer_url_devnet() { - let node = NodeTarget { - target: Some(Target::Devnet), - node_url: None, - mainnet: false, - testnet: false, - devnet: true, - }; - let actual = node.get_explorer_url(); - assert_eq!(None, actual); - } - - #[test] - fn test_get_explorer_url_local() { - let node = NodeTarget { - target: Some(Target::Local), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - let actual = node.get_explorer_url(); - assert_eq!(None, actual); - } - - #[test] - fn test_get_node_url_testnet() { - let node = NodeTarget { - target: None, - node_url: None, - mainnet: false, - testnet: true, - devnet: false, - }; - - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("https://testnet.fuel.network", actual); - } - - #[test] - fn test_get_node_url_mainnet() { - let node = NodeTarget { - target: None, - node_url: None, - mainnet: true, - testnet: false, - devnet: false, - }; - - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("https://mainnet.fuel.network", actual); - } - - #[test] - fn test_get_node_url_target_mainnet() { - let node = NodeTarget { - target: Some(Target::Mainnet), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("https://mainnet.fuel.network", actual); - } - - #[test] - fn test_get_node_url_target_testnet() { - let node = NodeTarget { - target: Some(Target::Testnet), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("https://testnet.fuel.network", actual); - } - - #[test] - fn test_get_node_url_default() { - let node = NodeTarget { - target: None, - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("http://127.0.0.1:4000", actual); - } - - #[test] - fn test_get_node_url_local() { - let node = NodeTarget { - target: Some(Target::Local), - node_url: None, - mainnet: false, - testnet: false, - devnet: false, - }; - let actual = node.get_node_url(&None).unwrap(); - assert_eq!("http://127.0.0.1:4000", actual); - } - - #[test] - #[should_panic( - expected = "Only one of `--mainnet`, `--testnet`, `--devnet`, `--target`, or `--node-url` should be specified" - )] - fn test_get_node_url_local_testnet() { - let node = NodeTarget { - target: Some(Target::Local), - node_url: None, - mainnet: false, - testnet: true, - devnet: false, - }; - node.get_node_url(&None).unwrap(); - } - - #[test] - #[should_panic( - expected = "Only one of `--mainnet`, `--testnet`, `--devnet`, `--target`, or `--node-url` should be specified" - )] - fn test_get_node_url_same_url() { - let node = NodeTarget { - target: Some(Target::Testnet), - node_url: Some("testnet.fuel.network".to_string()), - mainnet: false, - testnet: false, - devnet: false, - }; - node.get_node_url(&None).unwrap(); - } -} diff --git a/forc-plugins/forc-client/src/op/call/call_function.rs b/forc-plugins/forc-client/src/op/call/call_function.rs deleted file mode 100644 index d650403040f..00000000000 --- a/forc-plugins/forc-client/src/op/call/call_function.rs +++ /dev/null @@ -1,1067 +0,0 @@ -use crate::{ - cmd::{self, call::FuncType}, - op::call::{ - missing_contracts::determine_missing_contracts, - parser::{param_type_val_to_token, token_to_string}, - CallResponse, - }, -}; -use anyhow::{anyhow, bail, Result}; -use fuel_abi_types::abi::unified_program::UnifiedProgramABI; -use fuel_core_client::client::types::TransactionStatus; -use fuel_core_types::services::executor::{TransactionExecutionResult, TransactionExecutionStatus}; -use fuels::{ - accounts::ViewOnlyAccount, - client::FuelClient, - programs::calls::{ - receipt_parser::ReceiptParser, - traits::{ContractDependencyConfigurator, TransactionTuner}, - ContractCall, - }, - types::transaction::Transaction, -}; -use fuels_core::{ - codec::{ - encode_fn_selector, log_formatters_lookup, ABIDecoder, ABIEncoder, DecoderConfig, - EncoderConfig, ErrorDetails, LogDecoder, - }, - types::{ - param_types::ParamType, - transaction_builders::{BuildableTransaction, ScriptBuildStrategy, VariableOutputPolicy}, - ContractId, - }, -}; -use std::{collections::HashMap, str::FromStr, sync::Arc}; - -/// Calls a contract function with the given parameters -pub async fn call_function( - contract_id: ContractId, - abi: crate::cmd::call::AbiSource, - function: FuncType, - function_args: Vec, - cmd: cmd::Call, -) -> Result { - let cmd::Call { - node, - mode, - caller, - call_parameters, - gas, - mut output, - external_contracts, - contract_abis, - variable_output, - .. - } = cmd; - - // Use the reusable function to create ABI map - let abi_map = super::create_abi_map(contract_id, &abi, contract_abis).await?; - - // Get the main ABI for compatibility with existing code - let abi = abi_map - .get(&contract_id) - .ok_or_else(|| anyhow!("Main contract ABI not found in abi_map"))?; - - let cmd::call::FuncType::Selector(selector) = function; - - let (encoded_data, output_param) = - prepare_contract_call_data(&abi.unified, &selector, &function_args)?; - - // Setup connection to node - let (wallet, tx_policies, base_asset_id) = super::setup_connection(&node, caller, &gas).await?; - let call_parameters = cmd::call::CallParametersOpts { - asset_id: call_parameters.asset_id.or(Some(base_asset_id)), - ..call_parameters - }; - - // Create the contract call - let call = ContractCall { - contract_id, - encoded_selector: encode_fn_selector(&selector), - encoded_args: Ok(encoded_data), - call_parameters: call_parameters.clone().into(), - external_contracts: vec![], // set below - output_param: output_param.clone(), - is_payable: call_parameters.amount > 0, - custom_assets: Default::default(), - inputs: Vec::new(), - outputs: Vec::new(), - }; - - // Setup variable output policy and log decoder - let variable_output_policy = variable_output - .map(VariableOutputPolicy::Exactly) - .unwrap_or(VariableOutputPolicy::EstimateMinimum); - let error_codes = abi - .unified - .error_codes - .as_ref() - .map_or(HashMap::new(), |error_codes| { - error_codes - .iter() - .map(|(revert_code, error_details)| { - ( - *revert_code, - ErrorDetails::new( - error_details.pos.pkg.clone(), - error_details.pos.file.clone(), - error_details.pos.line, - error_details.pos.column, - error_details.log_id.clone(), - error_details.msg.clone(), - ), - ) - }) - .collect() - }); - let log_decoder = LogDecoder::new(log_formatters_lookup(vec![], contract_id), error_codes); - - // Execute the call based on execution mode - let client = FuelClient::new(wallet.provider().url()) - .map_err(|e| anyhow!("Failed to create client: {e}"))?; - let consensus_params = wallet.provider().consensus_parameters().await?; - let chain_id = consensus_params.chain_id(); - - // Get external contracts (either provided or auto-detected) - let external_contracts = match external_contracts { - Some(contracts) if contracts.first().is_some_and(|s| s.is_empty()) => vec![], - Some(contracts) => { - // Parse each contract ID - contracts - .into_iter() - .filter(|s| !s.is_empty()) - .map(|s| { - ContractId::from_str(s.strip_prefix("0x").unwrap_or(&s)) - .map_err(|e| anyhow!("Invalid contract ID '{}': {}", s, e)) - }) - .collect::>>()? - } - None => { - // Automatically retrieve missing contract addresses from the call - forc_tracing::println_warning( - "Automatically retrieving missing contract addresses for the call", - ); - let external_contracts = determine_missing_contracts( - &call, - wallet.provider(), - &tx_policies, - &variable_output_policy, - &consensus_params, - &log_decoder, - &wallet, - ) - .await?; - if !external_contracts.is_empty() { - forc_tracing::println_warning( - "Automatically provided external contract addresses with call (max 10):", - ); - external_contracts.iter().for_each(|addr| { - forc_tracing::println_warning(&format!("- 0x{addr}")); - }); - } - external_contracts - } - }; - - let tb = call - .clone() - .with_external_contracts(external_contracts) - .transaction_builder( - tx_policies, - variable_output_policy, - &consensus_params, - call.inputs.clone(), - &wallet, - ) - .map_err(|e| anyhow!("Failed to initialize transaction builder: {e}"))?; - - #[cfg_attr(test, allow(unused_variables))] - let (tx, tx_execution, storage_reads) = match mode { - cmd::call::ExecutionMode::DryRun => { - let tx = call - .build_tx(tb, &wallet) - .await - .map_err(|e| anyhow!("Failed to build transaction: {e}"))?; - let (tx_execs, storage_reads) = client - .dry_run_opt_record_storage_reads(&[tx.clone().into()], None, None, None) - .await - .map_err(|e| anyhow!("Failed to dry run transaction: {e}"))?; - let tx_exec = tx_execs - .first() - .ok_or(anyhow!( - "Failed to extract transaction from dry run execution" - ))? - .to_owned(); - (tx, tx_exec, storage_reads) - } - cmd::call::ExecutionMode::Simulate => { - let tb = tb.with_build_strategy(ScriptBuildStrategy::StateReadOnly); - let tx = call - .build_tx(tb, &wallet) - .await - .map_err(|e| anyhow!("Failed to build transaction: {e}"))?; - let gas_price = gas.map(|g| g.price).unwrap_or(Some(0)); - let (tx_execs, storage_reads) = client - .dry_run_opt_record_storage_reads(&[tx.clone().into()], None, gas_price, None) - .await - .map_err(|e| anyhow!("Failed to dry run transaction: {e}"))?; - let tx_exec = tx_execs - .first() - .ok_or(anyhow!( - "Failed to extract transaction from dry run execution" - ))? - .to_owned(); - (tx, tx_exec, storage_reads) - } - cmd::call::ExecutionMode::Live => { - forc_tracing::println_action_green( - "Sending transaction with wallet", - &format!("0x{}", wallet.address()), - ); - let tx = call - .build_tx(tb, &wallet) - .await - .map_err(|e| anyhow!("Failed to build transaction: {e}"))?; - let tx_status = client.submit_and_await_commit(&tx.clone().into()).await?; - - #[cfg_attr(test, allow(unused_variables))] - let (block_height, tx_exec) = match tx_status { - TransactionStatus::Success { - block_height, - program_state, - receipts, - total_gas, - total_fee, - .. - } => ( - block_height, - TransactionExecutionStatus { - id: tx.id(chain_id), - result: TransactionExecutionResult::Success { - result: program_state, - receipts: Arc::new(receipts), - total_gas, - total_fee, - }, - }, - ), - TransactionStatus::Failure { - total_gas, - total_fee, - program_state, - receipts, - block_height, - .. - } => ( - block_height, - TransactionExecutionStatus { - id: tx.id(chain_id), - result: TransactionExecutionResult::Failed { - result: program_state, - receipts: Arc::new(receipts), - total_gas, - total_fee, - }, - }, - ), - _ => bail!("Transaction status not found"), - }; - - #[cfg(not(test))] - let storage_reads = client - .storage_read_replay(&block_height) - .await - .map_err(|e| anyhow!("Failed to get storage reads: {e}"))?; - - #[cfg(test)] - let storage_reads = vec![]; - - (tx, tx_exec, storage_reads) - } - }; - - let tx: fuel_tx::Transaction = tx.into(); - let fuel_tx::Transaction::Script(script) = &tx else { - bail!("Transaction is not a script"); - }; - let receipts = tx_execution.result.receipts(); - - // Generate execution trace events by stepping through VM interpreter - #[cfg(test)] - let trace_events: Vec = vec![]; - #[cfg(not(test))] - let trace_events = { - use crate::op::call::trace::interpret_execution_trace; - interpret_execution_trace( - wallet.provider(), - &mode, - &consensus_params, - script, - receipts, - storage_reads, - &abi_map, - ) - .await - .map_err(|e| anyhow!("Failed to generate execution trace: {e}"))? - }; - - // display detailed call info if verbosity is set - if cmd.verbosity > 0 { - // Convert labels from Vec to HashMap - let labels: HashMap = cmd - .label - .as_ref() - .map(|labels| labels.iter().cloned().collect()) - .unwrap_or_default(); - - super::display_detailed_call_info( - &tx_execution, - script, - &abi_map, - cmd.verbosity, - &mut output, - &trace_events, - &labels, - )?; - } - - // If the call reverted, exit early; return an error with the revert details - if let Some((contract_id, revert_info)) = - crate::op::call::trace::first_revert_info(&trace_events) - { - return Err(anyhow!( - "Contract 0x{contract_id} reverted with code 0x{:x}", - revert_info.revert_code - )); - } - - // Parse the result based on output format - let mut receipt_parser = ReceiptParser::new(receipts, DecoderConfig::default()); - let result = match output { - cmd::call::OutputFormat::Default | cmd::call::OutputFormat::Json => { - let data = receipt_parser - .extract_contract_call_data(contract_id) - .ok_or(anyhow!("Failed to extract contract call data"))?; - ABIDecoder::default() - .decode_as_debug_str(&output_param, data.as_slice()) - .map_err(|e| anyhow!("Failed to decode as debug string: {e}"))? - } - cmd::call::OutputFormat::Raw => { - let token = receipt_parser - .parse_call(contract_id, &output_param) - .map_err(|e| anyhow!("Failed to parse call data: {e}"))?; - token_to_string(&token) - .map_err(|e| anyhow!("Failed to convert token to string: {e}"))? - } - }; - - // display tx info - super::display_tx_info( - tx_execution.id.to_string(), - Some(result.clone()), - &mode, - &node, - ); - - // Start interactive debugger if requested - if cmd.debug { - start_debug_session(&client, &tx, abi).await?; - } - - Ok(CallResponse { - tx_hash: tx_execution.id.to_string(), - result: Some(result), - total_gas: *tx_execution.result.total_gas(), - receipts: tx_execution.result.receipts().to_vec(), - script: Some(script.to_owned()), - trace_events, - }) -} - -fn prepare_contract_call_data( - unified_program_abi: &UnifiedProgramABI, - selector: &str, - function_args: &[String], -) -> Result<(Vec, ParamType)> { - let type_lookup = unified_program_abi - .types - .iter() - .map(|decl| (decl.type_id, decl.clone())) - .collect::>(); - - // Find the function in the ABI - let abi_func = unified_program_abi - .functions - .iter() - .find(|f| f.name == selector) - .cloned() - .ok_or_else(|| anyhow!("Function '{selector}' not found in ABI"))?; - - // Validate number of arguments - if abi_func.inputs.len() != function_args.len() { - bail!( - "Argument count mismatch for '{selector}': expected {}, got {}", - abi_func.inputs.len(), - function_args.len() - ); - } - - // Parse function arguments to tokens - let tokens = abi_func - .inputs - .iter() - .zip(function_args) - .map(|(type_application, arg)| { - let param_type = - ParamType::try_from_type_application(type_application, &type_lookup) - .map_err(|e| anyhow!("Failed to convert input type application: {e}"))?; - param_type_val_to_token(¶m_type, arg) - }) - .collect::>>()?; - - // Get output parameter type - let output_param = ParamType::try_from_type_application(&abi_func.output, &type_lookup) - .map_err(|e| anyhow!("Failed to convert output type: {e}"))?; - - // Encode function arguments - let abi_encoder = ABIEncoder::new(EncoderConfig::default()); - let encoded_data = abi_encoder - .encode(&tokens) - .map_err(|e| anyhow!("Failed to encode function arguments: {e}"))?; - - Ok((encoded_data, output_param)) -} - -/// Starts an interactive debugging session with the given transaction and ABI -async fn start_debug_session( - fuel_client: &FuelClient, - tx: &fuel_tx::Transaction, - abi: &super::Abi, -) -> Result<()> { - // Create debugger instance from the existing fuel client - let mut debugger = forc_debug::debugger::Debugger::from_client(fuel_client.clone()) - .await - .map_err(|e| anyhow!("Failed to create debugger: {e}"))?; - - // Create temporary files for transaction and ABI (auto-cleaned when dropped) - let mut tx_file = tempfile::Builder::new() - .suffix(".json") - .tempfile() - .map_err(|e| anyhow!("Failed to create temp transaction file: {e}"))?; - serde_json::to_writer_pretty(&mut tx_file, tx) - .map_err(|e| anyhow!("Failed to write transaction to temp file: {e}"))?; - - let mut abi_file = tempfile::Builder::new() - .suffix(".json") - .tempfile() - .map_err(|e| anyhow!("Failed to create temp ABI file: {e}"))?; - serde_json::to_writer_pretty(&mut abi_file, &abi.program) - .map_err(|e| anyhow!("Failed to write ABI to temp file: {e}"))?; - - // Prepare the start_tx command string for the CLI - let tx_cmd = format!( - "start_tx {} {}", - tx_file.path().to_string_lossy(), - abi_file.path().to_string_lossy() - ); - - // Start the interactive CLI session with the prepared command - let mut cli = forc_debug::cli::Cli::new() - .map_err(|e| anyhow!("Failed to create debug CLI interface: {e}"))?; - cli.run(&mut debugger, Some(tx_cmd)) - .await - .map_err(|e| anyhow!("Interactive debugging session failed: {e}"))?; - - Ok(()) -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::{ - cmd, - op::call::{call, get_wallet, PrivateKeySigner}, - }; - use fuel_tx::field::Outputs; - use fuels::{crypto::SecretKey, prelude::*}; - use std::path::PathBuf; - - fn get_contract_call_cmd( - id: ContractId, - node_url: &str, - secret_key: SecretKey, - selector: &str, - args: Vec<&str>, - ) -> cmd::Call { - cmd::Call { - address: (*id).into(), - abi: Some(cmd::call::AbiSource::File(PathBuf::from( - "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json", - ))), - function: Some(selector.to_string()), - function_args: args.into_iter().map(String::from).collect(), - node: crate::NodeTarget { node_url: Some(node_url.to_string()), ..Default::default() }, - caller: cmd::call::Caller { signing_key: Some(secret_key), wallet: false }, - call_parameters: Default::default(), - mode: cmd::call::ExecutionMode::DryRun, - gas: None, - external_contracts: None, - contract_abis: None, - label: None, - output: cmd::call::OutputFormat::Raw, - list_functions: false, - variable_output: None, - verbosity: 0, - debug: false, - } - } - - abigen!(Contract( - name = "TestContract", - abi = "forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json" - )); - - pub async fn get_contract_instance() -> (TestContract, ContractId, Provider, SecretKey) - { - let secret_key = SecretKey::random(&mut rand::thread_rng()); - let signer = PrivateKeySigner::new(secret_key); - let coins = setup_single_asset_coins(signer.address(), AssetId::zeroed(), 1, 1_000_000); - let provider = setup_test_provider(coins, vec![], None, None) - .await - .unwrap(); - let wallet = get_wallet(Some(secret_key), false, provider.clone()) - .await - .unwrap(); - - let id = Contract::load_from( - "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types.bin", - LoadConfiguration::default(), - ) - .unwrap() - .deploy(&wallet, TxPolicies::default()) - .await - .unwrap() - .contract_id; - - let instance = TestContract::new(id, wallet.clone()); - - (instance, id, provider, secret_key) - } - - #[tokio::test] - async fn contract_call_with_abi() { - let (_, id, provider, secret_key) = get_contract_instance().await; - let node_url = provider.url(); - - // test_empty_no_return - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_empty_no_return", vec![]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "()"); - - // test_empty - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_empty", vec![]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "()"); - - // test_unit - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_unit", vec!["()"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "()"); - - // test_u8 - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_u8", vec!["255"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "255"); - - // test_u16 - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_u16", vec!["65535"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "65535"); - - // test_u32 - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_u32", vec!["4294967295"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "4294967295" - ); - - // test_u64 - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_u64", - vec!["18446744073709551615"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "18446744073709551615" - ); - - // test_u128 - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_u128", - vec!["340282366920938463463374607431768211455"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "340282366920938463463374607431768211455" - ); - - // test_u256 - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_u256", - vec!["115792089237316195423570985008687907853269984665640564039457584007913129639935"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "115792089237316195423570985008687907853269984665640564039457584007913129639935" - ); - - // test b256 - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_b256", - vec!["0000000000000000000000000000000000000000000000000000000000000042"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "0x0000000000000000000000000000000000000000000000000000000000000042" - ); - - // test_b256 - fails if 0x prefix provided since it extracts input as an external contract; we don't want to do this so explicitly provide the external contract as empty - let mut cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_b256", - vec!["0x0000000000000000000000000000000000000000000000000000000000000042"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - cmd.external_contracts = Some(vec![]); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "0x0000000000000000000000000000000000000000000000000000000000000042" - ); - - // test_bytes - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_bytes", vec!["0x42"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "0x42"); - - // test bytes without 0x prefix - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_bytes", vec!["42"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "0x42"); - - // test_str - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_str", vec!["fuel"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "fuel"); - - // test str array - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_str_array", - vec!["fuel rocks"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "fuel rocks" - ); - - // test str array - fails if length mismatch - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_str_array", vec!["fuel"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "string array length mismatch: expected 10, got 4" - ); - - // test str slice - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_str_slice", - vec!["fuel rocks 42"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "fuel rocks 42" - ); - - // test tuple - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_tuple", vec!["(42, true)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(42, true)" - ); - - // test array - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_array", - vec!["[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]" - ); - - // test_array - fails if different types - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_array", vec!["[42, true]"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "failed to parse u64 value: true" - ); - - // test_array - succeeds if length not matched!? - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_array", vec!["[42, 42]"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert!(call(operation, cmd) - .await - .unwrap() - .result - .unwrap() - .starts_with("[42, 42, 0,")); - - // test_vector - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_vector", vec!["[42, 42]"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "[42, 42]" - ); - - // test_vector - fails if different types - let cmd = - get_contract_call_cmd(id, node_url, secret_key, "test_vector", vec!["[42, true]"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "failed to parse u64 value: true" - ); - - // test_struct - Identity { name: str[2], id: u64 } - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_struct", vec!["{fu, 42}"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "{fu, 42}" - ); - - // test_struct - fails if incorrect inner attribute length - let cmd = - get_contract_call_cmd(id, node_url, secret_key, "test_struct", vec!["{fuel, 42}"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "string array length mismatch: expected 2, got 4" - ); - - // test_struct - succeeds if missing inner final attribute; default value is used - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_struct", vec!["{fu}"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "{fu, 0}" - ); - - // test_struct - succeeds to use default values for all attributes if missing - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_struct", vec!["{}"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "{\0\0, 0}" - ); - - // test_enum - let cmd = - get_contract_call_cmd(id, node_url, secret_key, "test_enum", vec!["(Active:true)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(Active:true)" - ); - - // test_enum - succeeds if using index - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_enum", vec!["(1:56)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(Pending:56)" - ); - - // test_enum - fails if variant not found - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_enum", vec!["(A:true)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "failed to find index of variant: A" - ); - - // test_enum - fails if variant value incorrect - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_enum", vec!["(Active:3)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "failed to parse `Active` variant enum value: 3" - ); - - // test_enum - fails if variant value is missing - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_enum", vec!["(Active:)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "enum must have exactly two parts `(variant:value)`: (Active:)" - ); - - // test_option - encoded like an enum - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_option", vec!["(0:())"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(None:())" - ); - - // test_option - encoded like an enum; none value ignored - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_option", vec!["(0:42)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(None:())" - ); - - // test_option - encoded like an enum; some value - let cmd = get_contract_call_cmd(id, node_url, secret_key, "test_option", vec!["(1:42)"]); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(Some:42)" - ); - } - - #[tokio::test] - async fn contract_call_with_abi_complex() { - let (_, id, provider, secret_key) = get_contract_instance().await; - let node_url = provider.url(); - - // test_complex_struct - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_struct_with_generic", - vec!["{42, fuel}"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "{42, fuel}" - ); - - // test_enum_with_generic - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_enum_with_generic", - vec!["(value:32)"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(value:32)" - ); - - // test_enum_with_complex_generic - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_enum_with_complex_generic", - vec!["(value:{42, fuel})"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(value:{42, fuel})" - ); - - let cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "test_enum_with_complex_generic", - vec!["(container:{{42, fuel}, fuel})"], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap().result.unwrap(), - "(container:{{42, fuel}, fuel})" - ); - } - - #[tokio::test] - async fn contract_value_forwarding() { - let (_, id, provider, secret_key) = get_contract_instance().await; - let node_url = provider.url(); - - let consensus_parameters = provider.consensus_parameters().await.unwrap(); - let base_asset_id = consensus_parameters.base_asset_id(); - let get_recipient_balance = |addr: Address, provider: Provider| async move { - provider - .get_asset_balance(&addr, base_asset_id) - .await - .unwrap() - }; - let get_contract_balance = |id: ContractId, provider: Provider| async move { - provider - .get_contract_asset_balance(&id, base_asset_id) - .await - .unwrap() - }; - - // contract call transfer funds to another contract - let (_, id_2, _, _) = get_contract_instance().await; - let (amount, asset_id, recipient) = ( - "1", - &format!("{{0x{base_asset_id}}}"), - &format!("(ContractId:{{0x{id_2}}})"), - ); - let mut cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "transfer", - vec![amount, asset_id, recipient], - ); - let operation = cmd.validate_and_get_operation().unwrap(); - cmd.call_parameters = cmd::call::CallParametersOpts { - amount: amount.parse::().unwrap(), - asset_id: Some(*base_asset_id), - gas_forwarded: None, - }; - // validate balance is unchanged (dry-run) - let call_response = call(operation.clone(), cmd.clone()).await.unwrap(); - assert_eq!(call_response.result.unwrap(), "()"); - assert_eq!(call_response.script.unwrap().outputs().len(), 2); - cmd.mode = cmd::call::ExecutionMode::Live; - assert_eq!(call(operation, cmd).await.unwrap().result.unwrap(), "()"); - assert_eq!(get_contract_balance(id_2, provider.clone()).await, 1); - assert_eq!(get_contract_balance(id, provider.clone()).await, 1); - - // contract call transfer funds to another address - let random_wallet = Wallet::random(&mut rand::thread_rng(), provider.clone()); - let (amount, asset_id, recipient) = ( - "2", - &format!("{{0x{base_asset_id}}}"), - &format!("(Address:{{0x{}}})", random_wallet.address()), - ); - let mut cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "transfer", - vec![amount, asset_id, recipient], - ); - cmd.call_parameters = cmd::call::CallParametersOpts { - amount: amount.parse::().unwrap(), - asset_id: Some(*base_asset_id), - gas_forwarded: None, - }; - cmd.mode = cmd::call::ExecutionMode::Live; - let operation = cmd.validate_and_get_operation().unwrap(); - let call_response = call(operation, cmd).await.unwrap(); - assert_eq!(call_response.result.unwrap(), "()"); - assert_eq!(call_response.script.unwrap().outputs().len(), 3); - assert_eq!( - get_recipient_balance(random_wallet.address(), provider.clone()).await, - 2 - ); - assert_eq!(get_contract_balance(id, provider.clone()).await, 1); - - // contract call transfer funds to another address - // specify amount x, provide amount x - 1 - // fails with panic reason 'NotEnoughBalance' - let random_wallet = Wallet::random(&mut rand::thread_rng(), provider.clone()); - let (amount, asset_id, recipient) = ( - "5", - &format!("{{0x{base_asset_id}}}"), - &format!("(Address:{{0x{}}})", random_wallet.address()), - ); - let mut cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "transfer", - vec![amount, asset_id, recipient], - ); - cmd.call_parameters = cmd::call::CallParametersOpts { - amount: amount.parse::().unwrap() - 3, - asset_id: Some(*base_asset_id), - gas_forwarded: None, - }; - cmd.mode = cmd::call::ExecutionMode::Live; - let operation = cmd.validate_and_get_operation().unwrap(); - assert_eq!( - call(operation, cmd).await.unwrap_err().to_string(), - "Failed to parse call data: codec: `ReceiptDecoder`: failed to find matching receipts entry for Unit" - ); - assert_eq!(get_contract_balance(id, provider.clone()).await, 1); - - // contract call transfer funds to another address - // specify amount x, provide amount x + 5; should succeed - let random_wallet = Wallet::random(&mut rand::thread_rng(), provider.clone()); - let (amount, asset_id, recipient) = ( - "3", - &format!("{{0x{base_asset_id}}}"), - &format!("(Address:{{0x{}}})", random_wallet.address()), - ); - let mut cmd = get_contract_call_cmd( - id, - node_url, - secret_key, - "transfer", - vec![amount, asset_id, recipient], - ); - cmd.call_parameters = cmd::call::CallParametersOpts { - amount: amount.parse::().unwrap() + 5, - asset_id: Some(*base_asset_id), - gas_forwarded: None, - }; - cmd.mode = cmd::call::ExecutionMode::Live; - let operation = cmd.validate_and_get_operation().unwrap(); - let call_response = call(operation, cmd).await.unwrap(); - assert_eq!(call_response.result.unwrap(), "()"); - assert_eq!(call_response.script.unwrap().outputs().len(), 3); - assert_eq!( - get_recipient_balance(random_wallet.address(), provider.clone()).await, - 3 - ); - assert_eq!(get_contract_balance(id, provider.clone()).await, 6); // extra amount (5) is forwarded to the contract - } -} diff --git a/forc-plugins/forc-client/src/op/call/list_functions.rs b/forc-plugins/forc-client/src/op/call/list_functions.rs deleted file mode 100644 index dd696b653e9..00000000000 --- a/forc-plugins/forc-client/src/op/call/list_functions.rs +++ /dev/null @@ -1,192 +0,0 @@ -use crate::op::call::{ - parser::{get_default_value, param_to_function_arg, param_type_val_to_token, token_to_string}, - Abi, -}; -use anyhow::{anyhow, Result}; -use fuels_core::types::{param_types::ParamType, ContractId}; -use std::collections::HashMap; -use std::io::Write; - -/// List all functions in the contracts' ABIs along with examples of how to call them. -/// This function supports listing functions from multiple contracts when additional -/// contract ABIs are provided via the --contract-abi parameter. -pub fn list_contract_functions( - main_contract_id: &ContractId, - abi_map: &HashMap, - writer: &mut W, -) -> Result<()> { - // First, list functions for the main contract - if let Some(main_abi) = abi_map.get(main_contract_id) { - list_functions_for_single_contract(main_contract_id, main_abi, true, writer)?; - } else { - return Err(anyhow!("Main contract ABI not found in abi_map")); - } - - // Then, list functions for additional contracts if any - let additional_contracts: Vec<_> = abi_map - .iter() - .filter(|(id, _)| *id != main_contract_id) - .collect(); - - if !additional_contracts.is_empty() { - writeln!(writer, "\n{}", "=".repeat(80))?; - writeln!(writer, "Additional Contracts:\n")?; - - for (contract_id, abi) in additional_contracts { - list_functions_for_single_contract(contract_id, abi, false, writer)?; - } - } - - Ok(()) -} - -/// List functions for a single contract -fn list_functions_for_single_contract( - contract_id: &ContractId, - abi: &Abi, - is_main_contract: bool, - writer: &mut W, -) -> Result<()> { - let header = if is_main_contract { - format!("Callable functions for contract: {contract_id}\n") - } else { - format!("Functions for additional contract: {contract_id}\n") - }; - - writeln!(writer, "{header}")?; - - if abi.unified.functions.is_empty() { - writeln!(writer, "No functions found in the contract ABI.\n")?; - return Ok(()); - } - - for func in &abi.unified.functions { - let func_args = func - .inputs - .iter() - .map(|input| { - let Ok(param_type) = ParamType::try_from_type_application(input, &abi.type_lookup) - else { - return Err(anyhow!("Failed to convert input type application")); - }; - let func_args = format!("{}: {}", input.name, param_to_function_arg(¶m_type)); - let func_args_input = { - let token = - param_type_val_to_token(¶m_type, &get_default_value(¶m_type)) - .map_err(|err| { - anyhow!( - "Failed to generate example call for {}: {}", - func.name, - err - ) - })?; - token_to_string(&token).map_err(|err| { - anyhow!( - "Failed to convert token to string for {}: {}", - func.name, - err - ) - })? - }; - Ok((func_args, func_args_input, param_type)) - }) - .collect::>>()?; - - let func_args_types = func_args - .iter() - .map(|(func_args, _, _)| func_args.to_owned()) - .collect::>() - .join(", "); - - let func_args_inputs = func_args - .iter() - .map(|(_, func_args_input, param_type)| match param_type { - ParamType::Array(_, _) - | ParamType::Unit - | ParamType::Tuple(_) - | ParamType::Struct { .. } - | ParamType::Enum { .. } - | ParamType::RawSlice - | ParamType::Vector(_) => format!("\"{func_args_input}\""), - _ => func_args_input.to_owned(), - }) - .collect::>() - .join(" "); - - let return_type = ParamType::try_from_type_application(&func.output, &abi.type_lookup) - .map(|param_type| param_to_function_arg(¶m_type)) - .map_err(|err| { - anyhow!( - "Failed to convert output type application for {}: {}", - func.name, - err - ) - })?; - - let painted_name = forc_util::ansiterm::Colour::Blue.paint(func.name.clone()); - writeln!(writer, "{painted_name}({func_args_types}) -> {return_type}")?; - writeln!( - writer, - " forc call \\\n --abi {} \\\n {} \\\n {} {}\n", - abi.source, contract_id, func.name, func_args_inputs, - )?; - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::op::call::tests::get_contract_instance; - use std::{io::Cursor, path::Path, str::FromStr}; - - #[tokio::test] - async fn test_list_contract_functions_preserves_abi_source_format() { - let (_, id, _, _) = get_contract_instance().await; - - // Load a test ABI content - let abi_path_str = "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json"; - let abi_path = Path::new(abi_path_str); - let abi_str = std::fs::read_to_string(abi_path).unwrap(); - - // Test different source formats - let test_cases = vec![ - ( - "file_path", - crate::cmd::call::AbiSource::File(std::path::PathBuf::from("./test-abi.json")), - "--abi ./test-abi.json", - ), - ( - "url", - crate::cmd::call::AbiSource::Url( - url::Url::parse("https://example.com/abi.json").unwrap(), - ), - "--abi https://example.com/abi.json", - ), - ( - "inline_json", - crate::cmd::call::AbiSource::String(r#"{"test":"value"}"#.to_string()), - r#"--abi {"test":"value"}"#, - ), - ]; - - for (test_name, source, expected_abi_arg) in test_cases { - let abi = Abi::from_str(&abi_str).unwrap().with_source(source); - let mut abi_map = HashMap::new(); - abi_map.insert(id, abi); - - let mut output = Cursor::new(Vec::::new()); - list_contract_functions(&id, &abi_map, &mut output).unwrap(); - - let output_bytes = output.into_inner(); - let output_string = String::from_utf8(output_bytes).unwrap(); - - // Verify the ABI source is preserved exactly as provided - assert!( - output_string.contains(expected_abi_arg), - "Test '{test_name}' failed: expected '{expected_abi_arg}' in output, but got:\n{output_string}" - ); - } - } -} diff --git a/forc-plugins/forc-client/src/op/call/missing_contracts.rs b/forc-plugins/forc-client/src/op/call/missing_contracts.rs deleted file mode 100644 index ae9d37c59f2..00000000000 --- a/forc-plugins/forc-client/src/op/call/missing_contracts.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::{bail, Result}; -use fuel_tx::{ConsensusParameters, ContractId}; -use fuels::programs::calls::{ - traits::TransactionTuner, utils::find_ids_of_missing_contracts, ContractCall, -}; -use fuels_accounts::{ - provider::Provider, - signers::private_key::PrivateKeySigner, - wallet::{Unlocked, Wallet}, -}; -use fuels_core::types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}; - -/// Get the missing contracts from a contract call by dry-running the transaction -/// to find contracts that are not explicitly listed in the call's `external_contracts` field. -/// Note: This function is derived from `determine_missing_contracts` in `fuels-rs` -pub async fn determine_missing_contracts( - call: &ContractCall, - provider: &Provider, - tx_policies: &TxPolicies, - variable_output_policy: &VariableOutputPolicy, - consensus_params: &ConsensusParameters, - log_decoder: &fuels_core::codec::LogDecoder, - account: &Wallet>, -) -> Result> { - let tb = call - .transaction_builder( - *tx_policies, - *variable_output_policy, - consensus_params, - call.inputs.clone(), - account, - ) - .expect("Failed to initialize transaction builder"); - - let tx = call - .build_tx(tb, account) - .await - .expect("Failed to build transaction"); - - match provider - .dry_run(tx) - .await? - .take_receipts_checked(Some(log_decoder)) - { - Ok(_) => Ok(vec![]), - Err(fuels_core::types::errors::Error::Transaction( - fuels::types::errors::transaction::Reason::Failure { receipts, .. }, - )) => { - let missing_contracts = find_ids_of_missing_contracts(&receipts); - Ok(missing_contracts) - } - Err(err) => bail!(err), - } -} diff --git a/forc-plugins/forc-client/src/op/call/mod.rs b/forc-plugins/forc-client/src/op/call/mod.rs deleted file mode 100644 index 9840a4249a1..00000000000 --- a/forc-plugins/forc-client/src/op/call/mod.rs +++ /dev/null @@ -1,409 +0,0 @@ -mod call_function; -pub mod list_functions; -mod missing_contracts; -mod parser; -pub mod trace; -mod transfer; - -use crate::cmd::call::AbiSource; -use crate::{ - cmd, - constants::DEFAULT_PRIVATE_KEY, - op::call::{ - call_function::call_function, list_functions::list_contract_functions, transfer::transfer, - }, - util::tx::{prompt_forc_wallet_password, select_local_wallet_account}, -}; -use anyhow::{anyhow, Result}; -use fuel_abi_types::abi::{ - program::ProgramABI, - unified_program::{UnifiedProgramABI, UnifiedTypeDeclaration}, -}; -use fuel_core_types::services::executor::TransactionExecutionStatus; -use fuel_tx::Receipt; -use fuels::{ - accounts::{ - provider::Provider, signers::private_key::PrivateKeySigner, wallet::Wallet, ViewOnlyAccount, - }, - crypto::SecretKey, -}; -use fuels_core::types::{transaction::TxPolicies, AssetId, ContractId}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, str::FromStr}; - -/// Response returned from a contract call operation -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct CallResponse { - pub tx_hash: String, - pub total_gas: u64, - #[serde(skip_serializing_if = "Option::is_none")] - pub result: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub receipts: Vec, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub trace_events: Vec, - #[serde(rename = "Script", skip_serializing_if = "Option::is_none")] - pub script: Option, -} - -/// A command for calling a contract function. -pub async fn call(operation: cmd::call::Operation, cmd: cmd::Call) -> anyhow::Result { - let is_json_mode = matches!(cmd.output, cmd::call::OutputFormat::Json); - let response = match operation { - cmd::call::Operation::ListFunctions { contract_id, abi } => { - if let cmd::call::OutputFormat::Json = cmd.output { - return Err(anyhow!("JSON output is not supported for list functions")); - } - - let abi_map = create_abi_map(contract_id, &abi, cmd.contract_abis).await?; - - // Use the simplified list_contract_functions function - list_contract_functions(&contract_id, &abi_map, &mut std::io::stdout())?; - - CallResponse::default() - } - cmd::call::Operation::DirectTransfer { - recipient, - amount, - asset_id, - } => { - let cmd::Call { - node, - caller, - gas, - mut output, - .. - } = cmd; - - // Already validated that mode is ExecutionMode::Live - let (wallet, tx_policies, base_asset_id) = - setup_connection(&node, caller, &gas).await?; - let asset_id = asset_id.unwrap_or(base_asset_id); - - transfer( - &wallet, - recipient, - amount, - asset_id, - tx_policies, - &node, - &mut output, - ) - .await? - } - cmd::call::Operation::CallFunction { - contract_id, - abi, - function, - function_args, - } => { - // Call the function with required parameters - call_function(contract_id, abi, function, function_args, cmd).await? - } - }; - - // If using JSON output mode, explicitly print the response for potential parsing/piping - if is_json_mode { - println!("{}", serde_json::to_string_pretty(&response).unwrap()); - } - - Ok(response) -} - -/// Sets up the connection to the node and initializes common parameters -async fn setup_connection( - node: &crate::NodeTarget, - caller: cmd::call::Caller, - gas: &Option, -) -> anyhow::Result<(Wallet, TxPolicies, AssetId)> { - let node_url = node.get_node_url(&None)?; - let provider = Provider::connect(node_url).await?; - let wallet = get_wallet(caller.signing_key, caller.wallet, provider).await?; - let provider = wallet.provider(); - let tx_policies = gas.as_ref().map(Into::into).unwrap_or_default(); - let consensus_parameters = provider.consensus_parameters().await?; - let base_asset_id = consensus_parameters.base_asset_id(); - - Ok((wallet, tx_policies, *base_asset_id)) -} - -/// Helper function to load ABI from file, URL, or raw string -async fn load_abi(abi: &AbiSource) -> anyhow::Result { - match abi { - AbiSource::File(path) => std::fs::read_to_string(path) - .map_err(|e| anyhow!("Failed to read ABI file at {:?}: {}", path, e)), - AbiSource::Url(url) => { - let response = reqwest::get(url.clone()) - .await - .map_err(|e| anyhow!("Failed to fetch ABI from URL {}: {}", url, e))?; - let bytes = response - .bytes() - .await - .map_err(|e| anyhow!("Failed to read response body from URL {}: {}", url, e))?; - String::from_utf8(bytes.to_vec()) - .map_err(|e| anyhow!("Failed to parse response as UTF-8 from URL {}: {}", url, e)) - } - AbiSource::String(json_str) => { - // Validate that it's valid JSON - serde_json::from_str::(json_str) - .map_err(|e| anyhow!("Invalid JSON in ABI string: {}", e))?; - Ok(json_str.to_owned()) - } - } -} - -/// Get the wallet to use for the call - based on optionally provided signing key and wallet flag. -async fn get_wallet( - signing_key: Option, - use_wallet: bool, - provider: Provider, -) -> Result { - match (signing_key, use_wallet) { - (None, false) => { - let secret_key = SecretKey::from_str(DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet = Wallet::new(signer, provider); - forc_tracing::println_warning(&format!( - "No signing key or wallet flag provided. Using default signer: 0x{}", - wallet.address() - )); - Ok(wallet) - } - (Some(secret_key), false) => { - let signer = PrivateKeySigner::new(secret_key); - let wallet = Wallet::new(signer, provider); - forc_tracing::println_warning(&format!( - "Using account {} derived from signing key...", - wallet.address() - )); - Ok(wallet) - } - (None, true) => { - let password = prompt_forc_wallet_password()?; - let wallet = select_local_wallet_account(&password, &provider).await?; - Ok(wallet) - } - (Some(secret_key), true) => { - forc_tracing::println_warning( - "Signing key is provided while requesting to use forc-wallet. Using signing key...", - ); - let signer = PrivateKeySigner::new(secret_key); - let wallet = Wallet::new(signer, provider); - Ok(wallet) - } - } -} - -#[derive(Debug, Clone)] -pub struct Abi { - source: AbiSource, - program: ProgramABI, - unified: UnifiedProgramABI, - // TODO: required for vm interpreter step through - // ↳ gh issue: https://github.com/FuelLabs/sway/issues/7197 - #[allow(dead_code)] - type_lookup: HashMap, -} - -impl Abi { - /// Set the source of the ABI after creation - pub fn with_source(mut self, source: AbiSource) -> Self { - self.source = source; - self - } -} - -impl FromStr for Abi { - type Err = String; - fn from_str(s: &str) -> Result { - let program: ProgramABI = - serde_json::from_str(s).map_err(|err| format!("failed to parse ABI: {err}"))?; - - let unified = UnifiedProgramABI::from_counterpart(&program) - .map_err(|err| format!("conversion to unified ABI format failed: {err}"))?; - - let type_lookup = unified - .types - .iter() - .map(|decl| (decl.type_id, decl.clone())) - .collect::>(); - - Ok(Self { - source: AbiSource::String(s.to_string()), - program, - unified, - type_lookup, - }) - } -} - -/// Displays transaction information -pub(crate) fn display_tx_info( - tx_hash: String, - result: Option, - mode: &cmd::call::ExecutionMode, - node: &crate::NodeTarget, -) { - // print tx hash and result - forc_tracing::println_label_green("tx hash:", &tx_hash); - if let Some(ref result) = result { - forc_tracing::println_label_green("result:", result); - } - - // display transaction url if live mode - if *mode == cmd::call::ExecutionMode::Live { - if let Some(explorer_url) = node.get_explorer_url() { - forc_tracing::println_label_green( - "\nView transaction:", - &format!("{explorer_url}/tx/0x{tx_hash}\n"), - ); - } - } -} - -/// Prints receipts and trace to the writer based on verbosity level -pub(crate) fn display_detailed_call_info( - tx: &TransactionExecutionStatus, - script: &fuel_tx::Script, - abis: &HashMap, - verbosity: u8, - writer: &mut impl std::io::Write, - trace_events: &[trace::TraceEvent], - labels: &HashMap, -) -> Result<()> { - if verbosity >= 4 { - forc_tracing::println_label_green( - "transaction script:\n", - &serde_json::to_string_pretty(script).unwrap(), - ); - } - if verbosity >= 3 { - let formatted_receipts = - forc_util::tx_utils::format_log_receipts(tx.result.receipts(), true) - .map_err(|e| anyhow!("Failed to format receipts: {}", e))?; - forc_tracing::println_label_green("receipts:", &formatted_receipts); - } - if verbosity >= 2 { - trace::display_transaction_trace(*tx.result.total_gas(), trace_events, labels, writer) - .map_err(|e| anyhow!("Failed to display transaction trace: {e}"))?; - } - if verbosity >= 1 { - let logs = tx - .result - .receipts() - .iter() - .filter_map(|receipt| match receipt { - Receipt::LogData { - id, - rb, - data: Some(data), - .. - } => { - let default_program_abi = ProgramABI::default(); - let program_abi = abis - .get(id) - .map(|abi| &abi.program) - .unwrap_or(&default_program_abi); - forc_util::tx_utils::decode_fuel_vm_log_data(&rb.to_string(), data, program_abi) - .ok() - .map(|decoded| decoded.value) - } - _ => None, - }) - .collect::>(); - - // print logs if there are any - if !logs.is_empty() { - forc_tracing::println_green_bold("logs:"); - for log in logs.iter() { - writeln!(writer, " {log:#}")?; - } - } - } - Ok(()) -} - -/// Create a HashMap of contract ABIs from a main ABI and optional additional contract ABIs -/// This is a reusable function for both call_function and list_functions operations -pub async fn create_abi_map( - main_contract_id: ContractId, - main_abi: &AbiSource, - additional_contract_abis: Option>, -) -> anyhow::Result> { - // Load main ABI - let main_abi_str = load_abi(main_abi).await?; - let main_abi = Abi::from_str(&main_abi_str) - .map_err(|e| anyhow!("Failed to parse main ABI: {}", e))? - .with_source(main_abi.clone()); - - // Start with main contract ABI - let mut abi_map = HashMap::from([(main_contract_id, main_abi)]); - - // Load additional contract ABIs if provided - if let Some(contract_abis) = additional_contract_abis { - for (contract_id, abi_path) in contract_abis { - match load_abi(&abi_path).await { - Ok(abi_str) => match Abi::from_str(&abi_str) { - Ok(additional_abi) => { - abi_map.insert(contract_id, additional_abi.with_source(abi_path.clone())); - forc_tracing::println_action_green( - "Loaded additional ABI for contract", - &format!("0x{contract_id}"), - ); - } - Err(e) => { - forc_tracing::println_warning(&format!( - "Failed to parse ABI for contract 0x{contract_id}: {e}" - )); - } - }, - Err(e) => { - forc_tracing::println_warning(&format!( - "Failed to load ABI for contract 0x{contract_id}: {e}" - )); - } - } - } - } - - Ok(abi_map) -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use fuels::prelude::*; - - abigen!(Contract( - name = "TestContract", - abi = "forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json" - )); - - pub async fn get_contract_instance() -> (TestContract, ContractId, Provider, SecretKey) - { - let secret_key = SecretKey::from_str(DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let coins = setup_single_asset_coins(signer.address(), AssetId::zeroed(), 1, 1_000_000); - let provider = setup_test_provider(coins, vec![], None, None) - .await - .unwrap(); - let wallet = get_wallet(Some(secret_key), false, provider.clone()) - .await - .unwrap(); - - let id = Contract::load_from( - "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types.bin", - LoadConfiguration::default(), - ) - .unwrap() - .deploy(&wallet, TxPolicies::default()) - .await - .unwrap() - .contract_id; - - let instance = TestContract::new(id, wallet.clone()); - - (instance, id, provider, secret_key) - } -} diff --git a/forc-plugins/forc-client/src/op/call/parser.rs b/forc-plugins/forc-client/src/op/call/parser.rs deleted file mode 100644 index 4bbaeb272ff..00000000000 --- a/forc-plugins/forc-client/src/op/call/parser.rs +++ /dev/null @@ -1,1812 +0,0 @@ -use anyhow::{anyhow, bail, Result}; -use fuels_core::types::{param_types::ParamType, EnumSelector, StaticStringToken, Token, U256}; -use std::{fmt::Write, str::FromStr}; - -/// Converts a ParamType and associated value into a Token -pub fn param_type_val_to_token(param_type: &ParamType, input: &str) -> Result { - // Parses a string value while preserving quotes and escaped characters - let parse_string_value = |input: &str| { - if input.starts_with('"') && input.ends_with('"') { - // Remove outer quotes and unescape internal quotes - let without_outer_quotes = &input[1..input.len() - 1]; - without_outer_quotes.replace("\\\"", "\"") - } else { - // If no quotes, just trim whitespace - input.trim().to_string() - } - }; - - match param_type { - ParamType::Unit => Ok(Token::Unit), - ParamType::Bool => bool::from_str(input) - .map(Token::Bool) - .map_err(|_| anyhow!("failed to parse bool value: {}", input)), - ParamType::U8 => u8::from_str(input) - .map(Token::U8) - .map_err(|_| anyhow!("failed to parse u8 value: {}", input)), - ParamType::U16 => u16::from_str(input) - .map(Token::U16) - .map_err(|_| anyhow!("failed to parse u16 value: {}", input)), - ParamType::U32 => u32::from_str(input) - .map(Token::U32) - .map_err(|_| anyhow!("failed to parse u32 value: {}", input)), - ParamType::U64 => u64::from_str(input) - .map(Token::U64) - .map_err(|_| anyhow!("failed to parse u64 value: {}", input)), - ParamType::U128 => u128::from_str(input) - .map(Token::U128) - .map_err(|_| anyhow!("failed to parse u128 value: {}", input)), - ParamType::U256 => { - // if prefix is 0x, it's a hex string - if input.starts_with("0x") { - U256::from_str(input) - .map(Token::U256) - .map_err(|_| anyhow!("failed to parse U256 value: {}", input)) - } else { - U256::from_dec_str(input) - .map(Token::U256) - .map_err(|_| anyhow!("failed to parse U256 value: {}", input)) - } - } - ParamType::B256 => { - // remove 0x prefix if provided - let input = input.trim_start_matches("0x"); - if input.len() != 64 { - return Err(anyhow!("B256 value must be 64 hex characters: {}", input)); - } - hex::decode(input) - .map(|bytes| Token::B256(bytes.try_into().unwrap())) - .map_err(|_| anyhow!("failed to parse B256 value: {}", input)) - } - ParamType::String => Ok(Token::String(parse_string_value(input))), - ParamType::Bytes => { - // remove 0x prefix if provided - let input = input.trim_start_matches("0x"); - if !input.len().is_multiple_of(2) { - return Err(anyhow!("bytes value must be even length: {}", input)); - } - hex::decode(input) - .map(Token::Bytes) - .map_err(|_| anyhow!("failed to parse bytes value: {}", input)) - } - ParamType::RawSlice => { - // remove 0x prefix if provided - let input = input.trim_start_matches("0x"); - if !input.len().is_multiple_of(2) { - return Err(anyhow!("raw slice value must be even length: {}", input)); - } - hex::decode(input) - .map(Token::RawSlice) - .map_err(|_| anyhow!("failed to parse raw slice value: {}", input)) - } - ParamType::StringArray(size) => { - let parsed_str = parse_string_value(input); - if parsed_str.len() != *size { - return Err(anyhow!( - "string array length mismatch: expected {}, got {}", - size, - parsed_str.len() - )); - } - Ok(Token::StringArray(StaticStringToken::new( - parsed_str, - Some(*size), - ))) - } - ParamType::StringSlice => Ok(Token::StringSlice(StaticStringToken::new( - parse_string_value(input), - None, - ))), - ParamType::Tuple(types) => { - // ensure input starts with '(' and ends with ')' - let parsed_tuple = parse_delimited_string(param_type, input)?; - Ok(Token::Tuple( - types - .iter() - .zip(parsed_tuple.iter()) - .map(|(ty, s)| param_type_val_to_token(ty, s)) - .collect::>>()?, - )) - } - ParamType::Array(ty, _size) => { - // ensure input starts with '[' and ends with ']' - let parsed_array = parse_delimited_string(param_type, input)?; - Ok(Token::Array( - parsed_array - .iter() - .map(|s| param_type_val_to_token(ty, s)) - .collect::>>()?, - )) - } - ParamType::Vector(ty) => { - // ensure input starts with '[' and ends with ']' - let parsed_vector = parse_delimited_string(param_type, input)?; - Ok(Token::Vector( - parsed_vector - .iter() - .map(|s| param_type_val_to_token(ty, s)) - .collect::>>()?, - )) - } - ParamType::Struct { fields, .. } => { - // ensure input starts with '{' and ends with '}' - let parsed_vals = parse_delimited_string(param_type, input)?; - let parsed_struct = fields - .iter() - .zip(parsed_vals.iter()) - .map(|((_, ty), val)| param_type_val_to_token(ty, val)) - .collect::>>()?; - Ok(Token::Struct(parsed_struct)) - } - ParamType::Enum { enum_variants, .. } => { - // enums must start with '(' and end with ')' - // enums must be in format of (variant_index:variant_value) or (variant_name:variant_value) - let parsed_enum = parse_delimited_string(param_type, input)?; - if parsed_enum.len() != 2 { - bail!( - "enum must have exactly two parts `(variant:value)`: {}", - input - ); - } - - let (variant_name_or_index, variant_value) = (&parsed_enum[0], &parsed_enum[1]); - // if variant can be parsed as u64 it is index; else it is name - let discriminant = match variant_name_or_index.parse::() { - Ok(index) => index, - Err(_) => { - // must be name; find index of variant_name_or_index in enum_variants given - let index = enum_variants - .variants() - .iter() - .position(|(name, _)| *name == *variant_name_or_index) - .ok_or(anyhow!( - "failed to find index of variant: {}", - variant_name_or_index - ))?; - index as u64 - } - }; - let (_, ty) = enum_variants.select_variant(discriminant).map_err(|_| { - anyhow!("failed to select enum variant: `{}`", variant_name_or_index) - })?; - let token = param_type_val_to_token(ty, variant_value).map_err(|_| { - anyhow!( - "failed to parse `{}` variant enum value: {}", - variant_name_or_index, - variant_value - ) - })?; - let enum_selector: EnumSelector = (discriminant, token, enum_variants.clone()); - Ok(Token::Enum(enum_selector.into())) - } - } -} - -/// Converts a Token to ParamType - unused unless we want to support input-param validation for enums -#[allow(dead_code)] -pub fn token_to_param_type(token: &Token) -> Result { - match token { - Token::Unit => Ok(ParamType::Unit), - Token::Bool(_) => Ok(ParamType::Bool), - Token::U8(_) => Ok(ParamType::U8), - Token::U16(_) => Ok(ParamType::U16), - Token::U32(_) => Ok(ParamType::U32), - Token::U64(_) => Ok(ParamType::U64), - Token::U128(_) => Ok(ParamType::U128), - Token::U256(_) => Ok(ParamType::U256), - Token::B256(_) => Ok(ParamType::B256), - Token::Bytes(_) => Ok(ParamType::Bytes), - Token::String(_) => Ok(ParamType::String), - Token::RawSlice(_) => Ok(ParamType::RawSlice), - Token::StringArray(str) => Ok(ParamType::StringArray(str.get_encodable_str()?.len())), - Token::StringSlice(_) => Ok(ParamType::StringSlice), - Token::Tuple(tokens) => Ok(ParamType::Tuple( - tokens - .iter() - .map(token_to_param_type) - .collect::>>()?, - )), - Token::Array(tokens) => Ok(ParamType::Array( - Box::new(token_to_param_type( - &tokens.iter().next().unwrap_or(&Token::default()).clone(), - )?), - tokens.len(), - )), - Token::Vector(tokens) => Ok(ParamType::Vector(Box::new(token_to_param_type( - &tokens.iter().next().unwrap_or(&Token::default()).clone(), - )?))), - Token::Struct(tokens) => Ok(ParamType::Struct { - name: "".to_string(), - fields: tokens - .iter() - .map(|t| { - ( - "".to_string(), - token_to_param_type(t).expect("failed to convert token to param type"), - ) - }) - .collect::>(), - generics: vec![], - }), - Token::Enum(boxed_enum) => { - let (discriminant, _, enum_variants) = &**boxed_enum; - let (_name, _ty) = enum_variants - .select_variant(*discriminant) - .expect("failed to select variant"); - Ok(ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: Default::default(), - }) - } - } -} - -/// Converts a Token to a string -pub fn token_to_string(token: &Token) -> Result { - match token { - Token::Unit => Ok("()".to_string()), - Token::Bool(b) => Ok(b.to_string()), - Token::U8(n) => Ok(n.to_string()), - Token::U16(n) => Ok(n.to_string()), - Token::U32(n) => Ok(n.to_string()), - Token::U64(n) => Ok(n.to_string()), - Token::U128(n) => Ok(n.to_string()), - Token::U256(n) => Ok(n.to_string()), - Token::B256(bytes) => { - let mut hex = String::with_capacity(bytes.len() * 2); - for byte in bytes { - write!(hex, "{byte:02x}").unwrap(); - } - Ok(format!("0x{hex}")) - } - Token::Bytes(bytes) => { - let mut hex = String::with_capacity(bytes.len() * 2); - for byte in bytes { - write!(hex, "{byte:02x}").unwrap(); - } - Ok(format!("0x{hex}")) - } - Token::String(s) => Ok(s.clone()), - Token::RawSlice(bytes) => { - let mut hex = String::with_capacity(bytes.len() * 2); - for byte in bytes { - write!(hex, "{byte:02x}").unwrap(); - } - Ok(format!("0x{hex}")) - } - Token::StringArray(token) => Ok(token.get_encodable_str().map(|s| s.to_string())?), - Token::StringSlice(token) => token - .get_encodable_str() - .map(|s| s.to_string()) - .map_err(|_| anyhow!("failed to get encodable string from StringSlice token")), - Token::Tuple(tokens) => { - let inner = tokens - .iter() - .map(token_to_string) - .collect::>>()? - .join(", "); - Ok(format!("({inner})")) - } - Token::Array(tokens) => { - let inner = tokens - .iter() - .map(token_to_string) - .collect::>>()? - .join(", "); - Ok(format!("[{inner}]")) - } - Token::Vector(tokens) => { - let inner = tokens - .iter() - .map(token_to_string) - .collect::>>()? - .join(", "); - Ok(format!("[{inner}]")) - } - Token::Struct(tokens) => { - let inner = tokens - .iter() - .map(token_to_string) - .collect::>>()? - .join(", "); - Ok(format!("{{{inner}}}")) - } - Token::Enum(selector) => { - let (discriminant, value, enum_variants) = &**selector; - let (name, _ty) = enum_variants - .select_variant(*discriminant) - .expect("failed to select variant"); - // TODO: variant validation - currently causing issues since we need deep recursive comparisons.. - // // ensure variant matches expected type - // let ty_got = token_to_param_type(value).map_err(|_| anyhow!("failed to convert token to param type"))?; - // if ty_got != *ty { - // // ensure all fields match of expected type if struct or enum - // match (ty, ty_got.clone()) { - // // (ParamType::Struct { fields: ty_fields, .. }, ParamType::Struct { fields: ty_got_fields, .. }) => { - // // for ((_, ty_param), (_, ty_got_param)) in ty_fields.iter().zip(ty_got_fields.iter()) { - // // if ty_param != ty_got_param { - // // return Err(anyhow!("expected type {:?} but got {:?}; mismatch in field: expected {:?}, got {:?}", ty, ty_got, ty_param, ty_got_param)); - // // } - // // } - // // }, - // (ParamType::Enum { enum_variants: ty_enum_variants, .. }, ParamType::Enum { enum_variants: ty_got_enum_variants, .. }) => { - // for ((_, ty_param), (_, ty_got_param)) in ty_enum_variants.variants().iter().zip(ty_got_enum_variants.variants().iter()) { - // if ty_param != ty_got_param { - // return Err(anyhow!("expected type {:?} but got {:?}; mismatch in variant: expected {:?}, got {:?}", ty, ty_got, ty_param, ty_got_param)); - // } - // } - // }, - // _ => return Err(anyhow!("expected type {:?} but got {:?}", ty, ty_got)), - // } - // } - Ok(format!("({}:{})", name, token_to_string(value)?)) - } - } -} - -/// Parses a delimited string into a vector of strings, preserving quoted content and nested structures -fn parse_delimited_string(param_type: &ParamType, input: &str) -> Result> { - let input = input.trim(); - let (start_delim, end_delim, separator) = match param_type { - ParamType::Tuple(_) => ('(', ')', ','), - ParamType::Array(_, _) | ParamType::Vector(_) => ('[', ']', ','), - ParamType::Struct { .. } => ('{', '}', ','), - ParamType::Enum { .. } => ('(', ')', ':'), - _ => bail!("Unsupported param type: {:?}", param_type), - }; - - if !input.starts_with(start_delim) || !input.ends_with(end_delim) { - bail!( - "input must start with '{}' and end with '{}': {}", - start_delim, - end_delim, - input - ); - } - - let inner = &input[1..input.len() - 1]; - let mut parts = Vec::new(); - let mut current = String::new(); - let mut in_quotes = false; - let mut escaped = false; - let mut nesting_level = 0u8; - - for c in inner.chars() { - match (c, in_quotes, escaped) { - ('\\', _, false) => { - escaped = true; - current.push(c); - } - ('"', _, true) => { - escaped = false; - current.push(c); - } - ('"', false, false) => { - in_quotes = true; - current.push(c); - } - ('"', true, false) => { - in_quotes = false; - current.push(c); - } - ('{', false, false) => { - nesting_level += 1; - current.push(c); - } - ('}', false, false) => { - nesting_level = nesting_level.saturating_sub(1); - current.push(c); - } - ('(', false, false) => { - nesting_level += 1; - current.push(c); - } - (')', false, false) => { - nesting_level = nesting_level.saturating_sub(1); - current.push(c); - } - ('[', false, false) => { - nesting_level += 1; - current.push(c); - } - (']', false, false) => { - nesting_level = nesting_level.saturating_sub(1); - current.push(c); - } - (c, false, false) if c == separator && nesting_level == 0 => { - if !current.trim().is_empty() { - parts.push(current.trim().to_string()); - current = String::new(); - } - } - (_, _, _) => { - escaped = false; - current.push(c); - } - } - } - - if !current.trim().is_empty() { - parts.push(current.trim().to_string()); - } - - Ok(parts) -} - -pub fn param_to_function_arg(param_type: &ParamType) -> String { - match param_type { - ParamType::Unit => "()".to_string(), - ParamType::Bool => "bool".to_string(), - ParamType::U8 => "u8".to_string(), - ParamType::U16 => "u16".to_string(), - ParamType::U32 => "u32".to_string(), - ParamType::U64 => "u64".to_string(), - ParamType::U128 => "U128".to_string(), - ParamType::U256 => "U256".to_string(), - ParamType::B256 => "b256".to_string(), - ParamType::Bytes => "Bytes".to_string(), - ParamType::String => "str".to_string(), - ParamType::RawSlice => "RawSlice".to_string(), - ParamType::StringArray(size) => format!("str[{size}]"), - ParamType::StringSlice => "str".to_string(), - ParamType::Tuple(types) => { - let inner = types - .iter() - .map(param_to_function_arg) - .collect::>() - .join(", "); - format!("({inner})") - } - ParamType::Array(ty, size) => { - let inner = param_to_function_arg(ty); - format!("[{inner}; {size}]") - } - ParamType::Vector(ty) => { - let inner = param_to_function_arg(ty); - format!("Vec<{inner}>") - } - ParamType::Struct { name, .. } => { - // only get last part of name - name.split("::").last().unwrap_or(name).to_string() - } - ParamType::Enum { name, .. } => { - // only get last part of name - name.split("::").last().unwrap_or(name).to_string() - } - } -} - -pub fn get_default_value(param_type: &ParamType) -> String { - match param_type { - ParamType::Unit => "()".to_string(), - ParamType::Bool => "false".to_string(), - ParamType::U8 => "0".to_string(), - ParamType::U16 => "0".to_string(), - ParamType::U32 => "0".to_string(), - ParamType::U64 => "0".to_string(), - ParamType::U128 => "0".to_string(), - ParamType::U256 => "0".to_string(), - ParamType::B256 => { - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - } - ParamType::Bytes => "0x".to_string(), - ParamType::String => "hello".to_string(), - ParamType::RawSlice => "0x".to_string(), - ParamType::StringArray(size) => "a".repeat(*size), - ParamType::StringSlice => "hello".to_string(), - ParamType::Tuple(types) => { - let inner = types - .iter() - .map(get_default_value) - .collect::>() - .join(", "); - format!("({inner})") - } - ParamType::Array(ty, size) => { - let inner = (0..*size) - .map(|_| get_default_value(ty)) - .collect::>() - .join(", "); - format!("[{inner}]") - } - ParamType::Vector(ty) => { - let inner = (0..2) - .map(|_| get_default_value(ty)) - .collect::>() - .join(", "); - format!("[{inner}]") - } - ParamType::Struct { fields, .. } => { - let inner = fields - .iter() - .map(|(_, ty)| get_default_value(ty)) - .collect::>() - .join(", "); - format!("{{{inner}}}") - } - ParamType::Enum { enum_variants, .. } => { - let (variant_key, variant_val_type) = enum_variants - .variants() - .first() - .expect("Enum must have at least one variant"); - let variant_val_str = get_default_value(variant_val_type); - format!("({variant_key}: {variant_val_str})") - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use fuels_core::types::param_types::EnumVariants; - - #[test] - fn test_parse_delimited_string() { - // Test with comma separator - let result = parse_delimited_string(&ParamType::Tuple(vec![]), "(a, b, c)").unwrap(); - assert_eq!(result, vec!["a", "b", "c"]); - - // Test with colon separator - let result = parse_delimited_string( - &ParamType::Enum { - name: "TestEnum".to_string(), - enum_variants: EnumVariants::new(vec![("".to_string(), ParamType::String)]) - .unwrap(), - generics: vec![], - }, - "(key:value)", - ) - .unwrap(); - assert_eq!(result, vec!["key", "value"]); - - // Test with spaces around separator - let result = parse_delimited_string( - &ParamType::Struct { - name: "TestStruct".to_string(), - fields: vec![ - ("a".to_string(), ParamType::String), - ("b".to_string(), ParamType::String), - ("c".to_string(), ParamType::String), - ], - generics: vec![], - }, - "{a , b , c}", - ) - .unwrap(); - assert_eq!(result, vec!["a", "b", "c"]); - - // Test with quoted strings - let result = parse_delimited_string( - &ParamType::Vector(Box::new(ParamType::String)), - "[\"a,b\", c]", - ) - .unwrap(); - assert_eq!(result, vec!["\"a,b\"", "c"]); - - // Test with escaped quotes - let result = - parse_delimited_string(&ParamType::Tuple(vec![]), "(\"\\\"a:b\\\"\", c)").unwrap(); - assert_eq!(result, vec!["\"\\\"a:b\\\"\"", "c"]); - - // Test with separator in quotes - let result = parse_delimited_string(&ParamType::Tuple(vec![]), "(\"a:b\",c)").unwrap(); - assert_eq!(result, vec!["\"a:b\"", "c"]); - } - - #[test] - fn param_type_val_to_token_conversion() { - // unit - let token = param_type_val_to_token(&ParamType::Unit, "").unwrap(); - assert_eq!(token, Token::Unit); - - // bool - let token = param_type_val_to_token(&ParamType::Bool, "true").unwrap(); - assert_eq!(token, Token::Bool(true)); - - // u8 - let token = param_type_val_to_token(&ParamType::U8, "42").unwrap(); - assert_eq!(token, Token::U8(42)); - - // u16 - let token = param_type_val_to_token(&ParamType::U16, "42").unwrap(); - assert_eq!(token, Token::U16(42)); - - // u32 - let token = param_type_val_to_token(&ParamType::U32, "42").unwrap(); - assert_eq!(token, Token::U32(42)); - - // u64 - let token = param_type_val_to_token(&ParamType::U64, "42").unwrap(); - assert_eq!(token, Token::U64(42)); - - // u128 - let token = param_type_val_to_token(&ParamType::U128, "42").unwrap(); - assert_eq!(token, Token::U128(42)); - - // u256 - hex string - let token = param_type_val_to_token(&ParamType::U256, "0x42").unwrap(); - assert_eq!(token, Token::U256(66.into())); - - // u256 - decimal string - let token = param_type_val_to_token(&ParamType::U256, "42").unwrap(); - assert_eq!(token, Token::U256(42.into())); - - // u256 - decimal string with leading 0 - let token = param_type_val_to_token( - &ParamType::U256, - "0000000000000000000000000000000000000000000000000000000000000042", - ) - .unwrap(); - assert_eq!(token, Token::U256(42.into())); - - // b256 - hex string, incorrect length - let token_result = param_type_val_to_token(&ParamType::B256, "0x42"); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "B256 value must be 64 hex characters: 42" - ); - - // b256 - hex string, correct length - let token = param_type_val_to_token( - &ParamType::B256, - "0x0000000000000000000000000000000000000000000000000000000000000042", - ) - .unwrap(); - assert_eq!( - token, - Token::B256([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 66 - ]) - ); - - // b256 - no 0x prefix - let token = param_type_val_to_token( - &ParamType::B256, - "0000000000000000000000000000000000000000000000000000000000000042", - ) - .unwrap(); - assert_eq!( - token, - Token::B256([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 66 - ]) - ); - - // bytes - let token = param_type_val_to_token(&ParamType::Bytes, "0x42").unwrap(); - assert_eq!(token, Token::Bytes(vec![66])); - - // bytes - no 0x prefix - let token = param_type_val_to_token(&ParamType::Bytes, "42").unwrap(); - assert_eq!(token, Token::Bytes(vec![66])); - - // string - let token = param_type_val_to_token(&ParamType::String, "fuel").unwrap(); - assert_eq!(token, Token::String("fuel".to_string())); - - // raw slice - let token = param_type_val_to_token(&ParamType::RawSlice, "0x42").unwrap(); - assert_eq!(token, Token::RawSlice(vec![66])); - - // raw slice - no 0x prefix - let token = param_type_val_to_token(&ParamType::RawSlice, "42").unwrap(); - assert_eq!(token, Token::RawSlice(vec![66])); - - // string array - single val - let token = param_type_val_to_token(&ParamType::StringArray(4), "fuel").unwrap(); - assert_eq!( - token, - Token::StringArray(StaticStringToken::new("fuel".to_string(), Some(4))) - ); - - // string array - incorrect length fails - let token_result = param_type_val_to_token(&ParamType::StringArray(2), "fuel"); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "string array length mismatch: expected 2, got 4" - ); - - // string slice - let token = param_type_val_to_token(&ParamType::StringSlice, "fuel").unwrap(); - assert_eq!( - token, - Token::StringSlice(StaticStringToken::new("fuel".to_string(), None)) - ); - - // tuple - incorrect format - let token_result = param_type_val_to_token( - &ParamType::Tuple(vec![ParamType::String, ParamType::String]), - "fuel, 42", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "input must start with '(' and end with ')': fuel, 42" - ); - - // tuple - let token = param_type_val_to_token( - &ParamType::Tuple(vec![ParamType::String, ParamType::String]), - "(fuel, 42)", - ) - .unwrap(); - assert_eq!( - token, - Token::Tuple(vec![ - Token::String("fuel".to_string()), - Token::String("42".to_string()) - ]) - ); - - // tuple - different param types - let token = param_type_val_to_token( - &ParamType::Tuple(vec![ParamType::String, ParamType::U8]), - "(fuel, 42)", - ) - .unwrap(); - assert_eq!( - token, - Token::Tuple(vec![Token::String("fuel".to_string()), Token::U8(42)]) - ); - - // array - let token = - param_type_val_to_token(&ParamType::Array(ParamType::String.into(), 3), "[fuel, 42]") - .unwrap(); - assert_eq!( - token, - Token::Array(vec![ - Token::String("fuel".to_string()), - Token::String("42".to_string()) - ]) - ); - - // array - incorrect format - let token_result = - param_type_val_to_token(&ParamType::Array(ParamType::String.into(), 3), "fuel 42"); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "input must start with '[' and end with ']': fuel 42" - ); - - // vector - correct format - let token = - param_type_val_to_token(&ParamType::Vector(ParamType::String.into()), "[fuel, 42]") - .unwrap(); - assert_eq!( - token, - Token::Vector(vec![ - Token::String("fuel".to_string()), - Token::String("42".to_string()) - ]) - ); - - // vector - incorrect format - let token_result = - param_type_val_to_token(&ParamType::Vector(ParamType::String.into()), "fuel 42"); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "input must start with '[' and end with ']': fuel 42" - ); - - // struct - correct format; single value - let token = param_type_val_to_token( - &ParamType::Struct { - name: "".to_string(), - fields: vec![("".to_string(), ParamType::String)], - generics: vec![], - }, - "{fuel, 42}", - ) - .unwrap(); - assert_eq!( - token, - Token::Struct(vec![Token::String("fuel".to_string())]) - ); - - // struct - correct format; multiple values - let token = param_type_val_to_token( - &ParamType::Struct { - name: "".to_string(), - fields: vec![ - ("".to_string(), ParamType::String), - ("".to_string(), ParamType::String), - ], - generics: vec![], - }, - "{fuel, 42}", - ) - .unwrap(); - assert_eq!( - token, - Token::Struct(vec![ - Token::String("fuel".to_string()), - Token::String("42".to_string()) - ]) - ); - - // struct - correct format; multiple values; different param types - let token = param_type_val_to_token( - &ParamType::Struct { - name: "".to_string(), - fields: vec![ - ("".to_string(), ParamType::String), - ("".to_string(), ParamType::U8), - ], - generics: vec![], - }, - "{fuel, 42}", - ) - .unwrap(); - assert_eq!( - token, - Token::Struct(vec![Token::String("fuel".to_string()), Token::U8(42)]) - ); - - // struct - incorrect format (same as tuple) - let token_result = param_type_val_to_token( - &ParamType::Struct { - name: "".to_string(), - fields: vec![("a".to_string(), ParamType::String)], - generics: vec![], - }, - "fuel, 42", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "input must start with '{' and end with '}': fuel, 42" - ); - - // enum - incorrect format - let token_result = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(vec![ - ("".to_string(), ParamType::String), - ("".to_string(), ParamType::U8), - ]) - .unwrap(), - generics: vec![], - }, - "Active: true", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "input must start with '(' and end with ')': Active: true" - ); - - // enum - variant not found - let enum_variants = EnumVariants::new(vec![ - ("".to_string(), ParamType::String), - ("".to_string(), ParamType::U8), - ]) - .unwrap(); - let token_result = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(Active: true)", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "failed to find index of variant: Active" - ); - - // enum - variant found, incorrect variant value (expect cannot parse u8 as bool) - let enum_variants = EnumVariants::new(vec![ - ("Input".to_string(), ParamType::String), - ("Active".to_string(), ParamType::U8), - ]) - .unwrap(); - let token_result = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(Active: true)", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "failed to parse `Active` variant enum value: true" - ); - - // enum - variant found, correct variant value - let enum_variants = EnumVariants::new(vec![ - ("Input".to_string(), ParamType::String), - ("Active".to_string(), ParamType::Bool), - ]) - .unwrap(); - let token = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(Active: true)", - ) - .unwrap(); - assert_eq!( - token, - Token::Enum((1u64, Token::Bool(true), enum_variants).into()) - ); - - // enum - variant found by index, incorrect index type (should be bool) - let enum_variants = EnumVariants::new(vec![ - ("Input".to_string(), ParamType::String), - ("Active".to_string(), ParamType::Bool), - ]) - .unwrap(); - let token_result = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(1: 1)", - ); - assert!(token_result.is_err()); - assert_eq!( - token_result.unwrap_err().to_string(), - "failed to parse `1` variant enum value: 1" - ); - - // enum - variant found by index, correct variant value - let enum_variants = EnumVariants::new(vec![ - ("Input".to_string(), ParamType::String), - ("Active".to_string(), ParamType::Bool), - ]) - .unwrap(); - let token = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(1: true)", - ) - .unwrap(); - assert_eq!( - token, - Token::Enum((1u64, Token::Bool(true), enum_variants).into()) - ); - - // enum (complex example) - variants with a struct that contains an enum and a vec that contains another enum with 2 variants - let enum_variants = EnumVariants::new(vec![ - ( - "Input".to_string(), - ParamType::Struct { - generics: vec![], - name: "".to_string(), - fields: vec![ - ( - "".to_string(), - ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(), - generics: vec![], - }, - ), - ( - "".to_string(), - ParamType::Vector(Box::new(ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(), - generics: vec![], - })), - ), - ], - }, - ), - ("Active".to_string(), ParamType::Bool), - ]) - .unwrap(); - let token = param_type_val_to_token( - &ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![], - }, - "(Input: {(Active: true), [(Pending: 42)]})", - ) - .unwrap(); - assert_eq!( - token, - Token::Enum( - ( - 0u64, - Token::Struct(vec![ - Token::Enum( - ( - 0u64, - Token::Bool(true), - EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64) - ]) - .unwrap() - ) - .into() - ), - Token::Vector(vec![Token::Enum( - ( - 1u64, - Token::U64(42), - EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64) - ]) - .unwrap() - ) - .into() - )]) - ]), - enum_variants - ) - .into() - ) - ); - } - - #[test] - fn token_to_param_type_conversion() { - // unit - let token = Token::Unit; - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::Unit); - - // bool - let token = Token::Bool(true); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::Bool); - - // u8 - let token = Token::U8(42); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U8); - - // u16 - let token = Token::U16(42); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U16); - - // u32 - let token = Token::U32(42); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U32); - - // u64 - let token = Token::U64(42); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U64); - - // u128 - let token = Token::U128(42); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U128); - - // u256 - let token = Token::U256(42.into()); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::U256); - - // b256 - let token = Token::B256([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 66, - ]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::B256); - - // bytes - let token = Token::Bytes(vec![66]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::Bytes); - - // string - let token = Token::String("fuel".to_string()); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::String); - - // raw slice - let token = Token::RawSlice(vec![66]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::RawSlice); - - // string array - let token = Token::StringArray(StaticStringToken::new("fuel".to_string(), Some(4))); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::StringArray(4)); - - // string slice - let token = Token::StringSlice(StaticStringToken::new("fuel".to_string(), None)); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::StringSlice); - - // tuple - let token = Token::Tuple(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!( - param_type, - ParamType::Tuple(vec![ParamType::String, ParamType::U8]) - ); - - // array - let token = Token::Array(vec![ - Token::String("fuel".to_string()), - Token::String("rocks".to_string()), - ]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::Array(Box::new(ParamType::String), 2)); - - // vector - let token = Token::Vector(vec![ - Token::String("fuel".to_string()), - Token::String("rocks".to_string()), - ]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!(param_type, ParamType::Vector(Box::new(ParamType::String))); - - // struct - let token = Token::Struct(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!( - param_type, - ParamType::Struct { - name: "".to_string(), - fields: vec![ - ("".to_string(), ParamType::String), - ("".to_string(), ParamType::U8) - ], - generics: vec![] - } - ); - - // struct (complex example) - struct with 2 fields that contains another struct with 2 fields - let token = Token::Struct(vec![ - Token::Struct(vec![Token::U32(42), Token::U32(42)]), - Token::U32(42), - ]); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!( - param_type, - ParamType::Struct { - name: "".to_string(), - fields: vec![ - ( - "".to_string(), - ParamType::Struct { - name: "".to_string(), - fields: vec![ - ("".to_string(), ParamType::U32), - ("".to_string(), ParamType::U32) - ], - generics: vec![] - } - ), - ("".to_string(), ParamType::U32) - ], - generics: vec![] - } - ); - - // enum - let token = Token::Enum( - ( - 0u64, - Token::U32(42), - EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(), - ) - .into(), - ); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!( - param_type, - ParamType::Enum { - name: "".to_string(), - enum_variants: EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64) - ]) - .unwrap(), - generics: vec![] - } - ); - - // enum (complex example) - variants with a struct that contains an enum and a vec that contains another enum with 2 variants - let inner_enum_variants = EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(); - let enum_variants = EnumVariants::new(vec![ - ( - "Input".to_string(), - ParamType::Struct { - generics: vec![], - name: "".to_string(), - fields: vec![ - ( - "".to_string(), - ParamType::Enum { - name: "".to_string(), - enum_variants: inner_enum_variants.clone(), - generics: vec![], - }, - ), - ( - "".to_string(), - ParamType::Vector(Box::new(ParamType::Enum { - name: "".to_string(), - enum_variants: inner_enum_variants.clone(), - generics: vec![], - })), - ), - ], - }, - ), - ("Active".to_string(), ParamType::Bool), - ]) - .unwrap(); - let token = Token::Enum( - ( - 0u64, - Token::Struct(vec![ - Token::Enum((0u64, Token::Bool(true), inner_enum_variants.clone()).into()), - Token::Vector(vec![Token::Enum( - (1u64, Token::U64(42), inner_enum_variants.clone()).into(), - )]), - ]), - enum_variants.clone(), - ) - .into(), - ); - let param_type = token_to_param_type(&token).unwrap(); - assert_eq!( - param_type, - ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![] - } - ); - - // enum (complex example 2) - 2 variants, one with a struct that contains another struct with 2 fields, another with a struct - // enum GenericEnum { - // container: GenericStruct { - // value: GenericStruct { - // value: u32, - // description: str[4], - // }, - // description: str[4], - // }, - // value: GenericStruct { - // value: u32, - // description: str[4], - // }, - // } - let inner_struct = ParamType::Struct { - generics: vec![ParamType::U32], - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - }; - let enum_variants = EnumVariants::new(vec![ - ( - "container".to_string(), - ParamType::Struct { - name: "GenericStruct".to_string(), - generics: vec![ParamType::Struct { - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - generics: vec![ParamType::U32], - }], - fields: vec![ - ("value".to_string(), inner_struct.clone()), - ("description".to_string(), ParamType::StringArray(4)), - ], - }, - ), - ("value".to_string(), inner_struct.clone()), - ]) - .unwrap(); - let token = Token::Enum( - ( - 0u64, - Token::Struct(vec![ - Token::Struct(vec![ - Token::U32(42), - Token::StringArray(StaticStringToken::new("fuel".into(), Some(4))), - ]), - Token::StringArray(StaticStringToken::new("fuel".into(), Some(4))), - ]), - enum_variants.clone(), - ) - .into(), - ); - let output = token_to_param_type(&token).unwrap(); - assert_eq!( - output, - ParamType::Enum { - name: "".to_string(), - enum_variants: enum_variants.clone(), - generics: vec![] - } - ); - } - - #[test] - fn token_to_string_conversion() { - // unit - let token = Token::Unit; - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "()"); - - // bool - let token = Token::Bool(true); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "true"); - - // u8 - let token = Token::U8(42); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // u16 - let token = Token::U16(42); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // u32 - let token = Token::U32(42); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // u64 - let token = Token::U64(42); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // u128 - let token = Token::U128(42); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // u256 - let token = Token::U256(42.into()); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "42"); - - // b256 - let token = Token::B256([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 66, - ]); - let output = token_to_string(&token).unwrap(); - assert_eq!( - output, - "0x0000000000000000000000000000000000000000000000000000000000000042" - ); - - // bytes - let token = Token::Bytes(vec![66]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "0x42"); - - // string - let token = Token::String("fuel".to_string()); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "fuel"); - - // raw slice - let token = Token::RawSlice(vec![66]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "0x42"); - - // string array - fails if length is incorrect - let token = Token::StringArray(StaticStringToken::new("fuel".to_string(), Some(1))); - let output_res = token_to_string(&token); - assert!(output_res.is_err()); - assert_eq!( - output_res.unwrap_err().to_string(), - "codec: string data has len 4, but the expected len is 1" - ); - - // string array - fails if length overflows - let token = Token::StringArray(StaticStringToken::new("fuel".to_string(), Some(10))); - let output_res = token_to_string(&token); - assert!(output_res.is_err()); - assert_eq!( - output_res.unwrap_err().to_string(), - "codec: string data has len 4, but the expected len is 10" - ); - - // string array - succeeds if length not provided - // TODO: probably an issue in the SDK; should fail validation - let token = Token::StringArray(StaticStringToken::new("fuel".to_string(), None)); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "fuel"); - - // string array - succeeds if length is correct - let token = Token::StringArray(StaticStringToken::new("fuel".to_string(), Some(4))); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "fuel"); - - // string slice - let token = Token::StringSlice(StaticStringToken::new("fuel".to_string(), None)); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "fuel"); - - // tuple - let token = Token::Tuple(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "(fuel, 42)"); - - // array - same param types - let token = Token::Array(vec![ - Token::String("fuel".to_string()), - Token::String("rocks".to_string()), - ]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "[fuel, rocks]"); - - // array - different param types - // TODO: probably an issue in the SDK; should fail validation - let token = Token::Array(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "[fuel, 42]"); - - // vector - same param types - let token = Token::Vector(vec![ - Token::String("fuel".to_string()), - Token::String("rocks".to_string()), - ]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "[fuel, rocks]"); - - // vector - different param types - // TODO: probably an issue in the SDK; should fail validation - let token = Token::Vector(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "[fuel, 42]"); - - // struct - single value - let token = Token::Struct(vec![Token::String("fuel".to_string())]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "{fuel}"); - - // struct - multiple values - let token = Token::Struct(vec![Token::String("fuel".to_string()), Token::U8(42)]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "{fuel, 42}"); - - // struct (complex example) - struct with 2 fields that contains another struct with 2 fields - let token = Token::Struct(vec![ - Token::Struct(vec![Token::U32(42), Token::U32(42)]), - Token::U32(42), - ]); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "{{42, 42}, 42}"); - - // TODO: potentially re-enable this if we want to support input-param validation - // // enum - fails if variant incorrect - // let enum_variants = EnumVariants::new(vec![("Active".to_string(), ParamType::Bool), ("Pending".to_string(), ParamType::U64)]).unwrap(); - // let token = Token::Enum((1u64, Token::Bool(true), enum_variants).into()); - // let output_res = token_to_string(&token); - // assert!(output_res.is_err()); - // assert_eq!(output_res.unwrap_err().to_string(), "expected type U64 but got Bool"); - - // enum - correct variant - let enum_variants = EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(); - let token = Token::Enum((1u64, Token::U64(42), enum_variants).into()); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "(Pending:42)"); - - // enum (complex example) - variants with a struct that contains an enum and a vec that contains another enum with 2 variants - let inner_enum_variants = EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(); - let enum_variants = EnumVariants::new(vec![ - ( - "Input".to_string(), - ParamType::Struct { - generics: vec![], - name: "".to_string(), - fields: vec![ - ( - "".to_string(), - ParamType::Enum { - name: "".to_string(), - enum_variants: inner_enum_variants.clone(), - generics: vec![], - }, - ), - ( - "".to_string(), - ParamType::Vector(Box::new(ParamType::Enum { - name: "".to_string(), - enum_variants: inner_enum_variants.clone(), - generics: vec![], - })), - ), - ], - }, - ), - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(); - - // test active variant - let token = Token::Enum((1u64, Token::Bool(true), enum_variants.clone()).into()); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "(Active:true)"); - - // test Input variant - let token = Token::Enum( - ( - 0u64, - Token::Struct(vec![ - Token::Enum((0u64, Token::Bool(true), inner_enum_variants.clone()).into()), - Token::Vector(vec![Token::Enum( - (1u64, Token::U64(42), inner_enum_variants.clone()).into(), - )]), - ]), - enum_variants, - ) - .into(), - ); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "(Input:{(Active:true), [(Pending:42)]})"); - - // enum (complex example 2) - 2 variants, one with a struct that contains another struct with 2 fields, another with a struct - // enum GenericEnum> { - // container: GenericStruct { - // value: GenericStruct { - // value: u32, - // description: str[4], - // }, - // description: str[4], - // }, - // value: GenericStruct { - // value: u32, - // description: str[4], - // }, - // } - let inner_struct = ParamType::Struct { - generics: vec![ParamType::U32], - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - }; - let enum_variants = EnumVariants::new(vec![ - ( - "container".to_string(), - ParamType::Struct { - generics: vec![ParamType::Struct { - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - generics: vec![ParamType::U32], - }], - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), inner_struct.clone()), - ("description".to_string(), ParamType::StringArray(4)), - ], - }, - ), - ("value".to_string(), inner_struct.clone()), - ]) - .unwrap(); - let token = Token::Enum( - ( - 0u64, - Token::Struct(vec![ - Token::Struct(vec![ - Token::U32(42), - Token::StringArray(StaticStringToken::new("fuel".into(), Some(4))), - ]), - Token::StringArray(StaticStringToken::new("fuel".into(), Some(4))), - ]), - enum_variants, - ) - .into(), - ); - let output = token_to_string(&token).unwrap(); - assert_eq!(output, "(container:{{42, fuel}, fuel})"); - } - - #[test] - fn param_to_function_arg_conversion() { - let param_type = ParamType::Unit; - assert_eq!(param_to_function_arg(¶m_type), "()"); - - let param_type = ParamType::Bool; - assert_eq!(param_to_function_arg(¶m_type), "bool"); - - let param_type = ParamType::U8; - assert_eq!(param_to_function_arg(¶m_type), "u8"); - - let param_type = ParamType::U16; - assert_eq!(param_to_function_arg(¶m_type), "u16"); - - let param_type = ParamType::U32; - assert_eq!(param_to_function_arg(¶m_type), "u32"); - - let param_type = ParamType::U64; - assert_eq!(param_to_function_arg(¶m_type), "u64"); - - let param_type = ParamType::U128; - assert_eq!(param_to_function_arg(¶m_type), "U128"); - - let param_type = ParamType::U256; - assert_eq!(param_to_function_arg(¶m_type), "U256"); - - let param_type = ParamType::B256; - assert_eq!(param_to_function_arg(¶m_type), "b256"); - - let param_type = ParamType::Bytes; - assert_eq!(param_to_function_arg(¶m_type), "Bytes"); - - let param_type = ParamType::String; - assert_eq!(param_to_function_arg(¶m_type), "str"); - - let param_type = ParamType::RawSlice; - assert_eq!(param_to_function_arg(¶m_type), "RawSlice"); - - let param_type = ParamType::StringArray(4); - assert_eq!(param_to_function_arg(¶m_type), "str[4]"); - - let param_type = ParamType::StringSlice; - assert_eq!(param_to_function_arg(¶m_type), "str"); - - let param_type = ParamType::Tuple(vec![ParamType::U32, ParamType::StringArray(4)]); - assert_eq!(param_to_function_arg(¶m_type), "(u32, str[4])"); - - let param_type = ParamType::Array(Box::new(ParamType::U32), 4); - assert_eq!(param_to_function_arg(¶m_type), "[u32; 4]"); - - let param_type = ParamType::Vector(Box::new(ParamType::U32)); - assert_eq!(param_to_function_arg(¶m_type), "Vec"); - - let param_type = ParamType::Struct { - generics: vec![], - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - }; - assert_eq!(param_to_function_arg(¶m_type), "GenericStruct"); - - let param_type = ParamType::Enum { - generics: vec![], - name: "GenericEnum".to_string(), - enum_variants: EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(), - }; - assert_eq!(param_to_function_arg(¶m_type), "GenericEnum"); - } - - #[test] - fn get_default_value_for_param_type() { - let param_type = ParamType::Unit; - assert_eq!(get_default_value(¶m_type), "()"); - - let param_type = ParamType::Bool; - assert_eq!(get_default_value(¶m_type), "false"); - - let param_type = ParamType::U8; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::U16; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::U32; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::U64; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::U128; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::U256; - assert_eq!(get_default_value(¶m_type), "0"); - - let param_type = ParamType::B256; - assert_eq!( - get_default_value(¶m_type), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - - let param_type = ParamType::Bytes; - assert_eq!(get_default_value(¶m_type), "0x"); - - let param_type = ParamType::String; - assert_eq!(get_default_value(¶m_type), "hello"); - - let param_type = ParamType::RawSlice; - assert_eq!(get_default_value(¶m_type), "0x"); - - let param_type = ParamType::StringArray(4); - assert_eq!(get_default_value(¶m_type), "aaaa"); - - let param_type = ParamType::StringSlice; - assert_eq!(get_default_value(¶m_type), "hello"); - - let param_type = ParamType::Tuple(vec![ParamType::U32, ParamType::StringArray(4)]); - assert_eq!(get_default_value(¶m_type), "(0, aaaa)"); - - let param_type = ParamType::Array(Box::new(ParamType::U32), 4); - assert_eq!(get_default_value(¶m_type), "[0, 0, 0, 0]"); - - let param_type = ParamType::Vector(Box::new(ParamType::U32)); - assert_eq!(get_default_value(¶m_type), "[0, 0]"); - - let param_type = ParamType::Struct { - generics: vec![], - name: "GenericStruct".to_string(), - fields: vec![ - ("value".to_string(), ParamType::U32), - ("description".to_string(), ParamType::StringArray(4)), - ], - }; - assert_eq!(get_default_value(¶m_type), "{0, aaaa}"); - - let param_type = ParamType::Enum { - generics: vec![], - name: "GenericEnum".to_string(), - enum_variants: EnumVariants::new(vec![ - ("Active".to_string(), ParamType::Bool), - ("Pending".to_string(), ParamType::U64), - ]) - .unwrap(), - }; - assert_eq!(get_default_value(¶m_type), "(Active: false)"); - } -} diff --git a/forc-plugins/forc-client/src/op/call/trace/mod.rs b/forc-plugins/forc-client/src/op/call/trace/mod.rs deleted file mode 100644 index 25ec6ce5782..00000000000 --- a/forc-plugins/forc-client/src/op/call/trace/mod.rs +++ /dev/null @@ -1,1791 +0,0 @@ -pub mod storage; - -use crate::{cmd, op::call::Abi}; -use ansiterm::Color; -use anyhow::{anyhow, Result}; -use fuel_abi_types::{ - abi::program::PanickingCall, - revert_info::{RevertInfo, RevertKind}, -}; -use fuel_core_types::tai64::Tai64; -use fuel_tx::Receipt; -use fuel_vm::{ - fuel_asm::Word, - fuel_types::BlockHeight, - interpreter::{Interpreter, InterpreterParams, MemoryInstance}, - prelude::*, - state::ProgramState, -}; -use fuels::types::Token; -use fuels_core::{ - codec::{ABIDecoder, DecoderConfig}, - types::{param_types::ParamType, ContractId}, -}; -use std::{collections::HashMap, io::Read}; -use storage::ShallowStorage; - -/// A reader for VM memory that implements the necessary traits for ABI decoding -#[derive(Clone)] -pub struct MemoryReader<'a> { - mem: &'a MemoryInstance, - at: Word, -} - -impl<'a> MemoryReader<'a> { - pub fn new(mem: &'a MemoryInstance, at: Word) -> Self { - Self { mem, at } - } -} - -impl Read for MemoryReader<'_> { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let at = self.at; - self.at += buf.len() as Word; - buf.copy_from_slice( - self.mem - .read(at, buf.len()) - .map_err(|_err| std::io::Error::other("Inaccessible memory"))?, - ); - Ok(buf.len()) - } -} - -/// Interprets execution trace by stepping through VM execution until call receipts are encountered -pub async fn interpret_execution_trace( - provider: &fuels::accounts::provider::Provider, - mode: &cmd::call::ExecutionMode, - consensus_params: &ConsensusParameters, - script: &fuel_tx::Script, - receipts: &[Receipt], - storage_reads: Vec, - abis: &HashMap, -) -> Result> { - let mut tracer = CallRetTracer::new(abis); - - let block_height: BlockHeight = (provider.latest_block_height().await?).into(); - let gas_price = provider.latest_gas_price().await?; - let block = provider - .block_by_height(block_height) - .await? - .ok_or(anyhow!("Block not found"))?; - - // Create shallow storage with empty initial storage reads - let storage = ShallowStorage { - block_height, - timestamp: Tai64::from_unix( - block - .header - .time - .ok_or(anyhow!("Block time not found"))? - .timestamp(), - ), - consensus_parameters_version: block.header.consensus_parameters_version, - state_transition_version: block.header.state_transition_bytecode_version, - coinbase: Default::default(), // TODO: get from tx - storage: std::cell::RefCell::new(ShallowStorage::initial_storage(storage_reads)), - }; - - let script_tx = script - .clone() - .into_checked_basic(block_height, consensus_params) - .map_err(|err| anyhow!("Failed to check transaction: {err:?}"))? - .into_ready( - gas_price.gas_price, - consensus_params.gas_costs(), - consensus_params.fee_params(), - None, - ) - .map_err(|err| anyhow!("Failed to check transaction: {err:?}"))?; - - let mut vm = Interpreter::<_, _, Script>::with_storage( - MemoryInstance::new(), - storage.clone(), - InterpreterParams::new(gas_price.gas_price, consensus_params), - ); - vm.set_single_stepping(true); - - let mut t = *vm - .transact(script_tx) - .map_err(|e| anyhow!("Failed to transact in trace interpreter: {e:?}"))? - .state(); - loop { - tracer.process_vm_state(&vm)?; - match t { - ProgramState::Return(_) | ProgramState::ReturnData(_) | ProgramState::Revert(_) => { - break - } - ProgramState::RunProgram(_) | ProgramState::VerifyPredicate(_) => { - t = vm - .resume() - .map_err(|e| anyhow!("Failed to resume VM in trace interpreter: {e:?}"))?; - } - } - } - - if vm.receipts() != receipts { - match mode { - cmd::call::ExecutionMode::Live => return Err(anyhow!("Receipts mismatch")), - _ => forc_tracing::println_warning( - "Receipts mismatch; this is expected for non-live mode", - ), - } - } - - Ok(tracer.into_events()) -} - -#[allow(dead_code)] -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub enum TraceEvent { - Call { - /// Which receipt this call corresponds to. - index: usize, - /// Method being called. - method: String, - /// Arguments being passed to the method. - arguments: Option>, - /// Contract being called - to: ContractId, - /// Amount being transferred - amount: u64, - /// Gas for the call - gas: u64, - }, - Return { - index: usize, - /// Contract that returned - id: ContractId, - /// Return value (raw) - val: u64, - }, - ReturnData { - index: usize, - /// Contract that returned data - id: ContractId, - /// Return data; decoded if ABI provided, otherwise hex encoded - data: String, - }, - Panic { - index: usize, - /// Contract that panicked - id: ContractId, - /// Panic reason - reason: String, - /// Contract ID associated with the panic, if any - contract_id: Option, - }, - Revert { - index: usize, - /// Contract that reverted - id: ContractId, - /// Revert value - ra: u64, - revert_info: Option, - }, - Log { - index: usize, - /// Contract that logged - id: ContractId, - /// Log values - ra: u64, - rb: u64, - rc: u64, - rd: u64, - }, - LogData { - index: usize, - /// Contract that logged data - id: ContractId, - /// Decoded log data value - value: Option, - /// Data length - len: u64, - }, - Transfer { - index: usize, - /// Source contract - id: ContractId, - /// Destination (either contract or address) - to: String, - /// Amount transferred - amount: u64, - /// Asset ID - asset_id: String, - }, - ScriptResult { - index: usize, - /// Script execution result - result: ScriptExecutionResult, - /// Gas used - gas_used: u64, - }, - MessageOut { - index: usize, - /// Sender address - sender: String, - /// Recipient address - recipient: String, - /// Nonce - nonce: u64, - /// Digest - digest: String, - /// Amount - amount: u64, - /// Message data (hex encoded) - data: Option, - }, - Mint { - index: usize, - /// Contract that minted - contract_id: ContractId, - /// Sub asset ID - asset_id: String, - /// Amount minted - val: u64, - }, - Burn { - index: usize, - /// Contract that burned - contract_id: ContractId, - /// Sub asset ID - asset_id: String, - /// Amount burned - val: u64, - }, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct PanicLocation { - pub function: String, - pub pkg: String, - pub file: String, - pub line: u64, - pub column: u64, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct RevertInfoSummary { - pub revert_code: u64, - pub message: Option, - pub value: Option, - pub location: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub backtrace: Vec, - pub is_known_error: bool, - pub is_raw: bool, -} - -impl From for RevertInfoSummary { - fn from(info: RevertInfo) -> Self { - match info.kind { - RevertKind::RawRevert => Self { - revert_code: info.revert_code, - message: None, - value: None, - location: None, - backtrace: vec![], - is_known_error: false, - is_raw: true, - }, - RevertKind::KnownErrorSignal { err_msg } => Self { - revert_code: info.revert_code, - message: Some(err_msg), - value: None, - location: None, - backtrace: vec![], - is_known_error: true, - is_raw: false, - }, - RevertKind::Panic { - err_msg, - err_val, - pos, - backtrace, - } => Self { - revert_code: info.revert_code, - message: err_msg, - value: err_val, - location: Some(PanicLocation { - function: pos.function, - pkg: pos.pkg, - file: pos.file, - line: pos.line, - column: pos.column, - }), - backtrace, - is_known_error: false, - is_raw: false, - }, - } - } -} - -pub fn first_revert_info(events: &[TraceEvent]) -> Option<(ContractId, RevertInfoSummary)> { - events.iter().find_map(|e| { - if let TraceEvent::Revert { - id, - revert_info: Some(info), - .. - } = e - { - Some((*id, info.clone())) - } else { - None - } - }) -} - -fn decode_revert_info( - receipts: &[Receipt], - abis: &HashMap, - contract_id: ContractId, - revert_code: u64, -) -> Option { - let program_abi = abis.get(&contract_id).map(|abi| &abi.program); - let info = - forc_util::tx_utils::revert_info_from_receipts(receipts, program_abi, Some(revert_code))?; - if info.revert_code != revert_code { - return None; - } - Some(RevertInfoSummary::from(info)) -} - -/// Format transaction trace events into a hierarchical trace visualization. -/// This function processes trace events sequentially and displays them with proper indentation -/// based on call depth, similar to the original format_transaction_trace function. -pub fn display_transaction_trace( - total_gas: u64, - trace_events: &[TraceEvent], - labels: &HashMap, - writer: &mut W, -) -> Result<()> { - let format_contract_with_label = - |contract_id: ContractId, labels: &HashMap| -> String { - if let Some(label) = labels.get(&contract_id) { - label.to_string() - } else { - format!("0x{contract_id}") - } - }; - - writeln!(writer, "Traces:")?; - writeln!(writer, " [Script]")?; - - let mut depth = 0; - for event in trace_events { - let indent = if depth > 0 { - " │".repeat(depth) - } else { - "".to_string() - }; - match event { - TraceEvent::Call { - to, - gas, - method, - arguments, - .. - } => { - writeln!( - writer, - "{} ├─ [{}] {}{}{}({})", - indent, - gas, - Color::Green.paint(format_contract_with_label(*to, labels)), - Color::DarkGray.paint("::"), - method, - Color::DarkGray.paint(arguments.as_ref().unwrap_or(&vec![]).join(", ")) - )?; - depth += 1; - } - TraceEvent::ReturnData { data, .. } => { - writeln!( - writer, - "{} └─ ← {}", - indent, - Color::BrightCyan.paint(data), - )?; - depth = depth.saturating_sub(1); - } - TraceEvent::Return { val, .. } => { - writeln!(writer, "{indent} └─ ← [Return] val: {val}")?; - depth = depth.saturating_sub(1); - } - TraceEvent::LogData { value, .. } => { - if let Some(log_value) = value { - writeln!( - writer, - "{} ├─ emit {}", - indent, - Color::BrightCyan.paint(log_value) - )?; - } else { - writeln!(writer, "{indent} ├─ emit ()")?; - } - } - TraceEvent::Revert { revert_info, .. } => { - writeln!( - writer, - "{} └─ ← {}", - indent, - Color::Red.paint("[Revert]") - )?; - depth = depth.saturating_sub(1); - if let Some(details) = revert_info { - write_revert_trace_details(writer, &indent, details)?; - } - } - TraceEvent::Panic { reason, .. } => { - writeln!( - writer, - "{} └─ ← {} {}", - indent, - Color::Red.paint("[Panic]"), - Color::Red.paint(reason) - )?; - depth = depth.saturating_sub(1); - } - TraceEvent::Transfer { - amount, - asset_id, - to, - .. - } => { - writeln!( - writer, - "{indent} ├─ [Transfer] to:{to} asset_id:{asset_id} amount:{amount}" - )?; - } - TraceEvent::Mint { asset_id, val, .. } => { - writeln!( - writer, - "{indent} ├─ [Mint] asset_id:{asset_id} val:{val}" - )?; - } - TraceEvent::Burn { asset_id, val, .. } => { - writeln!( - writer, - "{indent} ├─ [Burn] asset_id:{asset_id} val:{val}" - )?; - } - - TraceEvent::Log { rb, .. } => { - writeln!(writer, "{indent} ├─ [Log] rb: 0x{rb:x}")?; - } - TraceEvent::MessageOut { - amount, - recipient, - nonce, - digest, - data, - .. - } => { - writeln!( - writer, - "{} ├─ [MessageOut] recipient:{} amount:{} nonce:{} digest:{} data:{}", - indent, - recipient, - amount, - nonce, - digest, - data.clone().unwrap_or("()".to_string()) - )?; - } - TraceEvent::ScriptResult { - result, gas_used, .. - } => { - writeln!( - writer, - " [ScriptResult] result: {result:?}, gas_used: {gas_used}" - )?; - writeln!(writer)?; - - match result { - ScriptExecutionResult::Success => writeln!( - writer, - "{}", - Color::Green.paint("Transaction successfully executed.") - )?, - _ => writeln!(writer, "{}", Color::Red.paint("Transaction failed."))?, - } - } - } - } - writeln!(writer, "Gas used: {total_gas}")?; - Ok(()) -} - -fn write_revert_trace_details( - writer: &mut W, - indent: &str, - details: &RevertInfoSummary, -) -> Result<()> { - let detail_prefix = format!("{indent} "); - let write_detail_line = |writer: &mut W, symbol: &str, text: String| -> Result<()> { - writeln!( - writer, - "{}{}", - detail_prefix, - Color::Red.paint(format!("{symbol} {text}")) - ) - .map_err(Into::into) - }; - - let has_more_details = !details.is_raw; - write_detail_line( - writer, - if has_more_details { "├─" } else { "└─" }, - format!("revert code: {:x}", details.revert_code), - )?; - - if details.is_known_error { - if let Some(msg) = &details.message { - write_detail_line(writer, "└─", format!("error message: {msg}"))?; - } - } else if !details.is_raw { - if let Some(err_msg) = &details.message { - write_detail_line(writer, "├─", format!("panic message: {err_msg}"))?; - } - if let Some(err_val) = &details.value { - write_detail_line(writer, "├─", format!("panic value: {err_val}"))?; - } - - if let Some(location) = &details.location { - let filtered_backtrace: Vec<_> = details - .backtrace - .iter() - .filter(|call| { - !call.pos.function.ends_with("::__entry") && call.pos.function != "__entry" - }) - .collect(); - - let branch_symbol = if filtered_backtrace.is_empty() { - "└─" - } else { - "├─" - }; - let location_prefix = if filtered_backtrace.is_empty() { - format!("{indent} ") - } else { - format!("{indent} {} ", Color::Red.paint("│")) - }; - - write_detail_line( - writer, - branch_symbol, - format!("panicked: in {}", location.function), - )?; - let loc_line = Color::Red.paint(format!( - " └─ at {}, {}:{}:{}", - location.pkg, location.file, location.line, location.column - )); - writeln!(writer, "{}{}", location_prefix, loc_line)?; - - if let Some((first, rest)) = filtered_backtrace.split_first() { - let line_prefix = format!("{indent} "); - write_backtrace_call(writer, &line_prefix, first, true)?; - for call in rest { - write_backtrace_call(writer, &line_prefix, call, false)?; - } - } - } - } - Ok(()) -} - -fn write_backtrace_call( - writer: &mut W, - indent_detail: &str, - call: &PanickingCall, - is_first: bool, -) -> Result<()> { - // Backtrace lines share the same indent; only the first includes the header. - let header_prefix = format!("{indent_detail}{}", Color::Red.paint("└─ backtrace: ")); - if is_first { - writeln!( - writer, - "{header_prefix}{}", - Color::Red.paint(format!("called in {}", call.pos.function)) - )?; - writeln!( - writer, - "{indent_detail} {}", - Color::Red.paint(format!( - "└─ at {}, {}:{}:{}", - call.pos.pkg, call.pos.file, call.pos.line, call.pos.column - )) - )?; - } else { - writeln!( - writer, - "{indent_detail} {}", - Color::Red.paint(format!("called in {}", call.pos.function)) - )?; - writeln!( - writer, - "{indent_detail} {}", - Color::Red.paint(format!( - "└─ at {}, {}:{}:{}", - call.pos.pkg, call.pos.file, call.pos.line, call.pos.column - )) - )?; - }; - Ok(()) -} - -pub type Vm = - Interpreter; - -pub struct CallRetTracer<'a> { - abis: &'a HashMap, - return_type_callstack: Vec, - events: Vec, -} - -enum StackFrame { - KnownAbi(ParamType), - UnknownAbi, -} - -impl<'a> CallRetTracer<'a> { - pub fn new(abis: &'a HashMap) -> Self { - Self { - abis, - return_type_callstack: Vec::new(), - events: Vec::new(), - } - } - - pub fn process_vm_state(&mut self, vm: &Vm) -> Result<()> { - let start_index = self.events.len(); - let decoder = ABIDecoder::new(DecoderConfig::default()); - - for (i, receipt) in vm.receipts().iter().enumerate().skip(start_index) { - let index = i + start_index; - let event = match receipt { - Receipt::Call { - to, - param1, - param2, - amount, - gas, - .. - } => { - let method = match decoder - .decode(&ParamType::String, MemoryReader::new(vm.memory(), *param1)) - { - Ok(Token::String(method)) => Some(method), - _ => None, - }; - - let arguments = if let Some((parameters, returns)) = method - .as_ref() - .and_then(|m| get_function_signature(self.abis.get(to)?, m.as_str())) - { - self.return_type_callstack - .push(StackFrame::KnownAbi(returns)); - let args_reader = MemoryReader::new(vm.memory(), *param2); - decoder - .decode_multiple_as_debug_str(parameters.as_slice(), args_reader) - .ok() - } else { - self.return_type_callstack.push(StackFrame::UnknownAbi); - None - }; - - TraceEvent::Call { - index, - method: method.unwrap_or("unknown".to_string()), - arguments, - to: *to, - amount: *amount, - gas: *gas, - } - } - - Receipt::Return { id, val, .. } => { - if !self.return_type_callstack.is_empty() { - let _ = self.return_type_callstack.pop().unwrap(); - } - TraceEvent::Return { - index, - id: *id, - val: *val, - } - } - - Receipt::ReturnData { id, ptr, data, .. } => { - let return_value = match self.return_type_callstack.pop() { - Some(StackFrame::KnownAbi(return_type)) => { - let reader = MemoryReader::new(vm.memory(), *ptr); - decoder - .decode_as_debug_str(&return_type, reader) - .unwrap_or_else(|_| match data { - Some(data) if !data.is_empty() => { - format!("0x{}", hex::encode(data)) - } - _ => "()".to_string(), - }) - } - Some(StackFrame::UnknownAbi) | None => match data { - // hex encode the data if available - Some(data) if !data.is_empty() => format!("0x{}", hex::encode(data)), - _ => "()".to_string(), - }, - }; - - TraceEvent::ReturnData { - index, - data: return_value, - id: *id, - } - } - - Receipt::Panic { - id, - reason, - contract_id, - .. - } => TraceEvent::Panic { - index, - id: *id, - reason: format!("{:?}", reason.reason()), - contract_id: *contract_id, - }, - - Receipt::Revert { id, ra, .. } => TraceEvent::Revert { - index, - id: *id, - ra: *ra, - revert_info: decode_revert_info(vm.receipts(), self.abis, *id, *ra), - }, - - Receipt::Log { - id, ra, rb, rc, rd, .. - } => TraceEvent::Log { - index, - id: *id, - ra: *ra, - rb: *rb, - rc: *rc, - rd: *rd, - }, - - Receipt::LogData { - id, rb, len, data, .. - } => { - let data_str = match data { - Some(data) => { - let hex_str = format!("0x{}", hex::encode(data)); - match self.abis.get(id) { - Some(abi) => { - let program_abi = sway_core::asm_generation::ProgramABI::Fuel( - abi.program.clone(), - ); - forc_util::tx_utils::decode_log_data( - &rb.to_string(), - data, - &program_abi, - ) - .ok() - .map(|decoded| decoded.value) - } - None => Some(hex_str), - } - } - None => None, - }; - TraceEvent::LogData { - index, - value: data_str, - id: *id, - len: *len, - } - } - - Receipt::Transfer { - id, - to, - amount, - asset_id, - .. - } => TraceEvent::Transfer { - index, - id: *id, - to: format!("0x{to}"), - amount: *amount, - asset_id: format!("0x{asset_id}"), - }, - - Receipt::TransferOut { - id, - to, - amount, - asset_id, - .. - } => TraceEvent::Transfer { - index, - id: *id, - to: format!("0x{to}"), - amount: *amount, - asset_id: format!("0x{asset_id}"), - }, - - Receipt::ScriptResult { result, gas_used } => TraceEvent::ScriptResult { - index, - result: *result, - gas_used: *gas_used, - }, - - Receipt::MessageOut { - sender, - recipient, - amount, - data, - .. - } => { - let data_hex = data.as_ref().map(|d| format!("0x{}", hex::encode(d))); - TraceEvent::MessageOut { - index, - sender: format!("0x{sender}"), - recipient: format!("0x{recipient}"), - amount: *amount, - data: data_hex, - nonce: 0, - digest: - "0x0000000000000000000000000000000000000000000000000000000000000000" - .to_string(), - } - } - - Receipt::Mint { - contract_id, - sub_id, - val, - .. - } => TraceEvent::Mint { - index, - contract_id: *contract_id, - asset_id: format!("0x{sub_id}"), - val: *val, - }, - - Receipt::Burn { - contract_id, - sub_id, - val, - .. - } => TraceEvent::Burn { - index, - contract_id: *contract_id, - asset_id: format!("0x{sub_id}"), - val: *val, - }, - }; - self.events.push(event); - } - - Ok(()) - } - - pub fn into_events(self) -> Vec { - self.events - } -} - -/// Extract function signature (parameters and return type) from ABI -fn get_function_signature(abi: &Abi, method: &str) -> Option<(Vec, ParamType)> { - let func = abi.unified.functions.iter().find(|f| f.name == *method)?; - - let mut parameters = Vec::new(); - for param in &func.inputs { - parameters.push(ParamType::try_from_type_application(param, &abi.type_lookup).ok()?); - } - - let returns = ParamType::try_from_type_application(&func.output, &abi.type_lookup).ok()?; - Some((parameters, returns)) -} - -#[cfg(test)] -mod tests { - use super::*; - use fuel_tx::ScriptExecutionResult; - use fuels_core::types::ContractId; - use std::str::FromStr; - - // Compare the results, ignoring whitespace differences and colors - fn normalize(s: &str) -> String { - // Remove ANSI color codes - let re = regex::Regex::new(r"\x1b\[[0-9;]*m").unwrap(); - let s = re.replace_all(s, ""); - s.split_whitespace().collect::>().join(" ") - } - - #[test] - fn writes_revert_details_without_backtrace() { - let summary = RevertInfoSummary { - revert_code: 0xbeef, - message: Some("boom".to_string()), - value: Some("Value".to_string()), - location: Some(PanicLocation { - function: "ctx::fn".to_string(), - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 1, - column: 1, - }), - backtrace: vec![], - is_known_error: false, - is_raw: false, - }; - - let mut buf = Vec::new(); - write_revert_trace_details(&mut buf, "", &summary).unwrap(); - let out = normalize(&String::from_utf8(buf).unwrap()); - - let expected = normalize( - r#" -├─ revert code: beef -├─ panic message: boom -├─ panic value: Value -└─ panicked: in ctx::fn - └─ at pkg, file.sw:1:1 -"#, - ); - - assert_eq!(out, expected); - } - - #[test] - fn writes_revert_details_with_backtrace() { - let summary = RevertInfoSummary { - revert_code: 0xdead, - message: None, - value: None, - location: Some(PanicLocation { - function: "ctx::fn".to_string(), - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 1, - column: 1, - }), - backtrace: vec![ - PanickingCall { - pos: fuel_abi_types::abi::program::ErrorPosition { - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 2, - column: 3, - function: "caller_fn".to_string(), - }, - function: "caller_fn".to_string(), - }, - PanickingCall { - pos: fuel_abi_types::abi::program::ErrorPosition { - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 4, - column: 5, - function: "root_fn".to_string(), - }, - function: "root_fn".to_string(), - }, - ], - is_known_error: false, - is_raw: false, - }; - - let mut buf = Vec::new(); - write_revert_trace_details(&mut buf, "│ ", &summary).unwrap(); - let out = normalize(&String::from_utf8(buf).unwrap()); - - let expected = normalize( - r#" -│ ├─ revert code: dead -│ ├─ panicked: in ctx::fn -│ │ └─ at pkg, file.sw:1:1 -│ └─ backtrace: called in caller_fn -│ └─ at pkg, file.sw:2:3 -│ called in root_fn -│ └─ at pkg, file.sw:4:5 -"#, - ); - - assert_eq!(out, expected); - } - - #[test] - fn test_display_transaction_trace_revert() { - let contract1_id = ContractId::from_str( - "4211b7b7a0c3104e6b9450b7a9e1b7f61912c57c3b319a956d5d7f95b480eb8e", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "f6035b8ac5ad76c228784d03fbba08545820715e811f574ff77300eab5e1aee9", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 46590, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 34124, - }, - TraceEvent::LogData { - index: 2, - id: contract2_id, - value: Some("0x0000000000000001".to_string()), - len: 8, - }, - TraceEvent::Revert { - index: 3, - id: contract2_id, - ra: 0, - revert_info: None, - }, - TraceEvent::ScriptResult { - index: 4, - result: ScriptExecutionResult::Revert, - gas_used: 37531, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [46590] 0x4211b7b7a0c3104e6b9450b7a9e1b7f61912c57c3b319a956d5d7f95b480eb8e::unknown() - │ ├─ [34124] 0xf6035b8ac5ad76c228784d03fbba08545820715e811f574ff77300eab5e1aee9::unknown() - │ │ ├─ emit 0x0000000000000001 - │ │ └─ ← [Revert] - [ScriptResult] result: Revert, gas_used: 37531 - - Transaction failed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_simple_call() { - let contract_id = ContractId::from_str( - "2af09151f8276611ba65f14650970657bc42c1503d6502ffbb4d085ec37065dd", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract_id, - amount: 0, - gas: 8793, - }, - TraceEvent::ReturnData { - index: 1, - id: contract_id, - data: "0x00000000000000000000000000000001".to_string(), - }, - TraceEvent::Return { - index: 2, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 3, - result: ScriptExecutionResult::Success, - gas_used: 12400, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [8793] 0x2af09151f8276611ba65f14650970657bc42c1503d6502ffbb4d085ec37065dd::unknown() - │ └─ ← 0x00000000000000000000000000000001 - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 12400 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_simple_call_log() { - let contract_id = ContractId::from_str( - "4a89a8fb150bf814a6610e1172baef6c68e4e273fce379fa9b30c75f584a697e", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract_id, - amount: 0, - gas: 28311, - }, - TraceEvent::LogData { - index: 1, - id: contract_id, - value: Some("0x00000000000000000000000000000001".to_string()), - len: 8, - }, - TraceEvent::ReturnData { - index: 2, - id: contract_id, - data: "()".to_string(), - }, - TraceEvent::Return { - index: 3, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 4, - result: ScriptExecutionResult::Success, - gas_used: 25412, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [28311] 0x4a89a8fb150bf814a6610e1172baef6c68e4e273fce379fa9b30c75f584a697e::unknown() - │ ├─ emit 0x00000000000000000000000000000001 - │ └─ ← () - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 25412 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_call_mint_transfer_burn() { - let contract_id = ContractId::from_str( - "5598f77f568631ad7e37e1d88b248d0c5002705ae4582fd544c9a87662a6af03", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract_id, - amount: 100, - gas: 46023, - }, - TraceEvent::Mint { - index: 1, - contract_id, - asset_id: "0000000000000000000000000000000000000000000000000000000000000000" - .to_string(), - val: 100, - }, - TraceEvent::Transfer { - index: 2, - id: contract_id, - to: "de97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c".to_string(), - amount: 100, - asset_id: "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - .to_string(), - }, - TraceEvent::Burn { - index: 3, - contract_id, - asset_id: "0000000000000000000000000000000000000000000000000000000000000000" - .to_string(), - val: 100, - }, - TraceEvent::ReturnData { - index: 4, - id: contract_id, - data: "()".to_string(), - }, - TraceEvent::Return { - index: 5, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 6, - result: ScriptExecutionResult::Success, - gas_used: 37228, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [46023] 0x5598f77f568631ad7e37e1d88b248d0c5002705ae4582fd544c9a87662a6af03::unknown() - │ ├─ [Mint] asset_id:0000000000000000000000000000000000000000000000000000000000000000 val:100 - │ ├─ [Transfer] to:de97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c asset_id:f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 amount:100 - │ ├─ [Burn] asset_id:0000000000000000000000000000000000000000000000000000000000000000 val:100 - │ └─ ← () - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 37228 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_nested_call_log_success() { - let contract1_id = ContractId::from_str( - "7c05fa2efa56c4bba646af0c48db02cba34e54149785cc692ae7e297f031b12e", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "7ecdf7b507b33131cac7295af2156fd98cd299c3512ec8c2733f920d5f8e4506", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 47382, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 34914, - }, - TraceEvent::LogData { - index: 2, - id: contract2_id, - value: Some("0x00000000000000000000000000000001".to_string()), - len: 8, - }, - TraceEvent::ReturnData { - index: 3, - id: contract2_id, - data: "()".to_string(), - }, - TraceEvent::ReturnData { - index: 4, - id: contract1_id, - data: "()".to_string(), - }, - TraceEvent::Return { - index: 5, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 6, - result: ScriptExecutionResult::Success, - gas_used: 38059, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [47382] 0x7c05fa2efa56c4bba646af0c48db02cba34e54149785cc692ae7e297f031b12e::unknown() - │ ├─ [34914] 0x7ecdf7b507b33131cac7295af2156fd98cd299c3512ec8c2733f920d5f8e4506::unknown() - │ │ ├─ emit 0x00000000000000000000000000000001 - │ │ └─ ← () - │ └─ ← () - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 38059 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_nested_call_log_success_with_multiple_calls() { - let contract1_id = ContractId::from_str( - "41a231bd983812dd51e5778751fc679461b8f580357515848b3ac9a297c6e8bc", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "38bf64bfa5ee78b652a36c70eb89fd97caff5ffb419d0abf1199247c168b730c", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 105141, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 92530, - }, - TraceEvent::ReturnData { - index: 2, - id: contract2_id, - data: "()".to_string(), - }, - TraceEvent::LogData { - index: 3, - id: contract1_id, - value: Some("0x00000000000000000000000000000001".to_string()), - len: 25, - }, - TraceEvent::Call { - index: 4, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 67314, - }, - TraceEvent::ReturnData { - index: 5, - id: contract2_id, - data: "0x00000000000000000000000000000002".to_string(), - }, - TraceEvent::LogData { - index: 6, - id: contract1_id, - value: Some("0x00000000000000000000000000000002".to_string()), - len: 8, - }, - TraceEvent::LogData { - index: 7, - id: contract1_id, - value: Some("0x00000000000000000000000000000003".to_string()), - len: 12, - }, - TraceEvent::Call { - index: 8, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 53729, - }, - TraceEvent::ReturnData { - index: 9, - id: contract2_id, - data: "()".to_string(), - }, - TraceEvent::ReturnData { - index: 10, - id: contract1_id, - data: "()".to_string(), - }, - TraceEvent::Return { - index: 11, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 12, - result: ScriptExecutionResult::Success, - gas_used: 76612, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [105141] 0x41a231bd983812dd51e5778751fc679461b8f580357515848b3ac9a297c6e8bc::unknown() - │ ├─ [92530] 0x38bf64bfa5ee78b652a36c70eb89fd97caff5ffb419d0abf1199247c168b730c::unknown() - │ │ └─ ← () - │ ├─ emit 0x00000000000000000000000000000001 - │ ├─ [67314] 0x38bf64bfa5ee78b652a36c70eb89fd97caff5ffb419d0abf1199247c168b730c::unknown() - │ │ └─ ← 0x00000000000000000000000000000002 - │ ├─ emit 0x00000000000000000000000000000002 - │ ├─ emit 0x00000000000000000000000000000003 - │ ├─ [53729] 0x38bf64bfa5ee78b652a36c70eb89fd97caff5ffb419d0abf1199247c168b730c::unknown() - │ │ └─ ← () - │ └─ ← () - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 76612 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_nested_call_log_revert() { - let contract1_id = ContractId::from_str( - "9a7195648cc46c832e490e9bc15ed929fa82801cc0316d1c8e0965bb5e0260a3", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "b56b9921112e2fed854ac85357a4914dab561eed98fed0cbe35c1871971dc129", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 46590, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 34124, - }, - TraceEvent::LogData { - index: 2, - id: contract2_id, - value: Some("0x00000000000000000000000000000001".to_string()), - len: 8, - }, - TraceEvent::Revert { - index: 3, - id: contract2_id, - ra: 0, - revert_info: None, - }, - TraceEvent::ScriptResult { - index: 4, - result: ScriptExecutionResult::Revert, - gas_used: 37531, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - // Expected output with color codes - let expected_output = r#" - Traces: - [Script] - ├─ [46590] 0x9a7195648cc46c832e490e9bc15ed929fa82801cc0316d1c8e0965bb5e0260a3::unknown() - │ ├─ [34124] 0xb56b9921112e2fed854ac85357a4914dab561eed98fed0cbe35c1871971dc129::unknown() - │ │ ├─ emit 0x00000000000000000000000000000001 - │ │ └─ ← [Revert] - [ScriptResult] result: Revert, gas_used: 37531 - - Transaction failed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_nested_call_log_revert_with_info() { - let contract1_id = ContractId::from_str( - "9a7195648cc46c832e490e9bc15ed929fa82801cc0316d1c8e0965bb5e0260a3", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "b56b9921112e2fed854ac85357a4914dab561eed98fed0cbe35c1871971dc129", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 46590, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 34124, - }, - TraceEvent::LogData { - index: 2, - id: contract2_id, - value: Some("0x00000000000000000000000000000001".to_string()), - len: 8, - }, - TraceEvent::Revert { - index: 3, - id: contract2_id, - ra: 0, - revert_info: Some(RevertInfoSummary { - revert_code: 0xdeadbeef, - message: Some("boom".to_string()), - value: None, - location: Some(super::PanicLocation { - function: "panic_fn".to_string(), - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 10, - column: 5, - }), - backtrace: vec![super::PanickingCall { - pos: fuel_abi_types::abi::program::ErrorPosition { - pkg: "pkg".to_string(), - file: "file.sw".to_string(), - line: 20, - column: 7, - function: "caller_fn".to_string(), - }, - function: "caller_fn".to_string(), - }], - is_known_error: false, - is_raw: false, - }), - }, - TraceEvent::ScriptResult { - index: 4, - result: ScriptExecutionResult::Revert, - gas_used: 37531, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [46590] 0x9a7195648cc46c832e490e9bc15ed929fa82801cc0316d1c8e0965bb5e0260a3::unknown() - │ ├─ [34124] 0xb56b9921112e2fed854ac85357a4914dab561eed98fed0cbe35c1871971dc129::unknown() - │ │ ├─ emit 0x00000000000000000000000000000001 - │ │ └─ ← [Revert] - │ │ ├─ revert code: deadbeef - │ │ ├─ panic message: boom - │ │ ├─ panicked: in panic_fn - │ │ │ └─ at pkg, file.sw:10:5 - │ │ └─ backtrace: called in caller_fn - │ │ └─ at pkg, file.sw:20:7 - [ScriptResult] result: Revert, gas_used: 37531 - - Transaction failed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_nested_call_log_panic() { - let contract1_id = ContractId::from_str( - "b09d73495f6c211ff3586a0542d5fe5fbd45a80e1cd2c1a9a787d6865cc65984", - ) - .unwrap(); - let contract2_id = ContractId::from_str( - "75c5015d5243cfd798a7f46eb8cf3338e05197e0a271b43c4703764c82d60080", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "unknown".to_string(), - arguments: None, - to: contract1_id, - amount: 0, - gas: 25156, - }, - TraceEvent::Call { - index: 1, - method: "unknown".to_string(), - arguments: None, - to: contract2_id, - amount: 0, - gas: 12432, - }, - TraceEvent::Panic { - index: 2, - id: contract2_id, - reason: "PanicInstruction { reason: MemoryOwnership, instruction: MCP { dst_addr: 0x13, src_addr: 0x14, len: 0x15 } (bytes: 28 4d 45 40) }".to_string(), - contract_id: None, - }, - TraceEvent::ScriptResult { - index: 3, - result: ScriptExecutionResult::Panic, - gas_used: 23242, - }, - ]; - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &HashMap::new(), &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [25156] 0xb09d73495f6c211ff3586a0542d5fe5fbd45a80e1cd2c1a9a787d6865cc65984::unknown() - │ ├─ [12432] 0x75c5015d5243cfd798a7f46eb8cf3338e05197e0a271b43c4703764c82d60080::unknown() - │ │ └─ ← [Panic] PanicInstruction { reason: MemoryOwnership, instruction: MCP { dst_addr: 0x13, src_addr: 0x14, len: 0x15 } (bytes: 28 4d 45 40) } - [ScriptResult] result: Panic, gas_used: 23242 - - Transaction failed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } - - #[test] - fn test_display_transaction_trace_with_labels() { - let contract_id = ContractId::from_str( - "2af09151f8276611ba65f14650970657bc42c1503d6502ffbb4d085ec37065dd", - ) - .unwrap(); - - let trace_events = vec![ - TraceEvent::Call { - index: 0, - method: "transfer".to_string(), - arguments: Some(vec!["100".to_string(), "0x123".to_string()]), - to: contract_id, - amount: 0, - gas: 8793, - }, - TraceEvent::ReturnData { - index: 1, - id: contract_id, - data: "()".to_string(), - }, - TraceEvent::Return { - index: 2, - id: ContractId::zeroed(), - val: 1, - }, - TraceEvent::ScriptResult { - index: 3, - result: ScriptExecutionResult::Success, - gas_used: 12400, - }, - ]; - - // Create labels map - let mut labels = HashMap::new(); - labels.insert(contract_id, "TokenContract".to_string()); - - let mut output = Vec::new(); - display_transaction_trace(0, &trace_events, &labels, &mut output).unwrap(); - let trace_output = String::from_utf8(output).unwrap(); - - let expected_output = r#" - Traces: - [Script] - ├─ [8793] TokenContract::transfer(100, 0x123) - │ └─ ← () - └─ ← [Return] val: 1 - [ScriptResult] result: Success, gas_used: 12400 - - Transaction successfully executed. - Gas used: 0 - "#; - - assert_eq!( - normalize(&trace_output), - normalize(expected_output), - "\nExpected:\n{expected_output}\n\nActual:\n{trace_output}\n" - ); - } -} diff --git a/forc-plugins/forc-client/src/op/call/trace/storage.rs b/forc-plugins/forc-client/src/op/call/trace/storage.rs deleted file mode 100644 index 181b1a9b956..00000000000 --- a/forc-plugins/forc-client/src/op/call/trace/storage.rs +++ /dev/null @@ -1,433 +0,0 @@ -use fuel_core_storage::column::Column; -use fuel_core_types::{services::executor::StorageReadReplayEvent, tai64::Tai64}; -use fuel_vm::{ - error::{InterpreterError, RuntimeError}, - fuel_storage::{StorageRead, StorageSize, StorageWrite}, - fuel_types::BlockHeight, - prelude::*, - storage::{ - BlobData, ContractsAssetKey, ContractsAssets, ContractsAssetsStorage, ContractsRawCode, - ContractsState, ContractsStateData, ContractsStateKey, InterpreterStorage, - UploadedBytecodes, - }, -}; -use fuels_core::types::U256; -use std::{cell::RefCell, collections::HashMap}; - -type InnerStorage = HashMap, Option>>>; - -#[derive(Clone)] -pub struct ShallowStorage { - pub block_height: BlockHeight, - pub timestamp: Tai64, - pub consensus_parameters_version: u32, - pub state_transition_version: u32, - pub coinbase: fuel_vm::prelude::ContractId, - pub storage: RefCell, -} - -impl ShallowStorage { - pub fn initial_storage(reads: Vec) -> InnerStorage { - let mut storage: InnerStorage = HashMap::new(); - for read in reads { - let column = Column::try_from(read.column).expect("Invalid column id in read event"); - storage - .entry(column) - .or_default() - .insert(read.key, read.value); - } - storage - } - - fn value_of_column(&self, column: Column, key: Vec) -> Option> { - self.storage.borrow().get(&column)?.get(&key)?.clone() - } - - fn replace_column( - &self, - column: Column, - key: Vec, - value: Option>, - ) -> Option> { - self.storage - .borrow_mut() - .entry(column) - .or_default() - .insert(key.clone(), value)? - } -} - -macro_rules! storage_rw { - ($vm_type:ident, $convert_key:expr, $convert_value:expr, $convert_value_back:expr $(,)?) => { - storage_rw!( - $vm_type = $vm_type, - $convert_key, - $convert_value, - $convert_value_back - ); - }; - ($vm_type:ident = $core_column:ident, $convert_key:expr, $convert_value:expr, $convert_value_back:expr $(,)?) => { - impl StorageSize<$vm_type> for ShallowStorage { - fn size_of_value( - &self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result, Self::Error> { - tracing::debug!( - "{:?} size_of_value {}", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ); - let head = self.value_of_column(Column::$core_column, $convert_key(key)); - Ok(head.map(|v| v.len())) - } - } - - impl StorageInspect<$vm_type> for ShallowStorage { - type Error = Error; - - fn get( - &self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result< - Option::OwnedValue>>, - Self::Error, - > { - tracing::debug!( - "{} get {}", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ); - let head = self.value_of_column(Column::$core_column, $convert_key(key)); - Ok(head.map($convert_value).map(std::borrow::Cow::Owned)) - } - - fn contains_key( - &self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result { - tracing::debug!( - "{} contains_key {}", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ); - let head = self.value_of_column(Column::$core_column, $convert_key(key)); - Ok(head.is_some()) - } - } - - impl StorageRead<$vm_type> for ShallowStorage { - fn read( - &self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - offset: usize, - buf: &mut [u8], - ) -> Result { - tracing::debug!( - "{} read {}", - stringify!($core_column), - hex::encode(&$convert_key(key)), - ); - let head = self.value_of_column(Column::$core_column, $convert_key(key)); - let Some(value) = head else { - return Ok(false); - }; - - if offset > value.len() || offset.saturating_add(buf.len()) > value.len() { - return Err(Error::CannotRead); - } - buf.copy_from_slice(&value[offset..][..buf.len()]); - Ok(true) - } - - fn read_alloc( - &self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result>, Self::Error> { - todo!( - "{} read_alloc {}", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ) - } - } - - impl StorageMutate<$vm_type> for ShallowStorage { - fn replace( - &mut self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - value: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Value, - ) -> Result< - Option<<$vm_type as fuel_vm::fuel_storage::Mappable>::OwnedValue>, - Self::Error, - > { - tracing::debug!( - "{} replace {} (value={value:?})", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ); - Ok(self - .replace_column( - Column::$core_column, - $convert_key(key), - Some($convert_value_back(value)), - ) - .map($convert_value)) - } - - fn take( - &mut self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result< - Option<<$vm_type as fuel_vm::fuel_storage::Mappable>::OwnedValue>, - Self::Error, - > { - tracing::debug!( - "{} take {}", - stringify!($core_column), - hex::encode(&$convert_key(key)) - ); - Ok(self - .replace_column(Column::$core_column, $convert_key(key), None) - .map($convert_value)) - } - } - - impl StorageWrite<$vm_type> for ShallowStorage { - fn write_bytes( - &mut self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - _buf: &[u8], - ) -> Result<(), Self::Error> { - todo!("write_bytes {key:?}") - } - - fn replace_bytes( - &mut self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - _buf: &[u8], - ) -> Result>, Self::Error> { - tracing::debug!("{} replace_bytes {key:?}", stringify!($core_column)); - Ok(self.replace_column(Column::$core_column, $convert_key(key), None)) - } - - fn take_bytes( - &mut self, - key: &<$vm_type as fuel_vm::fuel_storage::Mappable>::Key, - ) -> Result>, Self::Error> { - todo!("take_bytes {key:?}") - } - } - }; -} - -storage_rw!( - ContractsRawCode, - |key: &ContractId| -> Vec { (**key).to_vec() }, - |data| todo!("ContractsRawCode from bytes {data:?}"), - |data| -> Vec { todo!("ContractsRawCode to bytes {data:?}") }, -); -storage_rw!( - ContractsState, - |key: &ContractsStateKey| -> Vec { key.as_ref().into() }, - |data| { ContractsStateData(data.into()) }, - |data: &[u8]| -> Vec { data.to_vec() }, -); -storage_rw!( - ContractsAssets, - |key: &ContractsAssetKey| -> Vec { key.as_ref().into() }, - |data| { - assert_eq!(data.len(), 8); - let mut buffer = [0u8; 8]; - buffer.copy_from_slice(&data); - u64::from_be_bytes(buffer) - }, - |data: &u64| -> Vec { data.to_be_bytes().to_vec() }, -); -storage_rw!( - UploadedBytecodes, - |key: &Bytes32| -> Vec { key.as_ref().into() }, - |data| todo!("UploadedBytecodes from bytes {data:?}"), - |data| -> Vec { todo!("UploadedBytecodes to bytes {data:?}") }, -); -storage_rw!( - BlobData = Blobs, - |key: &BlobId| -> Vec { key.as_ref().into() }, - |data| todo!("BlobData from bytes {data:?}"), - |data| -> Vec { todo!("BlobData to bytes {data:?}") }, -); - -impl ContractsAssetsStorage for ShallowStorage {} - -#[derive(Debug)] -pub enum Error { - /// This block couldn't have been included - InvalidBlock, - /// The requested key is out of the available keyspace - KeyspaceOverflow, - /// Read offset too large, or buffer too small - CannotRead, -} -impl From for RuntimeError { - fn from(e: Error) -> Self { - RuntimeError::Storage(e) - } -} -impl From for InterpreterError { - fn from(e: Error) -> Self { - InterpreterError::Storage(e) - } -} - -impl InterpreterStorage for ShallowStorage { - type DataError = Error; - - fn block_height(&self) -> Result { - Ok(self.block_height) - } - - fn consensus_parameters_version(&self) -> Result { - Ok(self.consensus_parameters_version) - } - - fn state_transition_version(&self) -> Result { - Ok(self.state_transition_version) - } - - fn timestamp( - &self, - height: fuel_vm::fuel_types::BlockHeight, - ) -> Result { - match height { - height if height > self.block_height => Err(Error::InvalidBlock), - height if height == self.block_height => Ok(self.timestamp.0), - height => { - todo!("timestamp {height:?}"); - } - } - } - - fn block_hash( - &self, - block_height: fuel_vm::fuel_types::BlockHeight, - ) -> Result { - // Block header hashes for blocks with height greater than or equal to current block height are zero (0x00**32). - // https://docs.fuel.network/docs/specs/fuel-vm/instruction-set/#bhsh-block-hash - if block_height >= self.block_height || block_height == Default::default() { - Ok(Bytes32::zeroed()) - } else { - todo!("block_hash {block_height:?}"); - } - } - - fn coinbase(&self) -> Result { - Ok(self.coinbase) - } - - fn set_consensus_parameters( - &mut self, - _version: u32, - _consensus_parameters: &fuel_vm::prelude::ConsensusParameters, - ) -> Result, Self::DataError> { - unreachable!("Cannot be called by a script"); - } - - fn set_state_transition_bytecode( - &mut self, - _version: u32, - _hash: &fuel_vm::prelude::Bytes32, - ) -> Result, Self::DataError> { - unreachable!("Cannot be called by a script"); - } - - fn contract_state_range( - &self, - id: &fuel_vm::prelude::ContractId, - start_key: &fuel_vm::prelude::Bytes32, - range: usize, - ) -> Result>>, Self::DataError> - { - tracing::debug!("contract_state_range {id:?} {start_key:?} {range:?}"); - - let mut results = Vec::new(); - let mut key = U256::from_big_endian(start_key.as_ref()); - let mut key_buffer = Bytes32::zeroed(); - for offset in 0..(range as u64) { - if offset != 0 { - key = key.checked_add(1.into()).ok_or(Error::KeyspaceOverflow)?; - } - - key.to_big_endian(key_buffer.as_mut()); - let state_key = ContractsStateKey::new(id, &key_buffer); - let value = self - .storage::() - .get(&state_key)?; - results.push(value); - } - Ok(results) - } - - fn contract_state_insert_range<'a, I>( - &mut self, - contract: &fuel_vm::prelude::ContractId, - start_key: &fuel_vm::prelude::Bytes32, - values: I, - ) -> Result - where - I: Iterator, - { - tracing::debug!("contract_state_insert_range {contract:?} {start_key:?}"); - - let values: Vec<_> = values.collect(); - let mut key = U256::from_big_endian(start_key.as_ref()); - let mut key_buffer = Bytes32::zeroed(); - - let mut found_unset = 0u32; - for (idx, value) in values.iter().enumerate() { - if idx != 0 { - key = key.checked_add(1.into()).ok_or(Error::KeyspaceOverflow)?; - } - - key.to_big_endian(key_buffer.as_mut()); - let option = self.storage::().replace( - &(contract, Bytes32::from_bytes_ref(&key_buffer)).into(), - value, - )?; - - if option.is_none() { - found_unset += 1; - } - } - - Ok(found_unset as usize) - } - - fn contract_state_remove_range( - &mut self, - contract: &fuel_vm::prelude::ContractId, - start_key: &fuel_vm::prelude::Bytes32, - range: usize, - ) -> Result, Self::DataError> { - tracing::debug!("contract_state_remove_range {contract:?} {start_key:?}"); - - let mut key = U256::from_big_endian(start_key.as_ref()); - let mut key_buffer = Bytes32::zeroed(); - - let mut found_unset = false; - for idx in 0..range { - if idx != 0 { - key = key.checked_add(1.into()).ok_or(Error::KeyspaceOverflow)?; - } - - key.to_big_endian(key_buffer.as_mut()); - let option = self - .storage::() - .take(&(contract, Bytes32::from_bytes_ref(&key_buffer)).into())?; - - if option.is_none() { - found_unset = true; - } - } - - Ok(if found_unset { None } else { Some(()) }) - } -} diff --git a/forc-plugins/forc-client/src/op/call/transfer.rs b/forc-plugins/forc-client/src/op/call/transfer.rs deleted file mode 100644 index 5ef9ced90ed..00000000000 --- a/forc-plugins/forc-client/src/op/call/transfer.rs +++ /dev/null @@ -1,185 +0,0 @@ -use anyhow::anyhow; -use fuels::accounts::{wallet::Wallet, Account}; -use fuels_core::types::{transaction::TxPolicies, Address, AssetId}; - -pub async fn transfer( - wallet: &Wallet, - recipient: Address, - amount: u64, - asset_id: AssetId, - tx_policies: TxPolicies, - node: &crate::NodeTarget, - writer: &mut impl std::io::Write, -) -> anyhow::Result { - let provider = wallet.provider(); - - // check is recipient is a user - let tx_response = if provider.is_user_account(*recipient).await? { - writeln!( - writer, - "\nTransferring {amount} 0x{asset_id} to recipient address 0x{recipient}...\n" - )?; - wallet - .transfer(recipient, amount, asset_id, tx_policies) - .await - .map_err(|e| anyhow!("Failed to transfer funds to recipient: {}", e))? - } else { - writeln!( - writer, - "\nTransferring {amount} 0x{asset_id} to contract address 0x{recipient}...\n" - )?; - let contract_id = (*recipient).into(); - wallet - .force_transfer_to_contract(contract_id, amount, asset_id, tx_policies) - .await - .map_err(|e| anyhow!("Failed to transfer funds to contract: {}", e))? - }; - - // display tx info - super::display_tx_info( - tx_response.tx_id.to_string(), - None, - &crate::cmd::call::ExecutionMode::Live, - node, - ); - - Ok(super::CallResponse { - tx_hash: tx_response.tx_id.to_string(), - total_gas: tx_response.tx_status.total_gas, - result: None, - receipts: tx_response.tx_status.receipts.to_vec(), - script: None, - trace_events: vec![], - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{op::call::PrivateKeySigner, NodeTarget}; - use fuels::prelude::*; - - #[tokio::test] - async fn test_transfer_function_to_recipient() { - // Launch a local network and set up wallets - let mut wallets = launch_custom_provider_and_get_wallets( - WalletsConfig::new( - Some(2), /* Two wallets */ - Some(1), /* Single coin (UTXO) */ - Some(1_000_000_000), /* Amount per coin */ - ), - None, - None, - ) - .await - .unwrap(); - - let wallet_sender = wallets.pop().unwrap(); - let wallet_recipient = wallets.pop().unwrap(); - let recipient_address = wallet_recipient.address(); - - let provider = wallet_sender.provider(); - let consensus_parameters = provider.consensus_parameters().await.unwrap(); - let base_asset_id = consensus_parameters.base_asset_id(); - - // Test helpers to get balances - let get_recipient_balance = |addr: Address| async move { - provider - .get_asset_balance(&addr, base_asset_id) - .await - .unwrap() - }; - - // Get initial balance of recipient - let initial_balance = get_recipient_balance(wallet_recipient.address()).await; - - // Test parameters - let tx_policies = TxPolicies::default(); - let amount = 100; - let node = NodeTarget { - node_url: Some(provider.url().to_string()), - ..Default::default() - }; - - // should successfully transfer funds - let response = transfer( - &wallet_sender, - recipient_address, - amount, - *base_asset_id, - tx_policies, - &node, - &mut std::io::stdout(), - ) - .await - .unwrap(); - - // Verify response structure - assert!( - !response.tx_hash.is_empty(), - "Transaction hash should be returned" - ); - assert!(response.result.is_none(), "Result should be none"); - - // Verify balance has increased by the transfer amount - assert_eq!( - get_recipient_balance(wallet_recipient.address()).await, - initial_balance + amount as u128, - "Balance should increase by transfer amount" - ); - } - - #[tokio::test] - async fn test_transfer_function_to_contract() { - let (_, id, provider, secret_key) = crate::op::call::tests::get_contract_instance().await; - - let wallet = Wallet::new(PrivateKeySigner::new(secret_key), provider.clone()); - let consensus_parameters = provider.clone().consensus_parameters().await.unwrap(); - let base_asset_id = consensus_parameters.base_asset_id(); - - // Verify initial contract balance - let balance = provider - .get_contract_asset_balance(&id, base_asset_id) - .await - .unwrap(); - assert_eq!(balance, 0, "Balance should be 0"); - - // Test parameters - let tx_policies = TxPolicies::default(); - let amount = 100; - let node = NodeTarget { - node_url: Some(provider.url().to_string()), - ..Default::default() - }; - - // should successfully transfer funds - let response = transfer( - &wallet, - Address::new(id.into()), - amount, - *base_asset_id, - tx_policies, - &node, - &mut std::io::stdout(), - ) - .await - .unwrap(); - - // Verify response structure - assert!( - !response.tx_hash.is_empty(), - "Transaction hash should be returned" - ); - assert!(response.result.is_none(), "Result should be none"); - - // Verify balance has increased by the transfer amount - let balance = provider - .get_contract_asset_balance(&id, base_asset_id) - .await - .unwrap(); - assert_eq!( - balance, amount, - "Balance should increase by transfer amount" - ); - } -} diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs deleted file mode 100644 index 837e5e2d957..00000000000 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ /dev/null @@ -1,1149 +0,0 @@ -use crate::{ - cmd, - constants::TX_SUBMIT_TIMEOUT_MS, - util::{ - account::ForcClientAccount, - pkg::{built_pkgs, create_proxy_contract, update_proxy_address_in_manifest}, - target::Target, - tx::{ - check_and_create_wallet_at_default_path, prompt_forc_wallet_password, select_account, - update_proxy_contract_target, SignerSelectionMode, - }, - }, -}; -use anyhow::{bail, Context, Result}; -use forc::cli::shared::IrCliOpt; -use forc_pkg::{self as pkg, DumpOpts, PackageManifestFile}; -use forc_pkg::{manifest::GenericManifestFile, MemberFilter}; -use forc_tracing::{println_action_green, println_warning}; -use forc_util::default_output_directory; -use forc_wallet::utils::default_wallet_path; -use fuel_abi_types::abi::program::Configurable; -use fuel_core_client::client::types::{ChainInfo, TransactionStatus}; -use fuel_core_client::client::FuelClient; -use fuel_crypto::{fuel_types::ChainId, Hasher}; -use fuel_tx::{Salt, Transaction}; -use fuel_vm::prelude::*; -use fuels::{ - macros::abigen, - programs::{ - contract::{LoadConfiguration, StorageConfiguration}, - executable::Executable, - }, - types::transaction_builders::Blob, -}; -use fuels_accounts::{provider::Provider, Account, ViewOnlyAccount}; -use fuels_core::types::{transaction::TxPolicies, transaction_builders::CreateTransactionBuilder}; -use futures::FutureExt; -use pkg::{BuildProfile, BuiltPackage}; -use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, - str::FromStr, - sync::Arc, - time::Duration, -}; -use sway_core::{asm_generation::ProgramABI, language::parsed::TreeType, BuildTarget, IrCli}; - -/// Default maximum contract size allowed for a single contract. If the target -/// contract size is bigger than this amount, forc-deploy will automatically -/// starts dividing the contract and deploy them in chunks automatically. -/// The value is in bytes -const MAX_CONTRACT_SIZE: usize = 100_000; - -/// Represents a deployed instance of a forc package. -/// Packages other than libraries are deployable through different mechanisms. -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum DeployedPackage { - Contract(DeployedContract), - Script(DeployedExecutable), - Predicate(DeployedExecutable), -} - -/// Represents a deployed contract on the Fuel network. -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] -pub struct DeployedContract { - pub id: fuel_tx::ContractId, - pub proxy: Option, - pub chunked: bool, -} - -/// Represents a deployed executable (script or predicate) on the Fuel network. -/// Executables are deployed as blobs with generated loaders for efficiency. -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] -pub struct DeployedExecutable { - pub bytecode: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ChunkInfo { - index: usize, - size: usize, - hash: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ChunkedDeploymentInfo { - original_contract_size: usize, - max_chunk_size: usize, - total_chunks: usize, - chunks: Vec, - loader_contract_id: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum DeploymentType { - Standard, - Chunked, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DeploymentArtifact { - #[serde(skip_serializing_if = "Option::is_none")] - transaction_id: Option, - salt: String, - network_endpoint: String, - chain_id: ChainId, - contract_id: String, - deployment_size: usize, - deployed_block_height: Option, - deployment_type: DeploymentType, - #[serde(skip_serializing_if = "Option::is_none")] - chunked_deployment_info: Option, -} - -impl DeploymentArtifact { - pub fn to_file( - &self, - output_dir: &Path, - pkg_name: &str, - contract_id: ContractId, - ) -> Result<()> { - if !output_dir.exists() { - std::fs::create_dir_all(output_dir)?; - } - - let deployment_artifact_json = format!("{pkg_name}-deployment-0x{contract_id}"); - let deployments_path = output_dir - .join(deployment_artifact_json) - .with_extension("json"); - let deployments_file = std::fs::File::create(deployments_path)?; - serde_json::to_writer_pretty(&deployments_file, &self)?; - Ok(()) - } -} - -type ContractSaltMap = BTreeMap; - -/// Takes the contract member salt inputs passed via the --salt option, validates them against -/// the manifests and returns a ContractSaltMap (BTreeMap of contract names to salts). -fn validate_and_parse_salts<'a>( - salt_args: &[String], - manifests: impl Iterator, -) -> Result { - let mut contract_salt_map = BTreeMap::default(); - - // Parse all the salt arguments first, and exit if there are errors in this step. - for salt_arg in salt_args { - if let Some((given_contract_name, salt)) = salt_arg.split_once(':') { - let salt = salt - .parse::() - .map_err(|e| anyhow::anyhow!(e)) - .unwrap(); - - if let Some(old) = contract_salt_map.insert(given_contract_name.to_string(), salt) { - bail!("2 salts provided for contract '{given_contract_name}':\n {old}\n {salt}"); - }; - } else { - bail!("Invalid salt provided - salt must be in the form : when deploying a workspace"); - } - } - - for manifest in manifests { - for (dep_name, contract_dep) in manifest.contract_deps() { - let dep_pkg_name = contract_dep.dependency.package().unwrap_or(dep_name); - if let Some(declared_salt) = contract_salt_map.get(dep_pkg_name) { - bail!( - "Redeclaration of salt using the option '--salt' while a salt exists for contract '{}' \ - under the contract dependencies of the Forc.toml manifest for '{}'\n\ - Existing salt: '0x{}',\nYou declared: '0x{}'\n", - dep_pkg_name, - manifest.project_name(), - contract_dep.salt, - declared_salt, - ); - } - } - } - - Ok(contract_salt_map) -} - -/// Depending on the cli options user passed, either returns storage slots from -/// compiled package, or the ones user provided as overrides. -fn resolve_storage_slots( - command: &cmd::Deploy, - compiled: &BuiltPackage, -) -> Result> { - let mut storage_slots = - if let Some(storage_slot_override_file) = &command.override_storage_slots { - let storage_slots_file = std::fs::read_to_string(storage_slot_override_file)?; - let storage_slots: Vec = serde_json::from_str(&storage_slots_file)?; - storage_slots - } else { - compiled.storage_slots.clone() - }; - storage_slots.sort(); - Ok(storage_slots) -} - -/// Creates blobs from the contract to deploy contracts that are larger than -/// maximum contract size. Created blobs are deployed, and a loader contract is -/// generated such that it loads all the deployed blobs, and provides the user -/// a single contract (loader contract that loads the blobs) to call into. -async fn deploy_chunked( - command: &cmd::Deploy, - compiled: &BuiltPackage, - salt: Salt, - account: &ForcClientAccount, - provider: &Provider, - pkg_name: &str, -) -> anyhow::Result { - println_action_green("Deploying", &format!("contract {pkg_name} chunks")); - - let storage_slots = resolve_storage_slots(command, compiled)?; - let node_url = provider.url(); - let client = FuelClient::new(node_url)?; - let chain_info = client.chain_info().await?; - - let blobs = compiled - .bytecode - .bytes - .chunks(MAX_CONTRACT_SIZE) - .map(|chunk| Blob::new(chunk.to_vec())) - .collect(); - - let tx_policies = tx_policies_from_cmd(command); - let contract_id = - fuels::programs::contract::Contract::loader_from_blobs(blobs, salt, storage_slots)? - .deploy(account, tx_policies) - .await? - .contract_id; - - // Create deployment artifact for chunked deployment - create_chunked_deployment_artifact( - contract_id, - salt, - node_url, - chain_info, - compiled, - command, - &compiled.descriptor.manifest_file, - )?; - - Ok(contract_id) -} - -/// Deploys a new proxy contract for the given package. -async fn deploy_new_proxy( - command: &cmd::Deploy, - pkg_name: &str, - pkg_storage_slots: &[StorageSlot], - impl_contract: &fuel_tx::ContractId, - provider: &Provider, - account: &ForcClientAccount, -) -> Result { - abigen!(Contract(name = "ProxyContract", abi = "{\"programType\":\"contract\",\"specVersion\":\"1.1\",\"encodingVersion\":\"1\",\"concreteTypes\":[{\"type\":\"()\",\"concreteTypeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"type\":\"enum standards::src5::AccessError\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\",\"metadataTypeId\":1},{\"type\":\"enum standards::src5::State\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"metadataTypeId\":2},{\"type\":\"enum std::option::Option\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"metadataTypeId\":4,\"typeArguments\":[\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\",\"metadataTypeId\":5},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\",\"metadataTypeId\":6},{\"type\":\"str\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"},{\"type\":\"struct std::contract_id::ContractId\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\",\"metadataTypeId\":9},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\",\"metadataTypeId\":10},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\",\"metadataTypeId\":11}],\"metadataTypes\":[{\"type\":\"b256\",\"metadataTypeId\":0},{\"type\":\"enum standards::src5::AccessError\",\"metadataTypeId\":1,\"components\":[{\"name\":\"NotOwner\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum standards::src5::State\",\"metadataTypeId\":2,\"components\":[{\"name\":\"Uninitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Initialized\",\"typeId\":3},{\"name\":\"Revoked\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum std::identity::Identity\",\"metadataTypeId\":3,\"components\":[{\"name\":\"Address\",\"typeId\":8},{\"name\":\"ContractId\",\"typeId\":9}]},{\"type\":\"enum std::option::Option\",\"metadataTypeId\":4,\"components\":[{\"name\":\"None\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Some\",\"typeId\":7}],\"typeParameters\":[7]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"metadataTypeId\":5,\"components\":[{\"name\":\"CannotReinitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"metadataTypeId\":6,\"components\":[{\"name\":\"CannotUninitialize\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"generic T\",\"metadataTypeId\":7},{\"type\":\"struct std::address::Address\",\"metadataTypeId\":8,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct std::contract_id::ContractId\",\"metadataTypeId\":9,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"metadataTypeId\":10,\"components\":[{\"name\":\"new_proxy_owner\",\"typeId\":2}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"metadataTypeId\":11,\"components\":[{\"name\":\"new_target\",\"typeId\":9}]}],\"functions\":[{\"inputs\":[],\"name\":\"proxy_target\",\"output\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [Option] - The new proxy contract to which all fallback calls will be passed or `None`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[{\"name\":\"new_target\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"}],\"name\":\"set_proxy_target\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Change the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called by the `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_target`: [ContractId] - The new proxy contract to which all fallback calls will be passed.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When not called by `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Write: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\",\"write\"]}]},{\"inputs\":[],\"name\":\"proxy_owner\",\"output\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the owner of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [State] - Represents the state of ownership for this contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[],\"name\":\"initialize_proxy\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Initializes the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method sets the storage values using the values of the configurable constants `INITIAL_TARGET` and `INITIAL_OWNER`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This then allows methods that write to storage to be called.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called once.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When `storage::SRC14.proxy_owner` is not [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `2`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]},{\"inputs\":[{\"name\":\"new_proxy_owner\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\"}],\"name\":\"set_proxy_owner\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Changes proxy ownership to the passed State.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can be used to transfer ownership between Identities or to revoke ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_proxy_owner`: [State] - The new state of the proxy ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the sender is not the current proxy owner.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the new state of the proxy ownership is [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]}],\"loggedTypes\":[{\"logId\":\"4571204900286667806\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\"},{\"logId\":\"2151606668983994881\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\"},{\"logId\":\"2161305517876418151\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\"},{\"logId\":\"4354576968059844266\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\"},{\"logId\":\"10870989709723147660\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\"},{\"logId\":\"10098701174489624218\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"}],\"messagesTypes\":[],\"configurables\":[{\"name\":\"INITIAL_TARGET\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"offset\":13368},{\"name\":\"INITIAL_OWNER\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"offset\":13320}]}",)); - let proxy_dir_output = create_proxy_contract(pkg_name)?; - let address = account.address(); - - // Add the combined storage slots from the original contract and the proxy contract. - let proxy_storage_path = proxy_dir_output.join("proxy-storage_slots.json"); - let storage_configuration = StorageConfiguration::default() - .add_slot_overrides(pkg_storage_slots.iter().cloned()) - .add_slot_overrides_from_file(proxy_storage_path)?; - - let configurables = ProxyContractConfigurables::default() - .with_INITIAL_TARGET(Some(*impl_contract))? - .with_INITIAL_OWNER(State::Initialized(address.into()))?; - - let configuration = LoadConfiguration::default() - .with_storage_configuration(storage_configuration) - .with_configurables(configurables); - - let tx_policies = tx_policies_from_cmd(command); - let proxy_contract_id: ContractId = fuels::programs::contract::Contract::load_from( - proxy_dir_output.join("proxy.bin"), - configuration, - )? - .deploy(account, tx_policies) - .await? - .contract_id; - - let chain_info = provider.chain_info().await?; - let target = Target::from_str(&chain_info.name).unwrap_or_default(); - let contract_url = match target.explorer_url() { - Some(explorer_url) => format!("{explorer_url}/contract/0x"), - None => "".to_string(), - }; - - println_action_green( - "Finished", - &format!("deploying proxy contract for {pkg_name} {contract_url}{proxy_contract_id}"), - ); - - let instance = ProxyContract::new(proxy_contract_id, account.clone()); - instance.methods().initialize_proxy().call().await?; - println_action_green("Initialized", &format!("proxy contract for {pkg_name}")); - Ok(proxy_contract_id) -} - -/// Builds and deploys contracts, scripts, and predicates from the given path or workspace. -/// -/// Contracts are deployed directly, while scripts and predicates are deployed as blobs with generated loaders. -/// -/// Returns a vector of `DeployedPackage` representing all successful deployments. -pub async fn deploy(command: cmd::Deploy) -> Result> { - if command.unsigned { - println_warning("--unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); - } - let curr_dir = if let Some(ref path) = command.pkg.path { - PathBuf::from(path) - } else { - std::env::current_dir()? - }; - let build_opts = build_opts_from_cmd(&command, MemberFilter::default()); - let built_pkgs = built_pkgs(&curr_dir, &build_opts)?; - let mut deployed_packages = Vec::new(); - - let contracts_to_deploy = built_pkgs - .iter() - .filter(|pkg| { - pkg.descriptor - .manifest_file - .check_program_type(&[TreeType::Contract]) - .is_ok() - }) - .cloned() - .collect::>(); - - let scripts_to_deploy = built_pkgs - .iter() - .filter(|pkg| { - pkg.descriptor - .manifest_file - .check_program_type(&[TreeType::Script]) - .is_ok() - }) - .cloned() - .collect::>(); - - let predicates_to_deploy = built_pkgs - .iter() - .filter(|pkg| { - pkg.descriptor - .manifest_file - .check_program_type(&[TreeType::Predicate]) - .is_ok() - }) - .cloned() - .collect::>(); - - if contracts_to_deploy.is_empty() - && scripts_to_deploy.is_empty() - && predicates_to_deploy.is_empty() - { - println_warning("No deployable package was found in the current directory."); - } else { - deployed_packages.extend( - deploy_contracts(&command, &contracts_to_deploy) - .await? - .into_iter() - .map(DeployedPackage::Contract), - ); - deployed_packages.extend( - deploy_executables(&command, &scripts_to_deploy) - .await? - .into_iter() - .map(DeployedPackage::Script), - ); - deployed_packages.extend( - deploy_executables(&command, &predicates_to_deploy) - .await? - .into_iter() - .map(DeployedPackage::Predicate), - ); - } - - Ok(deployed_packages) -} - -/// Builds and deploys executable (script and predicate) package(s) as blobs, -/// and generates a loader for each of them. -pub async fn deploy_executables( - command: &cmd::Deploy, - executables_to_deploy: &[Arc], -) -> Result> { - let mut deployed_executable = vec![]; - if executables_to_deploy.is_empty() { - return Ok(deployed_executable); - } - - let node_url = validate_and_get_node_url(command, executables_to_deploy).await?; - // We will have 1 transaction per executable as each deployment uses a single blob. - let tx_count = executables_to_deploy.len(); - let account = setup_deployment_account(command, &node_url, tx_count).await?; - - for pkg in executables_to_deploy { - let script = Executable::from_bytes(pkg.bytecode.bytes.clone()); - let loader = script.convert_to_loader()?; - println_action_green("Uploading", "blob containing executable bytecode."); - loader.upload_blob(account.clone()).await?; - println_action_green("Generating", "loader bytecode for the uploaded executable."); - let loader_bytecode = loader.code(); - let pkg_name = &pkg.descriptor.name; - let out_dir = pkg.descriptor.manifest_file.dir().join("out"); - let bin_path = out_dir.join(format!("{pkg_name}-loader.bin")); - std::fs::write(&bin_path, &loader_bytecode)?; - println_action_green( - "Saved", - &format!("loader bytecode at {}", bin_path.display()), - ); - - let loader_configurables_offset = loader.configurables_offset_in_code(); - - // Calculate the offset shift to adjust the configurables in the abi. - if let ProgramABI::Fuel(mut fuel_abi) = pkg.program_abi.clone() { - println_action_green("Generating", "loader abi for the uploaded executable."); - let json_abi_path = out_dir.join(format!("{pkg_name}-loader-abi.json")); - let original_configurables_section = - extract_configurables_offset(&pkg.bytecode.bytes).unwrap(); - let offset_shift = original_configurables_section - loader_configurables_offset; - // if there are configurables in the abi we need to shift them by `offset_shift`. - let configurables = fuel_abi.configurables.clone().map(|configs| { - configs - .into_iter() - .map(|config| Configurable { - offset: config.offset - offset_shift as u64, - ..config.clone() - }) - .collect() - }); - fuel_abi.configurables = configurables; - let json_string = serde_json::to_string_pretty(&fuel_abi)?; - std::fs::write(json_abi_path, json_string)?; - } - - // If the executable is a predicate, we also want to display and save the predicate root. - if pkg - .descriptor - .manifest_file - .program_type() - .with_context(|| { - "error while trying to retrieve program type for executable deployment." - })? - == TreeType::Predicate - { - // Calculate the root. - let root = format!("0x{}", fuel_tx::Input::predicate_owner(&loader_bytecode)); - // Root files are named in `pkg-name-root` format, since this is a - // loader we are also adding an identifier to differentiate it from - // the root of the "original" predicate. - let root_file_name = format!("{}-loader-root", &pkg_name); - let root_path = out_dir.join(root_file_name); - std::fs::write(&root_path, &root)?; - println_action_green( - "Saved", - &format!("loader root ({}) at {}", root, root_path.display()), - ); - } - let deployed = DeployedExecutable { - bytecode: loader_bytecode, - }; - deployed_executable.push(deployed); - println_action_green("Finished", &format!("deploying executable {pkg_name}")); - } - Ok(deployed_executable) -} - -// This helper is borrowed from `fuels::programs::assembly` -fn extract_configurables_offset(binary: &[u8]) -> Result { - if binary.len() < 24 { - anyhow::bail!( - "given binary is too short to contain a configurable offset, len: {}", - binary.len() - ); - } - - let configurable_offset: [u8; 8] = binary[16..24].try_into().expect("checked above"); - Ok(u64::from_be_bytes(configurable_offset) as usize) -} - -/// Builds and deploys contract(s). If the given path corresponds to a workspace, all deployable members -/// will be built and deployed. -/// -/// Upon success, returns the ID of each deployed contract in order of deployment. -/// -/// When deploying a single contract, only that contract's ID is returned. -pub async fn deploy_contracts( - command: &cmd::Deploy, - contracts_to_deploy: &[Arc], -) -> Result> { - let mut deployed_contracts = Vec::new(); - - if contracts_to_deploy.is_empty() { - return Ok(deployed_contracts); - } - - let contract_salt_map = if let Some(salt_input) = &command.salt { - // If we're building 1 package, we just parse the salt as a string, ie. 0x00... - // If we're building >1 package, we must parse the salt as a pair of strings, ie. contract_name:0x00... - if contracts_to_deploy.len() > 1 { - let map = validate_and_parse_salts( - salt_input, - contracts_to_deploy - .iter() - .map(|b| &b.descriptor.manifest_file), - )?; - - Some(map) - } else { - if salt_input.len() > 1 { - bail!("More than 1 salt was specified when deploying a single contract"); - } - - // OK to index into salt_input and built_pkgs_with_manifest here, - // since both are known to be len 1. - - let salt = salt_input[0] - .parse::() - .map_err(|e| anyhow::anyhow!(e)) - .unwrap(); - let mut contract_salt_map = ContractSaltMap::default(); - contract_salt_map.insert( - contracts_to_deploy[0] - .descriptor - .manifest_file - .project_name() - .to_string(), - salt, - ); - Some(contract_salt_map) - } - } else { - None - }; - - let node_url = validate_and_get_node_url(command, contracts_to_deploy).await?; - let provider = Provider::connect(node_url.clone()).await?; - - // Confirmation step. Summarize the transaction(s) for the deployment. - let account = confirm_transaction_details( - contracts_to_deploy, - command, - node_url.clone(), - MAX_CONTRACT_SIZE, - ) - .await?; - - for pkg in contracts_to_deploy { - let salt = match (&contract_salt_map, command.default_salt) { - (Some(map), false) => { - if let Some(salt) = map.get(pkg.descriptor.manifest_file.project_name()) { - *salt - } else { - Default::default() - } - } - (None, true) => Default::default(), - (None, false) => rand::random(), - (Some(_), true) => { - bail!("Both `--salt` and `--default-salt` were specified: must choose one") - } - }; - let bytecode_size = pkg.bytecode.bytes.len(); - let deployed_contract_id = if bytecode_size > MAX_CONTRACT_SIZE { - // Deploy chunked - let node_url = command - .node - .get_node_url(&pkg.descriptor.manifest_file.network)?; - let provider = Provider::connect(node_url).await?; - - deploy_chunked( - command, - pkg, - salt, - &account, - &provider, - &pkg.descriptor.name, - ) - .await? - } else { - deploy_pkg(command, pkg, salt, &provider, &account).await? - }; - - let proxy_id = match &pkg.descriptor.manifest_file.proxy { - Some(forc_pkg::manifest::Proxy { - enabled: true, - address: Some(proxy_addr), - }) => { - // Make a call into the contract to update impl contract address to 'deployed_contract'. - - // Create a contract instance for the proxy contract using default proxy contract abi and - // specified address. - let proxy_contract = - ContractId::from_str(proxy_addr).map_err(|e| anyhow::anyhow!(e))?; - - update_proxy_contract_target(&account, proxy_contract, deployed_contract_id) - .await?; - Some(proxy_contract) - } - Some(forc_pkg::manifest::Proxy { - enabled: true, - address: None, - }) => { - let pkg_name = &pkg.descriptor.name; - let pkg_storage_slots = &pkg.storage_slots; - // Deploy a new proxy contract. - let deployed_proxy_contract = deploy_new_proxy( - command, - pkg_name, - pkg_storage_slots, - &deployed_contract_id, - &provider, - &account, - ) - .await?; - - // Update manifest file such that the proxy address field points to the new proxy contract. - update_proxy_address_in_manifest( - &format!("0x{deployed_proxy_contract}"), - &pkg.descriptor.manifest_file, - )?; - Some(deployed_proxy_contract) - } - // Proxy not enabled. - _ => None, - }; - - let deployed_contract = DeployedContract { - id: deployed_contract_id, - proxy: proxy_id, - chunked: bytecode_size > MAX_CONTRACT_SIZE, - }; - deployed_contracts.push(deployed_contract); - } - Ok(deployed_contracts) -} - -/// Prompt the user to confirm the transactions required for deployment, as well as the signing key. -async fn confirm_transaction_details( - pkgs_to_deploy: &[Arc], - command: &cmd::Deploy, - node_url: String, - max_contract_size: usize, -) -> Result { - // Confirmation step. Summarize the transaction(s) for the deployment. - let mut tx_count = 0; - let tx_summary = pkgs_to_deploy - .iter() - .map(|pkg| { - tx_count += 1; - let proxy_text = match &pkg.descriptor.manifest_file.proxy { - Some(forc_pkg::manifest::Proxy { - enabled: true, - address, - }) => { - tx_count += 1; - if address.is_some() { - " + update proxy" - } else { - " + deploy proxy" - } - } - _ => "", - }; - - let pkg_bytecode_len = pkg.bytecode.bytes.len(); - let blob_text = if pkg_bytecode_len > max_contract_size { - let number_of_blobs = pkg_bytecode_len.div_ceil(max_contract_size); - tx_count += number_of_blobs; - &format!(" + {number_of_blobs} blobs") - } else { - "" - }; - - format!( - "deploy {}{blob_text}{proxy_text}", - pkg.descriptor.manifest_file.project_name() - ) - }) - .collect::>() - .join(" + "); - - println_action_green("Confirming", &format!("transactions [{tx_summary}]")); - println_action_green("", &format!("Network: {node_url}")); - - let account = setup_deployment_account(command, &node_url, tx_count).await?; - - // TODO: Display the estimated gas cost of the transaction(s). - // https://github.com/FuelLabs/sway/issues/6277 - - Ok(account) -} - -/// Deploy a single pkg given deploy command and the manifest file -pub async fn deploy_pkg( - command: &cmd::Deploy, - compiled: &BuiltPackage, - salt: Salt, - provider: &Provider, - account: &ForcClientAccount, -) -> Result { - let manifest = &compiled.descriptor.manifest_file; - let node_url = provider.url(); - let client = FuelClient::new(node_url)?; - - let bytecode = &compiled.bytecode.bytes; - - let storage_slots = resolve_storage_slots(command, compiled)?; - let contract = Contract::from(bytecode.clone()); - let root = contract.root(); - let state_root = Contract::initial_state_root(storage_slots.iter()); - let contract_id = Contract::id(&salt, &root, &state_root); - let tx_policies = tx_policies_from_cmd(command); - - let mut tb = CreateTransactionBuilder::prepare_contract_deployment( - bytecode.clone(), - contract_id, - state_root, - salt, - storage_slots.clone(), - tx_policies, - ); - - account.add_witnesses(&mut tb)?; - account.adjust_for_fee(&mut tb, 0).await?; - - let tx = tb.build(provider).await?; - let tx = Transaction::from(tx); - - let chain_info = client.chain_info().await?; - let chain_id = chain_info.consensus_parameters.chain_id(); - - // If only submitting the transaction, don't wait for the deployment to complete - let contract_id: ContractId = if command.submit_only { - match client.submit(&tx).await { - Ok(transaction_id) => { - // Create a deployment artifact. - create_deployment_artifact( - DeploymentArtifact { - transaction_id: Some(format!("0x{transaction_id}")), - salt: format!("0x{salt}"), - network_endpoint: node_url.to_string(), - chain_id, - contract_id: format!("0x{contract_id}"), - deployment_size: bytecode.len(), - deployed_block_height: None, - deployment_type: DeploymentType::Standard, - chunked_deployment_info: None, - }, - command, - manifest, - chain_info, - )?; - - contract_id - } - Err(e) => { - bail!( - "contract {} failed to deploy due to an error: {:?}", - &contract_id, - e - ) - } - } - } else { - let deployment_request = client.submit_and_await_commit(&tx).map(|res| match res { - Ok(logs) => match logs { - TransactionStatus::Submitted { .. } => { - bail!("contract {} deployment timed out", &contract_id); - } - TransactionStatus::Success { block_height, .. } => { - // Create a deployment artifact. - create_deployment_artifact( - DeploymentArtifact { - transaction_id: Some(format!("0x{}", tx.id(&chain_id))), - salt: format!("0x{salt}"), - network_endpoint: node_url.to_string(), - chain_id, - contract_id: format!("0x{contract_id}"), - deployment_size: bytecode.len(), - deployed_block_height: Some(*block_height), - deployment_type: DeploymentType::Standard, - chunked_deployment_info: None, - }, - command, - manifest, - chain_info, - )?; - - Ok(contract_id) - } - e => { - bail!( - "contract {} failed to deploy due to an error: {:?}", - &contract_id, - e - ) - } - }, - Err(e) => bail!("{e}"), - }); - tokio::time::timeout( - Duration::from_millis(TX_SUBMIT_TIMEOUT_MS), - deployment_request, - ) - .await - .with_context(|| { - format!( - "Timed out waiting for contract {} to deploy. The transaction may have been dropped.", - &contract_id - ) - })?? - }; - - Ok(contract_id) -} - -fn tx_policies_from_cmd(cmd: &cmd::Deploy) -> TxPolicies { - let mut tx_policies = TxPolicies::default(); - if let Some(max_fee) = cmd.gas.max_fee { - tx_policies = tx_policies.with_max_fee(max_fee); - } - if let Some(script_gas_limit) = cmd.gas.script_gas_limit { - tx_policies = tx_policies.with_script_gas_limit(script_gas_limit); - } - tx_policies -} - -fn build_opts_from_cmd(cmd: &cmd::Deploy, member_filter: pkg::MemberFilter) -> pkg::BuildOpts { - pkg::BuildOpts { - pkg: pkg::PkgOpts { - path: cmd.pkg.path.clone(), - offline: cmd.pkg.offline, - terse: cmd.pkg.terse, - locked: cmd.pkg.locked, - output_directory: cmd.pkg.output_directory.clone(), - ipfs_node: cmd.pkg.ipfs_node.clone().unwrap_or_default(), - }, - print: pkg::PrintOpts { - ast: cmd.print.ast, - dca_graph: cmd.print.dca_graph.clone(), - dca_graph_url_format: cmd.print.dca_graph_url_format.clone(), - asm: cmd.print.asm(), - bytecode: cmd.print.bytecode, - bytecode_spans: false, - ir: cmd.print.ir(), - reverse_order: cmd.print.reverse_order, - }, - verify_ir: cmd - .verify_ir - .as_ref() - .map_or(IrCli::default(), |opts| IrCliOpt::from(opts).0), - dump: DumpOpts::default(), - time_phases: cmd.print.time_phases, - profile: cmd.print.profile, - metrics_outfile: cmd.print.metrics_outfile.clone(), - minify: pkg::MinifyOpts { - json_abi: cmd.minify.json_abi, - json_storage_slots: cmd.minify.json_storage_slots, - }, - build_profile: cmd.build_profile.clone(), - release: cmd.build_profile == BuildProfile::RELEASE, - error_on_warnings: false, - binary_outfile: cmd.build_output.bin_file.clone(), - debug_outfile: cmd.build_output.debug_file.clone(), - hex_outfile: cmd.build_output.hex_file.clone(), - build_target: BuildTarget::default(), - tests: false, - member_filter, - experimental: cmd.experimental.experimental.clone(), - no_experimental: cmd.experimental.no_experimental.clone(), - no_output: false, - } -} - -/// Creates a deployment artifact and writes it to a file. -/// -/// This function is used to generate a deployment artifact containing details -/// about the deployment, such as the transaction ID, salt, network endpoint, -/// chain ID, contract ID, deployment size, and deployed block height. It then -/// writes this artifact to a specified output directory. -fn create_deployment_artifact( - deployment_artifact: DeploymentArtifact, - cmd: &cmd::Deploy, - manifest: &PackageManifestFile, - chain_info: ChainInfo, -) -> Result<()> { - let contract_id = ContractId::from_str(&deployment_artifact.contract_id).unwrap(); - let pkg_name = manifest.project_name(); - - let target = Target::from_str(&chain_info.name).unwrap_or_default(); - let (contract_url, block_url) = match target.explorer_url() { - Some(explorer_url) => ( - format!("{explorer_url}/contract/0x"), - format!("{explorer_url}/block/"), - ), - None => ("".to_string(), "".to_string()), - }; - println_action_green( - "Finished", - &format!("deploying {pkg_name} {contract_url}{contract_id}"), - ); - - let block_height = deployment_artifact.deployed_block_height; - if let Some(block_height) = block_height { - println_action_green("Deployed", &format!("in block {block_url}{block_height}")); - } - - let output_dir = cmd - .pkg - .output_directory - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| default_output_directory(manifest.dir())) - .join("deployments"); - deployment_artifact.to_file(&output_dir, pkg_name, contract_id) -} - -/// Creates a deployment artifact for chunked deployments and writes it to a file. -fn create_chunked_deployment_artifact( - contract_id: ContractId, - salt: Salt, - node_url: &str, - chain_info: ChainInfo, - compiled: &BuiltPackage, - cmd: &cmd::Deploy, - manifest: &PackageManifestFile, -) -> Result<()> { - let pkg_name = manifest.project_name(); - let chain_id = chain_info.consensus_parameters.chain_id(); - - // Calculate chunks info - let original_size = compiled.bytecode.bytes.len(); - let chunks: Vec = compiled - .bytecode - .bytes - .chunks(MAX_CONTRACT_SIZE) - .enumerate() - .map(|(index, chunk)| { - let mut hasher = Hasher::default(); - hasher.input(chunk); - let hash = format!("0x{}", hasher.digest()); - ChunkInfo { - index, - size: chunk.len(), - hash, - } - }) - .collect(); - - let chunked_info = ChunkedDeploymentInfo { - original_contract_size: original_size, - max_chunk_size: MAX_CONTRACT_SIZE, - total_chunks: chunks.len(), - chunks, - loader_contract_id: format!("0x{contract_id}"), - }; - - let deployment_artifact = DeploymentArtifact { - transaction_id: None, // fuels SDK doesn't expose transaction details for chunked deployments - salt: format!("0x{salt}"), - network_endpoint: node_url.to_string(), - chain_id, - contract_id: format!("0x{contract_id}"), - deployment_size: original_size, - deployed_block_height: None, // Cannot get this from fuels SDK for chunked deployments - deployment_type: DeploymentType::Chunked, - chunked_deployment_info: Some(chunked_info), - }; - - let target = Target::from_str(&chain_info.name).unwrap_or_default(); - let contract_url = match target.explorer_url() { - Some(explorer_url) => format!("{explorer_url}/contract/0x"), - None => "".to_string(), - }; - - println_action_green( - "Finished", - &format!("deploying chunked contract {pkg_name} {contract_url}{contract_id}"), - ); - - let output_dir = cmd - .pkg - .output_directory - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| default_output_directory(manifest.dir())) - .join("deployments"); - deployment_artifact.to_file(&output_dir, pkg_name, contract_id) -} - -/// Validates that all packages are being deployed to the same node and returns the node URL. -async fn validate_and_get_node_url( - command: &cmd::Deploy, - packages: &[Arc], -) -> Result { - let node_url = command - .node - .get_node_url(&packages[0].descriptor.manifest_file.network)?; - if !packages.iter().all(|pkg| { - command - .node - .get_node_url(&pkg.descriptor.manifest_file.network) - .ok() - == Some(node_url.clone()) - }) { - bail!("All packages in a deployment should be deployed to the same node. Please ensure that the network specified in the Forc.toml files of all packages is the same."); - } - Ok(node_url) -} - -/// Sets up and returns the account for deployment. -async fn setup_deployment_account( - command: &cmd::Deploy, - node_url: &str, - tx_count: usize, -) -> Result { - let provider = Provider::connect(node_url).await?; - - let wallet_mode = if command.default_signer || command.signing_key.is_some() { - SignerSelectionMode::Manual - } else if let Some(arn) = &command.aws_kms_signer { - SignerSelectionMode::AwsSigner(arn.clone()) - } else { - // Check if we have a wallet in the default path - // If there is one we will ask for the password - // If not we will ask the user to either create a new one or import one - let wallet_path = default_wallet_path(); - check_and_create_wallet_at_default_path(&wallet_path).await?; - println_action_green("", &format!("Wallet: {}", default_wallet_path().display())); - let password = prompt_forc_wallet_password()?; - SignerSelectionMode::ForcWallet(password) - }; - - let account = select_account( - &wallet_mode, - command.default_signer || command.unsigned, - command.signing_key, - &provider, - tx_count, - ) - .await?; - - Ok(account) -} -#[cfg(test)] -mod test { - use super::*; - - fn setup_manifest_files() -> BTreeMap { - let mut contract_to_manifest = BTreeMap::default(); - - let manifests_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("test") - .join("data"); - - for entry in manifests_dir.read_dir().unwrap() { - let manifest = - PackageManifestFile::from_file(entry.unwrap().path().join("Forc.toml")).unwrap(); - contract_to_manifest.insert(manifest.project_name().to_string(), manifest); - } - - contract_to_manifest - } - - #[test] - fn test_parse_and_validate_salts_pass() { - let mut manifests = setup_manifest_files(); - let mut expected = ContractSaltMap::new(); - let mut salt_strs = vec![]; - - // Remove contracts with dependencies - manifests.remove("contract_with_dep_with_salt_conflict"); - manifests.remove("contract_with_dep"); - - for (index, manifest) in manifests.values().enumerate() { - let salt = "0x0000000000000000000000000000000000000000000000000000000000000000"; - - let salt_str = format!("{}:{salt}", manifest.project_name()); - salt_strs.push(salt_str.to_string()); - - expected.insert( - manifest.project_name().to_string(), - salt.parse::().unwrap(), - ); - - let got = validate_and_parse_salts(&salt_strs, manifests.values()).unwrap(); - assert_eq!(got.len(), index + 1); - assert_eq!(got, expected); - } - } - - #[test] - fn test_parse_and_validate_salts_duplicate_salt_input() { - let manifests = setup_manifest_files(); - let first_name = manifests.first_key_value().unwrap().0; - let salt: Salt = "0x0000000000000000000000000000000000000000000000000000000000000000" - .parse() - .unwrap(); - let salt_str = format!("{first_name}:{salt}"); - let err_message = - format!("2 salts provided for contract '{first_name}':\n {salt}\n {salt}"); - - assert_eq!( - validate_and_parse_salts(&[salt_str.clone(), salt_str], manifests.values()) - .unwrap_err() - .to_string(), - err_message, - ); - } - - #[test] - fn test_parse_single_salt_multiple_manifests_malformed_input() { - let manifests = setup_manifest_files(); - let salt_str = - "contract_a=0x0000000000000000000000000000000000000000000000000000000000000000"; - let err_message = - "Invalid salt provided - salt must be in the form : when deploying a workspace"; - - assert_eq!( - validate_and_parse_salts(&[salt_str.to_string()], manifests.values()) - .unwrap_err() - .to_string(), - err_message, - ); - } - - #[test] - fn test_parse_multiple_salts_conflict() { - let manifests = setup_manifest_files(); - let salt_str = - "contract_with_dep:0x0000000000000000000000000000000000000000000000000000000000000001"; - let err_message = - "Redeclaration of salt using the option '--salt' while a salt exists for contract 'contract_with_dep' \ - under the contract dependencies of the Forc.toml manifest for 'contract_with_dep_with_salt_conflict'\n\ - Existing salt: '0x0000000000000000000000000000000000000000000000000000000000000000',\n\ - You declared: '0x0000000000000000000000000000000000000000000000000000000000000001'\n"; - - assert_eq!( - validate_and_parse_salts(&[salt_str.to_string()], manifests.values()) - .unwrap_err() - .to_string(), - err_message, - ); - } -} diff --git a/forc-plugins/forc-client/src/op/mod.rs b/forc-plugins/forc-client/src/op/mod.rs deleted file mode 100644 index e0f77556ea8..00000000000 --- a/forc-plugins/forc-client/src/op/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod call; -mod deploy; -mod run; -mod submit; - -pub use call::call; -pub use deploy::{deploy, DeployedContract, DeployedExecutable, DeployedPackage}; -pub use run::run; -pub use submit::submit; diff --git a/forc-plugins/forc-client/src/op/run/encode.rs b/forc-plugins/forc-client/src/op/run/encode.rs deleted file mode 100644 index 30587bd6eef..00000000000 --- a/forc-plugins/forc-client/src/op/run/encode.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::util::encode::{Token, Type}; -use fuel_abi_types::abi::full_program::FullProgramABI; -use fuels_core::codec::{ABIEncoder, EncoderConfig}; - -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct ScriptCallHandler { - main_arg_types: Vec, -} - -impl ScriptCallHandler { - const MAIN_KEYWORD: &'static str = "main"; - - /// Generate a new call handler for calling script main function from the json abi. - /// - /// Provide json abi is used for determining the argument types, this is required as the data - /// encoding is requiring the type of the data. - pub(crate) fn from_json_abi_str(json_abi_str: &str) -> anyhow::Result { - let full_abi = FullProgramABI::from_json_abi(json_abi_str)?; - // Note: using .expect() here is safe since a script without a main function is a compile - // error and the fact that we have the json abi of the built script suggests that this is a - // valid script. - let main_function = full_abi - .functions - .iter() - .find(|abi_func| abi_func.name() == Self::MAIN_KEYWORD) - .expect("every valid script needs to have a main function"); - let main_arg_types = main_function - .inputs() - .iter() - .map(Type::try_from) - .collect::>>()?; - - Ok(Self { main_arg_types }) - } - - /// Encode the provided values with script's main argument types. - /// - /// Returns an error if the provided value count does not match the number of arguments. - pub(crate) fn encode_arguments(&self, values: &[&str]) -> anyhow::Result> { - let main_arg_types = &self.main_arg_types; - let expected_arg_count = main_arg_types.len(); - let provided_arg_count = values.len(); - - if expected_arg_count != provided_arg_count { - anyhow::bail!( - "main function takes {expected_arg_count} arguments, {provided_arg_count} provided" - ); - } - - let tokens = main_arg_types - .iter() - .zip(values.iter()) - .map(|(ty, val)| Token::from_type_and_value(ty, val).map(|token| token.0)) - .collect::>>()?; - - let abi_encoder = ABIEncoder::new(EncoderConfig::default()); - Ok(abi_encoder.encode(tokens.as_slice())?) - } -} - -#[cfg(test)] -mod tests { - use super::{ScriptCallHandler, Type}; - - const TEST_JSON_ABI: &str = r#"{"programType": "contract","specVersion": "1.1","encodingVersion": "1","metadataTypes":[], - "concreteTypes":[{"concreteTypeId":"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "type":"()"},{"concreteTypeId":"b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903","type":"bool"}, - {"concreteTypeId":"c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b","type":"u8"}], - "functions":[{"inputs":[{"name":"test_u8","concreteTypeId":"c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b"}, - {"name":"test_bool","concreteTypeId":"b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903"}],"name":"main", - "output":"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d"}],"loggedTypes":[], - "messagesTypes":[],"configurables":[]}"#; - - #[test] - fn test_script_call_handler_generation_success() { - let generated_call_handler = ScriptCallHandler::from_json_abi_str(TEST_JSON_ABI).unwrap(); - - let expected_call_handler = ScriptCallHandler { - main_arg_types: vec![Type::U8, Type::Bool], - }; - - assert_eq!(generated_call_handler, expected_call_handler); - } - - #[test] - #[should_panic] - fn test_script_call_handler_generation_fail_missing_main() { - let test_json_abi = - r#"{"types":[],"functions":[],"loggedTypes":[],"messagesTypes":[],"configurables":[]}"#; - ScriptCallHandler::from_json_abi_str(test_json_abi).unwrap(); - } - - #[test] - fn test_main_encoding_success() { - let call_handler = ScriptCallHandler::from_json_abi_str(TEST_JSON_ABI).unwrap(); - let values = ["2", "true"]; - - let encoded_bytes = call_handler.encode_arguments(&values).unwrap(); - let expected_bytes = vec![2u8, 1u8]; - assert_eq!(encoded_bytes, expected_bytes); - } - - #[test] - #[should_panic] - fn test_main_encoding_fail_arg_type_mismatch() { - let call_handler = ScriptCallHandler::from_json_abi_str(TEST_JSON_ABI).unwrap(); - // The abi describes the following main function: - // - fn main(test_u8: u8, test_bool: bool) - // Providing a bool to u8 field should return an error. - let values = ["true", "2"]; - call_handler.encode_arguments(&values).unwrap(); - } - - #[test] - #[should_panic(expected = "main function takes 2 arguments, 1 provided")] - fn test_main_encoding_fail_arg_count_mismatch() { - let call_handler = ScriptCallHandler::from_json_abi_str(TEST_JSON_ABI).unwrap(); - // The abi describes the following main function: - // - fn main(test_u8: u8, test_bool: bool) - // Providing only 1 value should return an error as function requires 2 args. - let values = ["true"]; - call_handler.encode_arguments(&values).unwrap(); - } -} diff --git a/forc-plugins/forc-client/src/op/run/mod.rs b/forc-plugins/forc-client/src/op/run/mod.rs deleted file mode 100644 index cbbc24f9499..00000000000 --- a/forc-plugins/forc-client/src/op/run/mod.rs +++ /dev/null @@ -1,349 +0,0 @@ -mod encode; -use crate::{ - cmd, - constants::TX_SUBMIT_TIMEOUT_MS, - util::{ - pkg::built_pkgs, - tx::{prompt_forc_wallet_password, select_account, SignerSelectionMode}, - }, -}; -use anyhow::{anyhow, bail, Context, Result}; -use forc::cli::shared::IrCliOpt; -use forc_pkg::{self as pkg, fuel_core_not_running, DumpOpts, PackageManifestFile}; -use forc_tracing::println_warning; -use forc_util::tx_utils::format_log_receipts; -use fuel_abi_types::abi::program::ProgramABI; -use fuel_core_client::client::FuelClient; -use fuel_tx::{ContractId, Transaction}; -use fuels::{ - programs::calls::{traits::TransactionTuner, ScriptCall}, - types::{ - transaction::TxPolicies, - transaction_builders::{BuildableTransaction, VariableOutputPolicy}, - }, -}; -use fuels_accounts::{provider::Provider, Account, ViewOnlyAccount}; -use pkg::BuiltPackage; -use std::time::Duration; -use std::{path::PathBuf, str::FromStr}; -use sway_core::BuildTarget; -use sway_core::{language::parsed::TreeType, IrCli}; -use tokio::time::timeout; -use tracing::info; - -use self::encode::ScriptCallHandler; - -pub struct RanScript { - pub receipts: Vec, -} - -/// Builds and runs script(s). If given path corresponds to a workspace, all runnable members will -/// be built and deployed. -/// -/// Upon success, returns the receipts of each script in the order they are executed. -/// -/// When running a single script, only that script's receipts are returned. -pub async fn run(command: cmd::Run) -> Result> { - let mut command = command; - if command.unsigned { - println_warning("--unsigned flag is deprecated, please prefer using --default-signer. Assuming `--default-signer` is passed. This means your transaction will be signed by an account that is funded by fuel-core by default for testing purposes."); - command.default_signer = true; - } - let mut receipts = Vec::new(); - let curr_dir = if let Some(path) = &command.pkg.path { - PathBuf::from(path) - } else { - std::env::current_dir().map_err(|e| anyhow!("{:?}", e))? - }; - let build_opts = build_opts_from_cmd(&command); - let built_pkgs_with_manifest = built_pkgs(&curr_dir, &build_opts)?; - let wallet_mode = if command.default_signer || command.signing_key.is_some() { - SignerSelectionMode::Manual - } else { - let password = prompt_forc_wallet_password()?; - SignerSelectionMode::ForcWallet(password) - }; - for built in built_pkgs_with_manifest { - if built - .descriptor - .manifest_file - .check_program_type(&[TreeType::Script]) - .is_ok() - { - let pkg_receipts = run_pkg( - &command, - &built.descriptor.manifest_file, - &built, - &wallet_mode, - ) - .await?; - receipts.push(pkg_receipts); - } - } - - Ok(receipts) -} - -fn tx_policies_from_cmd(cmd: &cmd::Run) -> TxPolicies { - let mut tx_policies = TxPolicies::default(); - if let Some(max_fee) = cmd.gas.max_fee { - tx_policies = tx_policies.with_max_fee(max_fee); - } - if let Some(script_gas_limit) = cmd.gas.script_gas_limit { - tx_policies = tx_policies.with_script_gas_limit(script_gas_limit); - } - tx_policies -} - -pub async fn run_pkg( - command: &cmd::Run, - manifest: &PackageManifestFile, - compiled: &BuiltPackage, - signer_mode: &SignerSelectionMode, -) -> Result { - let node_url = command.node.get_node_url(&manifest.network)?; - let provider = Provider::connect(node_url.clone()).await?; - let consensus_params = provider.consensus_parameters().await?; - let tx_count = 1; - let account = select_account( - signer_mode, - command.default_signer || command.unsigned, - command.signing_key, - &provider, - tx_count, - ) - .await?; - - let script_data = match (&command.data, &command.args) { - (None, Some(args)) => { - let minify_json_abi = true; - let package_json_abi = compiled - .json_abi_string(minify_json_abi)? - .ok_or_else(|| anyhow::anyhow!("Missing json abi string"))?; - let main_arg_handler = ScriptCallHandler::from_json_abi_str(&package_json_abi)?; - let args = args.iter().map(|arg| arg.as_str()).collect::>(); - main_arg_handler.encode_arguments(args.as_slice())? - } - (Some(_), Some(_)) => { - bail!("Both --args and --data provided, must choose one.") - } - _ => { - let input_data = command.data.as_deref().unwrap_or(""); - let data = input_data.strip_prefix("0x").unwrap_or(input_data); - hex::decode(data).expect("Invalid hex") - } - }; - - let external_contracts = command - .contract - .as_ref() - .into_iter() - .flat_map(|contracts| contracts.iter()) - .map(|contract| { - ContractId::from_str(contract) - .map_err(|e| anyhow!("Failed to parse contract id: {}", e)) - }) - .collect::>>()?; - - let script_binary = compiled.bytecode.bytes.clone(); - let call = ScriptCall { - script_binary, - encoded_args: Ok(script_data), - inputs: vec![], - outputs: vec![], - external_contracts, - }; - let tx_policies = tx_policies_from_cmd(command); - let mut tb = call.transaction_builder( - tx_policies, - VariableOutputPolicy::EstimateMinimum, - &consensus_params, - call.inputs.clone(), - &account, - )?; - - account.add_witnesses(&mut tb)?; - account.adjust_for_fee(&mut tb, 0).await?; - - let tx = tb.build(provider).await?; - - if command.dry_run { - info!("{:?}", tx); - Ok(RanScript { receipts: vec![] }) - } else { - let program_abi = match &compiled.program_abi { - sway_core::asm_generation::ProgramABI::Fuel(abi) => Some(abi), - _ => None, - }; - let receipts = try_send_tx( - node_url.as_str(), - &tx.into(), - command.pretty_print, - command.simulate, - command.debug, - program_abi, - ) - .await?; - Ok(RanScript { receipts }) - } -} - -async fn try_send_tx( - node_url: &str, - tx: &Transaction, - pretty_print: bool, - simulate: bool, - debug: bool, - abi: Option<&ProgramABI>, -) -> Result> { - let client = FuelClient::new(node_url)?; - - match client.health().await { - Ok(_) => timeout( - Duration::from_millis(TX_SUBMIT_TIMEOUT_MS), - send_tx(&client, tx, pretty_print, simulate, debug, abi), - ) - .await - .with_context(|| format!("timeout waiting for {tx:?} to be included in a block"))?, - Err(_) => Err(fuel_core_not_running(node_url)), - } -} - -async fn send_tx( - client: &FuelClient, - tx: &Transaction, - pretty_print: bool, - simulate: bool, - debug: bool, - abi: Option<&ProgramABI>, -) -> Result> { - let outputs = { - if !simulate { - let status = client.submit_and_await_commit(tx).await?; - - match status { - fuel_core_client::client::types::TransactionStatus::Success { - receipts, .. - } => receipts, - fuel_core_client::client::types::TransactionStatus::Failure { - receipts, .. - } => receipts, - _ => vec![], - } - } else { - let txs = vec![tx.clone()]; - let receipts = client.dry_run(txs.as_slice()).await?; - let receipts = receipts - .first() - .map(|tx| &tx.result) - .map(|res| res.receipts()); - match receipts { - Some(receipts) => receipts.to_vec(), - None => vec![], - } - } - }; - if !outputs.is_empty() { - info!("{}", format_log_receipts(&outputs, pretty_print)?); - } - if debug { - start_debug_session(client, tx, abi).await?; - } - Ok(outputs) -} - -/// Starts an interactive debugging session with the given transaction -async fn start_debug_session( - fuel_client: &FuelClient, - tx: &fuel_tx::Transaction, - program_abi: Option<&ProgramABI>, -) -> Result<()> { - // Create debugger instance from the existing fuel client - let mut debugger = forc_debug::debugger::Debugger::from_client(fuel_client.clone()) - .await - .map_err(|e| anyhow!("Failed to create debugger: {e}"))?; - - // Create temporary files for transaction and ABI (auto-cleaned when dropped) - let mut tx_file = tempfile::Builder::new() - .suffix(".json") - .tempfile() - .map_err(|e| anyhow!("Failed to create temp transaction file: {e}"))?; - serde_json::to_writer_pretty(&mut tx_file, tx) - .map_err(|e| anyhow!("Failed to write transaction to temp file: {e}"))?; - - let mut abi_file = tempfile::Builder::new() - .suffix(".json") - .tempfile() - .map_err(|e| anyhow!("Failed to create temp ABI file: {e}"))?; - - let tx_cmd = if let Some(abi) = program_abi { - serde_json::to_writer_pretty(&mut abi_file, &abi) - .map_err(|e| anyhow!("Failed to write ABI to temp file: {e}"))?; - - // Prepare the start_tx command string for the CLI - format!( - "start_tx {} {}", - tx_file.path().to_string_lossy(), - abi_file.path().to_string_lossy() - ) - } else { - // Prepare the start_tx command string for the CLI - format!("start_tx {}", tx_file.path().to_string_lossy()) - }; - - // Start the interactive CLI session with the prepared command - let mut cli = forc_debug::cli::Cli::new() - .map_err(|e| anyhow!("Failed to create debug CLI interface: {e}"))?; - cli.run(&mut debugger, Some(tx_cmd)) - .await - .map_err(|e| anyhow!("Interactive debugging session failed: {e}"))?; - - Ok(()) -} - -fn build_opts_from_cmd(cmd: &cmd::Run) -> pkg::BuildOpts { - pkg::BuildOpts { - pkg: pkg::PkgOpts { - path: cmd.pkg.path.clone(), - offline: cmd.pkg.offline, - terse: cmd.pkg.terse, - locked: cmd.pkg.locked, - output_directory: cmd.pkg.output_directory.clone(), - ipfs_node: cmd.pkg.ipfs_node.clone().unwrap_or_default(), - }, - print: pkg::PrintOpts { - ast: cmd.print.ast, - dca_graph: cmd.print.dca_graph.clone(), - dca_graph_url_format: cmd.print.dca_graph_url_format.clone(), - asm: cmd.print.asm(), - bytecode: cmd.print.bytecode, - bytecode_spans: false, - ir: cmd.print.ir(), - reverse_order: cmd.print.reverse_order, - }, - verify_ir: cmd - .verify_ir - .as_ref() - .map_or(IrCli::default(), |opts| IrCliOpt::from(opts).0), - minify: pkg::MinifyOpts { - json_abi: cmd.minify.json_abi, - json_storage_slots: cmd.minify.json_storage_slots, - }, - dump: DumpOpts::default(), - build_target: BuildTarget::default(), - build_profile: cmd.build_profile.build_profile.clone(), - release: cmd.build_profile.release, - error_on_warnings: cmd.build_profile.error_on_warnings, - time_phases: cmd.print.time_phases, - profile: cmd.print.profile, - metrics_outfile: cmd.print.metrics_outfile.clone(), - binary_outfile: cmd.build_output.bin_file.clone(), - debug_outfile: cmd.build_output.debug_file.clone(), - hex_outfile: cmd.build_output.hex_file.clone(), - tests: false, - member_filter: pkg::MemberFilter::only_scripts(), - experimental: cmd.experimental.experimental.clone(), - no_experimental: cmd.experimental.no_experimental.clone(), - no_output: false, - } -} diff --git a/forc-plugins/forc-client/src/op/submit.rs b/forc-plugins/forc-client/src/op/submit.rs deleted file mode 100644 index c3a15910039..00000000000 --- a/forc-plugins/forc-client/src/op/submit.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::cmd; -use anyhow::Context; -use fuel_core_client::client::{types::TransactionStatus, FuelClient}; -use fuel_crypto::fuel_types::canonical::Deserialize; - -/// A command for submitting transactions to a Fuel network. -pub async fn submit(cmd: cmd::Submit) -> anyhow::Result<()> { - let tx = read_tx(&cmd.tx_path)?; - let node_url = cmd.network.node.get_node_url(&None)?; - let client = FuelClient::new(node_url)?; - if cmd.network.await_ { - let status = client - .submit_and_await_commit(&tx) - .await - .context("Submission of tx or awaiting commit failed")?; - if cmd.tx_status.json { - print_status_json(&status)?; - } else { - print_status(&status); - } - } else { - let id = client.submit(&tx).await.context("Failed to submit tx")?; - println!("{id}"); - } - Ok(()) -} - -/// Deserialize a `Transaction` from the given file into memory. -pub fn read_tx(path: &std::path::Path) -> anyhow::Result { - let file = std::fs::File::open(path)?; - let reader = std::io::BufReader::new(file); - fn has_extension(path: &std::path::Path, ext: &str) -> bool { - path.extension().and_then(|ex| ex.to_str()) == Some(ext) - } - let tx: fuel_tx::Transaction = if has_extension(path, "json") { - serde_json::from_reader(reader)? - } else if has_extension(path, "bin") { - let tx_bytes = std::fs::read(path)?; - fuel_tx::Transaction::from_bytes(&tx_bytes).map_err(anyhow::Error::msg)? - } else { - anyhow::bail!(r#"Unsupported transaction file extension, expected ".json" or ".bin""#); - }; - Ok(tx) -} - -/// Format the transaction status in a more human-friendly manner. -pub fn fmt_status(status: &TransactionStatus, s: &mut String) -> anyhow::Result<()> { - use chrono::TimeZone; - use std::fmt::Write; - match status { - TransactionStatus::Submitted { submitted_at } => { - writeln!(s, "Transaction Submitted at {:?}", submitted_at.0)?; - } - TransactionStatus::Success { - block_height, - time, - program_state, - .. - } => { - let utc = chrono::Utc.timestamp_nanos(time.to_unix()); - writeln!(s, "Transaction Succeeded")?; - writeln!(s, " Block ID: {block_height}")?; - writeln!(s, " Time: {utc}",)?; - writeln!(s, " Program State: {program_state:?}")?; - } - TransactionStatus::SqueezedOut { reason } => { - writeln!(s, "Transaction Squeezed Out: {reason}")?; - } - TransactionStatus::Failure { - block_height, - time, - reason, - program_state, - .. - } => { - let utc = chrono::Utc.timestamp_nanos(time.to_unix()); - writeln!(s, "Transaction Failed")?; - writeln!(s, " Reason: {reason}")?; - writeln!(s, " Block ID: {block_height}")?; - writeln!(s, " Time: {utc}")?; - writeln!(s, " Program State: {program_state:?}")?; - } - TransactionStatus::PreconfirmationSuccess { - total_gas, - transaction_id, - receipts, - .. - } => { - writeln!(s, "Transaction Preconfirmatino Succeeded")?; - writeln!(s, " Total Gas: {total_gas}")?; - writeln!(s, " Transaction Id: {transaction_id}",)?; - writeln!(s, " Receipts: {receipts:?}")?; - } - TransactionStatus::PreconfirmationFailure { - total_gas, - transaction_id, - receipts, - reason, - .. - } => { - writeln!(s, "Transaction Preconfirmation Failed")?; - writeln!(s, " Total Gas: {total_gas}")?; - writeln!(s, " Transaction Id: {transaction_id}",)?; - writeln!(s, " Receipts: {receipts:?}")?; - writeln!(s, " Reason: {reason:?}")?; - } - } - Ok(()) -} - -/// Print the status to stdout. -pub fn print_status(status: &TransactionStatus) { - let mut string = String::new(); - fmt_status(status, &mut string).expect("formatting to `String` is infallible"); - println!("{string}"); -} - -/// Print the status to stdout in its JSON representation. -pub fn print_status_json(status: &TransactionStatus) -> anyhow::Result<()> { - let json = serde_json::to_string_pretty(status)?; - println!("{json}"); - Ok(()) -} diff --git a/forc-plugins/forc-client/src/util/account.rs b/forc-plugins/forc-client/src/util/account.rs deleted file mode 100644 index 023a5c89ba3..00000000000 --- a/forc-plugins/forc-client/src/util/account.rs +++ /dev/null @@ -1,91 +0,0 @@ -use async_trait::async_trait; -use fuel_crypto::{Message, Signature}; -use fuels::{ - prelude::*, - types::{coin_type_id::CoinTypeId, input::Input}, -}; -use fuels_accounts::{ - signers::private_key::PrivateKeySigner, - wallet::{Unlocked, Wallet}, - Account, -}; - -use super::aws::AwsSigner; - -#[derive(Clone, Debug)] -/// Set of different signers available to be used with `forc-client` operations. -pub enum ForcClientAccount { - /// Local signer where the private key owned locally. This can be - /// generated through `forc-wallet` integration or manually by providing - /// a private-key. - Wallet(Wallet>), - /// A KMS Signer specifically using AWS KMS service. The signing key - /// is managed by another entity for KMS signers. Messages are - /// signed by the KMS entity. Signed transactions are retrieved - /// and submitted to the node by `forc-client`. - KmsSigner(AwsSigner), -} - -impl Account for ForcClientAccount { - fn add_witnesses(&self, tb: &mut Tb) -> Result<()> { - tb.add_signer(self.clone())?; - - Ok(()) - } -} - -#[async_trait] -impl ViewOnlyAccount for ForcClientAccount { - fn address(&self) -> Address { - match self { - ForcClientAccount::Wallet(wallet) => wallet.address(), - ForcClientAccount::KmsSigner(account) => { - fuels_accounts::ViewOnlyAccount::address(account) - } - } - } - - fn try_provider(&self) -> Result<&Provider> { - match self { - ForcClientAccount::Wallet(wallet) => wallet.try_provider(), - ForcClientAccount::KmsSigner(account) => Ok(account.provider()), - } - } - - async fn get_asset_inputs_for_amount( - &self, - asset_id: AssetId, - amount: u128, - excluded_coins: Option>, - ) -> Result> { - match self { - ForcClientAccount::Wallet(wallet) => { - wallet - .get_asset_inputs_for_amount(asset_id, amount, excluded_coins) - .await - } - ForcClientAccount::KmsSigner(account) => { - account - .get_asset_inputs_for_amount(asset_id, amount, excluded_coins) - .await - } - } - } -} - -#[async_trait] -impl Signer for ForcClientAccount { - async fn sign(&self, message: Message) -> Result { - match self { - ForcClientAccount::Wallet(wallet) => wallet.signer().sign(message).await, - ForcClientAccount::KmsSigner(account) => account.sign(message).await, - } - } - - fn address(&self) -> Address { - match self { - ForcClientAccount::Wallet(wallet) => wallet.address(), - ForcClientAccount::KmsSigner(account) => fuels_core::traits::Signer::address(account), - } - } -} diff --git a/forc-plugins/forc-client/src/util/aws.rs b/forc-plugins/forc-client/src/util/aws.rs deleted file mode 100644 index 398edda74fb..00000000000 --- a/forc-plugins/forc-client/src/util/aws.rs +++ /dev/null @@ -1,269 +0,0 @@ -use async_trait::async_trait; -use aws_config::{default_provider::credentials::DefaultCredentialsChain, Region, SdkConfig}; -use aws_sdk_kms::config::Credentials; -use aws_sdk_kms::operation::get_public_key::GetPublicKeyOutput; -use aws_sdk_kms::primitives::Blob; -use aws_sdk_kms::types::{MessageType, SigningAlgorithmSpec}; -use aws_sdk_kms::{config::BehaviorVersion, Client}; -use fuel_crypto::Message; -use fuels::prelude::*; -use fuels::types::coin_type_id::CoinTypeId; -use fuels::types::input::Input; -use fuels_accounts::provider::Provider; -use fuels_accounts::{Account, ViewOnlyAccount}; -use fuels_core::traits::Signer; - -/// AWS configuration for the `AwsSigner` to be created. -/// De-facto way of creating the configuration is to load it from env. -#[derive(Debug, Clone)] -pub struct AwsConfig { - sdk_config: SdkConfig, -} - -impl AwsConfig { - /// Load configuration from environment variables. - /// For more details see: https://docs.rs/aws-config/latest/aws_config/ - pub async fn from_env() -> Self { - let loader = aws_config::defaults(BehaviorVersion::latest()) - .credentials_provider(DefaultCredentialsChain::builder().build().await); - - let loader = match std::env::var("E2E_TEST_AWS_ENDPOINT") { - Ok(url) => loader.endpoint_url(url), - _ => loader, - }; - - Self { - sdk_config: loader.load().await, - } - } - - pub async fn for_testing(url: String) -> Self { - let sdk_config = aws_config::defaults(BehaviorVersion::latest()) - .credentials_provider(Credentials::new( - "test", - "test", - None, - None, - "Static Credentials", - )) - .endpoint_url(url) - .region(Region::new("us-east-1")) // placeholder region for test - .load() - .await; - - Self { sdk_config } - } - - pub fn url(&self) -> Option<&str> { - self.sdk_config.endpoint_url() - } - - pub fn region(&self) -> Option<&Region> { - self.sdk_config.region() - } -} - -/// A configured `AwsClient` which allows using the AWS KMS SDK. -#[derive(Clone, Debug)] -pub struct AwsClient { - client: Client, -} - -impl AwsClient { - pub fn new(config: AwsConfig) -> Self { - let config = config.sdk_config; - let client = Client::new(&config); - - Self { client } - } - - pub fn inner(&self) -> &Client { - &self.client - } -} - -/// A signer which is capable of signing `fuel_crypto::Message`s using AWS KMS. -/// This is both a `Signer` and `Account`, which means it is directly usable -/// with most of the fuels-* calls, without any additional operations on the -/// representation. -#[derive(Clone, Debug)] -pub struct AwsSigner { - kms: AwsClient, - key_id: String, - address: Address, - public_key_bytes: Vec, - provider: Provider, -} - -async fn request_get_pubkey( - kms: &Client, - key_id: String, -) -> std::result::Result { - kms.get_public_key() - .key_id(key_id) - .send() - .await - .map_err(Into::into) -} - -/// Decode an AWS KMS Pubkey response. -fn decode_pubkey(resp: &GetPublicKeyOutput) -> std::result::Result, anyhow::Error> { - let raw = resp - .public_key - .as_ref() - .ok_or(anyhow::anyhow!("public key not found"))?; - Ok(raw.clone().into_inner()) -} - -async fn sign_with_kms( - client: &aws_sdk_kms::Client, - key_id: &str, - public_key_bytes: &[u8], - message: Message, -) -> anyhow::Result { - use k256::{ - ecdsa::{RecoveryId, VerifyingKey}, - pkcs8::DecodePublicKey, - }; - - let reply = client - .sign() - .key_id(key_id) - .signing_algorithm(SigningAlgorithmSpec::EcdsaSha256) - .message_type(MessageType::Digest) - .message(Blob::new(*message)) - .send() - .await - .inspect_err(|err| tracing::error!("Failed to sign with AWS KMS: {err:?}"))?; - let signature_der = reply - .signature - .ok_or_else(|| anyhow::anyhow!("no signature returned from AWS KMS"))? - .into_inner(); - // https://stackoverflow.com/a/71475108 - let sig = k256::ecdsa::Signature::from_der(&signature_der) - .map_err(|_| anyhow::anyhow!("invalid DER signature from AWS KMS"))?; - let sig = sig.normalize_s().unwrap_or(sig); - - // This is a hack to get the recovery id. The signature should be normalized - // before computing the recovery id, but aws kms doesn't support this, and - // instead always computes the recovery id from non-normalized signature. - // So instead the recovery id is determined by checking which variant matches - // the original public key. - - let recid1 = RecoveryId::new(false, false); - let recid2 = RecoveryId::new(true, false); - - let rec1 = VerifyingKey::recover_from_prehash(&*message, &sig, recid1); - let rec2 = VerifyingKey::recover_from_prehash(&*message, &sig, recid2); - - let correct_public_key = k256::PublicKey::from_public_key_der(public_key_bytes) - .map_err(|_| anyhow::anyhow!("invalid DER public key from AWS KMS"))? - .into(); - - let recovery_id = if rec1.map(|r| r == correct_public_key).unwrap_or(false) { - recid1 - } else if rec2.map(|r| r == correct_public_key).unwrap_or(false) { - recid2 - } else { - anyhow::bail!("Invalid signature generated (reduced-x form coordinate)"); - }; - - // Insert the recovery id into the signature - debug_assert!( - !recovery_id.is_x_reduced(), - "reduced-x form coordinates are caught by the if-else chain above" - ); - let v = recovery_id.is_y_odd() as u8; - let mut signature = <[u8; 64]>::from(sig.to_bytes()); - signature[32] = (v << 7) | (signature[32] & 0x7f); - Ok(fuel_crypto::Signature::from_bytes(signature)) -} - -impl AwsSigner { - pub async fn new( - kms: AwsClient, - key_id: String, - provider: Provider, - ) -> std::result::Result { - use k256::pkcs8::DecodePublicKey; - - let resp = request_get_pubkey(kms.inner(), key_id.clone()).await?; - let public_key_bytes = decode_pubkey(&resp)?; - let k256_public_key = k256::PublicKey::from_public_key_der(&public_key_bytes)?; - - let public_key = fuel_crypto::PublicKey::from(k256_public_key); - let hashed = public_key.hash(); - let address = Address::from(*hashed); - Ok(Self { - kms, - key_id, - address, - public_key_bytes, - provider, - }) - } - - /// Sign a digest with the key associated with a key ID. - pub async fn sign_message_with_key( - &self, - key_id: String, - message: Message, - ) -> std::result::Result { - sign_with_kms(self.kms.inner(), &key_id, &self.public_key_bytes, message).await - } - - /// Sign a digest with this signer's key. - pub async fn sign_message( - &self, - message: Message, - ) -> std::result::Result { - self.sign_message_with_key(self.key_id.clone(), message) - .await - } - - pub fn provider(&self) -> &Provider { - &self.provider - } -} - -#[async_trait] -impl Signer for AwsSigner { - async fn sign(&self, message: Message) -> Result { - let sig = self.sign_message(message).await.map_err(|_| { - fuels_core::types::errors::Error::Other("aws signer failed".to_string()) - })?; - Ok(sig) - } - - fn address(&self) -> Address { - self.address - } -} - -#[async_trait] -impl ViewOnlyAccount for AwsSigner { - fn address(&self) -> Address { - self.address - } - - fn try_provider(&self) -> Result<&Provider> { - Ok(&self.provider) - } - - async fn get_asset_inputs_for_amount( - &self, - asset_id: AssetId, - amount: u128, - excluded_coins: Option>, - ) -> Result> { - Ok(self - .get_spendable_resources(asset_id, amount, excluded_coins) - .await? - .into_iter() - .map(Input::resource_signed) - .collect::>()) - } -} - -#[async_trait] -impl Account for AwsSigner {} diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs deleted file mode 100644 index f1ab46793e4..00000000000 --- a/forc-plugins/forc-client/src/util/encode.rs +++ /dev/null @@ -1,142 +0,0 @@ -use anyhow::Context; -use fuel_abi_types::abi::full_program::FullTypeApplication; -use std::str::FromStr; -use sway_types::u256::U256; - -/// A wrapper around fuels_core::types::Token, which enables serde de/serialization. -#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] -pub(crate) struct Token(pub(crate) fuels_core::types::Token); - -#[derive(Debug, PartialEq, Eq)] -pub(crate) enum Type { - Unit, - U8, - U16, - U32, - U64, - U256, - Bool, -} - -impl TryFrom<&FullTypeApplication> for Type { - type Error = anyhow::Error; - - fn try_from(value: &FullTypeApplication) -> Result { - let type_field_string = &value.type_decl.type_field; - Type::from_str(type_field_string) - } -} - -impl Token { - /// Generate a new token using provided type information and the value for the argument. - /// - /// Generates an error if there is a mismatch between the type information and the provided - /// value for that type. - #[allow(dead_code)] - pub(crate) fn from_type_and_value(arg_type: &Type, value: &str) -> anyhow::Result { - match arg_type { - Type::Unit => Ok(Token(fuels_core::types::Token::Unit)), - Type::U8 => { - let u8_val = value.parse::()?; - Ok(Token(fuels_core::types::Token::U8(u8_val))) - } - Type::U16 => { - let u16_val = value.parse::()?; - Ok(Token(fuels_core::types::Token::U16(u16_val))) - } - Type::U32 => { - let u32_val = value.parse::()?; - Ok(Token(fuels_core::types::Token::U32(u32_val))) - } - Type::U64 => { - let u64_val = value.parse::()?; - Ok(Token(fuels_core::types::Token::U64(u64_val))) - } - Type::U256 => { - let v = value.parse::().context("u256 literal out of range")?; - let bytes = v.to_be_bytes(); - Ok(Token(fuels_core::types::Token::U256(bytes.into()))) - } - Type::Bool => { - let bool_val = value.parse::()?; - Ok(Token(fuels_core::types::Token::Bool(bool_val))) - } - } - } -} - -impl FromStr for Type { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s { - "()" => Ok(Type::Unit), - "u8" => Ok(Type::U8), - "u16" => Ok(Type::U16), - "u32" => Ok(Type::U32), - "u64" => Ok(Type::U64), - "u256" => Ok(Type::U256), - "bool" => Ok(Type::Bool), - other => anyhow::bail!("{other} type is not supported."), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_token_generation_success() { - let u8_token = Token::from_type_and_value(&Type::U8, "1").unwrap(); - let u16_token = Token::from_type_and_value(&Type::U16, "1").unwrap(); - let u32_token = Token::from_type_and_value(&Type::U32, "1").unwrap(); - let u64_token = Token::from_type_and_value(&Type::U64, "1").unwrap(); - let bool_token = Token::from_type_and_value(&Type::Bool, "true").unwrap(); - - let generated_tokens = [u8_token, u16_token, u32_token, u64_token, bool_token]; - let expected_tokens = [ - Token(fuels_core::types::Token::U8(1)), - Token(fuels_core::types::Token::U16(1)), - Token(fuels_core::types::Token::U32(1)), - Token(fuels_core::types::Token::U64(1)), - Token(fuels_core::types::Token::Bool(true)), - ]; - - assert_eq!(generated_tokens, expected_tokens) - } - - #[test] - #[should_panic] - fn test_token_generation_fail_type_mismatch() { - Token::from_type_and_value(&Type::U8, "false").unwrap(); - } - - #[test] - fn test_type_generation_success() { - let possible_type_list = ["()", "u8", "u16", "u32", "u64", "u256", "bool"]; - let types = possible_type_list - .iter() - .map(|type_str| Type::from_str(type_str)) - .collect::>>() - .unwrap(); - - let expected_types = vec![ - Type::Unit, - Type::U8, - Type::U16, - Type::U32, - Type::U64, - Type::U256, - Type::Bool, - ]; - assert_eq!(types, expected_types) - } - - #[test] - #[should_panic(expected = "u2 type is not supported.")] - fn test_type_generation_fail_invalid_type() { - let invalid_type_str = "u2"; - Type::from_str(invalid_type_str).unwrap(); - } -} diff --git a/forc-plugins/forc-client/src/util/mod.rs b/forc-plugins/forc-client/src/util/mod.rs deleted file mode 100644 index 66515215916..00000000000 --- a/forc-plugins/forc-client/src/util/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod account; -pub mod aws; -pub(crate) mod encode; -pub(crate) mod pkg; -pub(crate) mod target; -pub mod tx; diff --git a/forc-plugins/forc-client/src/util/pkg.rs b/forc-plugins/forc-client/src/util/pkg.rs deleted file mode 100644 index aad97d0e151..00000000000 --- a/forc-plugins/forc-client/src/util/pkg.rs +++ /dev/null @@ -1,87 +0,0 @@ -use anyhow::Result; -use forc_pkg::manifest::GenericManifestFile; -use forc_pkg::{self as pkg, manifest::ManifestFile, BuildOpts, BuildPlan}; -use forc_util::user_forc_directory; -use pkg::{build_with_options, BuiltPackage, PackageManifestFile}; -use std::fs::File; -use std::io::{Read, Write}; -use std::path::PathBuf; -use std::{collections::HashMap, path::Path, sync::Arc}; - -/// The name of the folder that forc generated proxy contract project will reside at. -pub const GENERATED_CONTRACT_FOLDER_NAME: &str = ".generated_contracts"; -pub const PROXY_CONTRACT_BIN: &[u8] = include_bytes!("../../proxy_abi/proxy_contract.bin"); -pub const PROXY_CONTRACT_STORAGE_SLOTS: &str = - include_str!("../../proxy_abi/proxy_contract-storage_slots.json"); -pub const PROXY_BIN_FILE_NAME: &str = "proxy.bin"; -pub const PROXY_STORAGE_SLOTS_FILE_NAME: &str = "proxy-storage_slots.json"; - -/// Updates the given package manifest file such that the address field under the proxy table updated to the given value. -/// Updated manifest file is written back to the same location, without thouching anything else such as comments etc. -/// A safety check is done to ensure the proxy table exists before attempting to update the value. -pub(crate) fn update_proxy_address_in_manifest( - address: &str, - manifest: &PackageManifestFile, -) -> Result<()> { - let mut toml = String::new(); - let mut file = File::open(manifest.path())?; - file.read_to_string(&mut toml)?; - let mut manifest_toml = toml.parse::()?; - if manifest.proxy().is_some() { - manifest_toml["proxy"]["address"] = toml_edit::value(address); - let mut file = std::fs::OpenOptions::new() - .write(true) - .truncate(true) - .open(manifest.path())?; - file.write_all(manifest_toml.to_string().as_bytes())?; - } - Ok(()) -} - -/// Creates a proxy contract project at the given path, adds a forc.toml and source file. -pub(crate) fn create_proxy_contract(pkg_name: &str) -> Result { - // Create the proxy contract folder. - let proxy_contract_dir = user_forc_directory() - .join(GENERATED_CONTRACT_FOLDER_NAME) - .join(format!("{pkg_name}-proxy")); - std::fs::create_dir_all(&proxy_contract_dir)?; - std::fs::write( - proxy_contract_dir.join(PROXY_BIN_FILE_NAME), - PROXY_CONTRACT_BIN, - )?; - std::fs::write( - proxy_contract_dir.join(PROXY_STORAGE_SLOTS_FILE_NAME), - PROXY_CONTRACT_STORAGE_SLOTS, - )?; - - Ok(proxy_contract_dir) -} - -pub(crate) fn built_pkgs(path: &Path, build_opts: &BuildOpts) -> Result>> { - let manifest_file = ManifestFile::from_dir(path)?; - let lock_path = manifest_file.lock_path()?; - let build_plan = BuildPlan::from_lock_and_manifests( - &lock_path, - &manifest_file.member_manifests()?, - build_opts.pkg.locked, - build_opts.pkg.offline, - &build_opts.pkg.ipfs_node, - )?; - let graph = build_plan.graph(); - let built = build_with_options(build_opts, None)?; - let mut members: HashMap<&pkg::Pinned, Arc<_>> = built.into_members().collect(); - let mut built_pkgs = Vec::new(); - - for member_index in build_plan.member_nodes() { - let pkg = &graph[member_index]; - // Check if the current member is built. - // - // For individual members of the workspace, member nodes would be iterating - // over all the members but only the relevant member would be built. - if let Some(built_pkg) = members.remove(pkg) { - built_pkgs.push(built_pkg); - } - } - - Ok(built_pkgs) -} diff --git a/forc-plugins/forc-client/src/util/target.rs b/forc-plugins/forc-client/src/util/target.rs deleted file mode 100644 index 52d589bff88..00000000000 --- a/forc-plugins/forc-client/src/util/target.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::constants::{ - DEVNET_ENDPOINT_URL, DEVNET_FAUCET_URL, MAINNET_ENDPOINT_URL, MAINNET_EXPLORER_URL, NODE_URL, - TESTNET_ENDPOINT_URL, TESTNET_EXPLORER_URL, TESTNET_FAUCET_URL, -}; -use anyhow::{bail, Result}; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -/// Possible target values that forc-client can interact with. -pub enum Target { - Mainnet, - Testnet, - Devnet, - Local, -} - -impl Default for Target { - fn default() -> Self { - Self::Local - } -} - -impl Target { - pub fn target_url(&self) -> String { - let url = match self { - Target::Mainnet => MAINNET_ENDPOINT_URL, - Target::Testnet => TESTNET_ENDPOINT_URL, - Target::Devnet => DEVNET_ENDPOINT_URL, - Target::Local => NODE_URL, - }; - url.to_string() - } - - pub fn from_target_url(target_url: &str) -> Option { - match target_url { - TESTNET_ENDPOINT_URL => Some(Target::Testnet), - MAINNET_ENDPOINT_URL => Some(Target::Mainnet), - DEVNET_ENDPOINT_URL => Some(Target::Devnet), - NODE_URL => Some(Target::Local), - _ => None, - } - } - - pub fn local() -> Self { - Target::Local - } - - pub fn devnet() -> Self { - Target::Devnet - } - - pub fn testnet() -> Self { - Target::Testnet - } - - pub fn mainnet() -> Self { - Target::Mainnet - } - - pub fn faucet_url(&self) -> Option { - match self { - Target::Mainnet => None, - Target::Testnet => Some(TESTNET_FAUCET_URL.to_string()), - Target::Devnet => Some(DEVNET_FAUCET_URL.to_string()), - Target::Local => Some("http://localhost:3000".to_string()), - } - } - - pub fn explorer_url(&self) -> Option { - match self { - Target::Mainnet => Some(MAINNET_EXPLORER_URL.to_string()), - Target::Testnet => Some(TESTNET_EXPLORER_URL.to_string()), - Target::Devnet => None, - _ => None, - } - } -} - -impl FromStr for Target { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s { - "Fuel Sepolia Testnet" => Ok(Target::Testnet), - "Ignition" => Ok(Target::Mainnet), - "local" => Ok(Target::Local), - "Devnet" | "devnet" => Ok(Target::Devnet), - _ => bail!( - "'{s}' is not a valid target name. Possible values: '{}', '{}', '{}', '{}'", - Target::Testnet, - Target::Mainnet, - Target::Local, - Target::Devnet, - ), - } - } -} - -impl std::fmt::Display for Target { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let s = match self { - Target::Mainnet => "Ignition", - Target::Testnet => "Fuel Sepolia Testnet", - Target::Devnet => "Devnet", - Target::Local => "local", - }; - write!(f, "{s}") - } -} diff --git a/forc-plugins/forc-client/src/util/tx.rs b/forc-plugins/forc-client/src/util/tx.rs deleted file mode 100644 index 61a16e67383..00000000000 --- a/forc-plugins/forc-client/src/util/tx.rs +++ /dev/null @@ -1,379 +0,0 @@ -use crate::{ - constants::DEFAULT_PRIVATE_KEY, - util::{account::ForcClientAccount, aws::AwsSigner, target::Target}, -}; -use anyhow::Result; -use dialoguer::{theme::ColorfulTheme, Confirm, Password, Select}; -use forc_tracing::{println_action_green, println_warning}; -use forc_wallet::{ - account::{derive_secret_key, new_at_index_cli}, - balance::{collect_accounts_with_verification, AccountBalances, AccountVerification}, - import::{import_wallet_cli, Import}, - new::{new_wallet_cli, New}, - utils::default_wallet_path, -}; -use fuel_crypto::SecretKey; -use fuel_tx::{AssetId, ContractId}; -use fuels::{ - macros::abigen, programs::responses::CallResponse, types::checksum_address::checksum_encode, -}; -use fuels_accounts::{ - provider::Provider, - signers::private_key::PrivateKeySigner, - wallet::{Unlocked, Wallet}, - ViewOnlyAccount, -}; - -use std::{collections::BTreeMap, path::Path, str::FromStr}; - -use super::aws::{AwsClient, AwsConfig}; - -type AccountsMap = BTreeMap; - -#[derive(PartialEq, Eq)] -pub enum SignerSelectionMode { - /// Holds the password of forc-wallet instance. - ForcWallet(String), - /// Holds ARN of the AWS signer. - AwsSigner(String), - Manual, -} - -fn ask_user_yes_no_question(question: &str) -> Result { - let answer = Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt(question) - .default(false) - .show_default(false) - .interact()?; - Ok(answer) -} - -fn ask_user_with_options(question: &str, options: &[&str], default: usize) -> Result { - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt(question) - .items(options) - .default(default) - .interact()?; - Ok(selection) -} - -async fn collect_user_accounts( - wallet_path: &Path, - password: &str, - node_url: &str, -) -> Result { - let verification = AccountVerification::Yes(password.to_string()); - let node_url = reqwest::Url::parse(node_url) - .map_err(|e| anyhow::anyhow!("Failed to parse node URL: {}", e))?; - let accounts = collect_accounts_with_verification(wallet_path, verification, &node_url) - .await - .map_err(|e| { - if e.to_string().contains("Mac Mismatch") { - anyhow::anyhow!("Failed to access forc-wallet vault. Please check your password") - } else { - e - } - })?; - let accounts = accounts - .into_iter() - .map(|(index, address)| { - let bytes: [u8; fuel_tx::Address::LEN] = address.into(); - (index, fuel_tx::Address::from(bytes)) - }) - .collect(); - Ok(accounts) -} - -pub(crate) fn prompt_forc_wallet_password() -> Result { - let password = Password::with_theme(&ColorfulTheme::default()) - .with_prompt("Wallet password") - .allow_empty_password(true) - .interact()?; - - Ok(password) -} - -pub(crate) async fn check_and_create_wallet_at_default_path(wallet_path: &Path) -> Result<()> { - if !wallet_path.exists() { - let question = - format!("Could not find a wallet at {wallet_path:?}, please select an option: "); - let wallet_options = ask_user_with_options( - &question, - &["Create new wallet", "Import existing wallet"], - 0, - )?; - let ctx = forc_wallet::CliContext { - wallet_path: wallet_path.to_path_buf(), - node_url: forc_wallet::network::DEFAULT.parse().unwrap(), - }; - match wallet_options { - 0 => { - new_wallet_cli(&ctx, New { force: false, cache_accounts: None }).await?; - println!("Wallet created successfully."); - } - 1 => { - import_wallet_cli(&ctx, Import { force: false, cache_accounts: None }).await?; - println!("Wallet imported successfully."); - }, - _ => anyhow::bail!("Refused to create or import a new wallet. If you don't want to use forc-wallet, you can sign this transaction manually with --manual-signing flag."), - } - // Derive first account for the fresh wallet we created. - new_at_index_cli(&ctx, 0).await?; - println!("Account derived successfully."); - } - Ok(()) -} - -pub(crate) fn secret_key_from_forc_wallet( - wallet_path: &Path, - account_index: usize, - password: &str, -) -> Result { - let secret_key = derive_secret_key(wallet_path, account_index, password).map_err(|e| { - if e.to_string().contains("Mac Mismatch") { - anyhow::anyhow!("Failed to access forc-wallet vault. Please check your password") - } else { - e - } - })?; - SecretKey::try_from(secret_key.as_ref()) - .map_err(|e| anyhow::anyhow!("Failed to convert secret key: {e}")) -} - -pub(crate) fn select_manual_secret_key( - default_signer: bool, - signing_key: Option, -) -> Option { - match (default_signer, signing_key) { - // Note: unwrap is safe here as we already know that 'DEFAULT_PRIVATE_KEY' is a valid private key. - (true, None) => Some(SecretKey::from_str(DEFAULT_PRIVATE_KEY).unwrap()), - (true, Some(signing_key)) => { - println_warning("Signing key is provided while requesting to sign with a default signer. Using signing key"); - Some(signing_key) - } - (false, None) => None, - (false, Some(signing_key)) => Some(signing_key), - } -} - -/// Collect and return balances of each account in the accounts map. -async fn collect_account_balances( - accounts_map: &AccountsMap, - provider: &Provider, -) -> Result { - let accounts: Vec<_> = accounts_map - .values() - .map(|addr| Wallet::new_locked(*addr, provider.clone())) - .collect(); - - futures::future::try_join_all(accounts.iter().map(|acc| acc.get_balances())) - .await - .map_err(|e| anyhow::anyhow!("{e}")) -} - -/// Format collected account balances for each asset type, including only the balance of the base asset that can be used to pay gas. -pub fn format_base_asset_account_balances( - accounts_map: &AccountsMap, - account_balances: &AccountBalances, - base_asset_id: &AssetId, -) -> Result> { - accounts_map - .iter() - .zip(account_balances) - .map(|((ix, address), balance)| { - let base_asset_amount = balance - .get(&base_asset_id.to_string()) - .copied() - .unwrap_or(0); - let raw_addr = format!("0x{address}"); - let checksum_addr = checksum_encode(&raw_addr)?; - let eth_amount = base_asset_amount as f64 / 1_000_000_000.0; - Ok(format!("[{ix}] {checksum_addr} - {eth_amount} ETH")) - }) - .collect::>>() -} - -// TODO: Simplify the function signature once https://github.com/FuelLabs/sway/issues/6071 is closed. -pub(crate) async fn select_account( - wallet_mode: &SignerSelectionMode, - default_sign: bool, - signing_key: Option, - provider: &Provider, - tx_count: usize, -) -> Result { - let chain_info = provider.chain_info().await?; - match wallet_mode { - SignerSelectionMode::ForcWallet(password) => { - let wallet_path = default_wallet_path(); - let accounts = collect_user_accounts(&wallet_path, password, provider.url()).await?; - let account_balances = collect_account_balances(&accounts, provider).await?; - - let total_balance = account_balances - .iter() - .flat_map(|account| account.values()) - .sum::(); - if total_balance == 0 { - let first_account = accounts - .get(&0) - .ok_or_else(|| anyhow::anyhow!("No account derived for this wallet"))?; - let target = Target::from_str(&chain_info.name).unwrap_or_default(); - let message = if let Some(faucet_url) = target.faucet_url() { - format!( - "Your wallet does not have any funds to pay for the transaction.\ - \n\nIf you are interacting with a testnet, consider using the faucet.\ - \n-> {target} network faucet: {faucet_url}/?address={first_account}\ - \nIf you are interacting with a local node, consider providing a chainConfig which funds your account." - ) - } else { - "Your wallet does not have any funds to pay for the transaction.".to_string() - }; - anyhow::bail!(message) - } - - // TODO: Do this via forc-wallet once the functionality is exposed. - // TODO: calculate the number of transactions to sign and ask the user to confirm. - let question = format!( - "Do you agree to sign {tx_count} transaction{}?", - if tx_count > 1 { "s" } else { "" } - ); - let accepted = ask_user_yes_no_question(&question)?; - if !accepted { - anyhow::bail!("User refused to sign"); - } - - let wallet = select_local_wallet_account(password, provider).await?; - Ok(ForcClientAccount::Wallet(wallet)) - } - SignerSelectionMode::Manual => { - let secret_key = select_manual_secret_key(default_sign, signing_key) - .ok_or_else(|| anyhow::anyhow!("missing manual secret key"))?; - let signer = PrivateKeySigner::new(secret_key); - let wallet = Wallet::new(signer, provider.clone()); - Ok(ForcClientAccount::Wallet(wallet)) - } - SignerSelectionMode::AwsSigner(arn) => { - let aws_config = AwsConfig::from_env().await; - let aws_client = AwsClient::new(aws_config); - let aws_signer = AwsSigner::new(aws_client, arn.clone(), provider.clone()).await?; - - let account = ForcClientAccount::KmsSigner(aws_signer); - Ok(account) - } - } -} - -pub(crate) async fn select_local_wallet_account( - password: &str, - provider: &Provider, -) -> Result>> { - let wallet_path = default_wallet_path(); - let accounts = collect_user_accounts(&wallet_path, password, provider.url()).await?; - let account_balances = collect_account_balances(&accounts, provider).await?; - let consensus_parameters = provider.consensus_parameters().await?; - let base_asset_id = consensus_parameters.base_asset_id(); - let selections = - format_base_asset_account_balances(&accounts, &account_balances, base_asset_id)?; - - let mut account_index; - loop { - account_index = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Wallet account") - .max_length(5) - .items(&selections[..]) - .default(0) - .interact()?; - - if accounts.contains_key(&account_index) { - break; - } - let options: Vec = accounts - .keys() - .map(|key| { - let raw_addr = format!("0x{key}"); - let checksum_addr = checksum_encode(&raw_addr)?; - Ok(checksum_addr) - }) - .collect::>>()?; - println_warning(&format!( - "\"{}\" is not a valid account.\nPlease choose a valid option from {}", - account_index, - options.join(","), - )); - } - - let secret_key = secret_key_from_forc_wallet(&wallet_path, account_index, password)?; - let signer = PrivateKeySigner::new(secret_key); - let wallet = Wallet::new(signer, provider.clone()); - Ok(wallet) -} - -pub async fn update_proxy_contract_target( - account: &ForcClientAccount, - proxy_contract_id: ContractId, - new_target: ContractId, -) -> Result> { - abigen!(Contract(name = "ProxyContract", abi = "{\"programType\":\"contract\",\"specVersion\":\"1.1\",\"encodingVersion\":\"1\",\"concreteTypes\":[{\"type\":\"()\",\"concreteTypeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"type\":\"enum standards::src5::AccessError\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\",\"metadataTypeId\":1},{\"type\":\"enum standards::src5::State\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"metadataTypeId\":2},{\"type\":\"enum std::option::Option\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"metadataTypeId\":4,\"typeArguments\":[\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\",\"metadataTypeId\":5},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\",\"metadataTypeId\":6},{\"type\":\"str\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"},{\"type\":\"struct std::contract_id::ContractId\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\",\"metadataTypeId\":9},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\",\"metadataTypeId\":10},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\",\"metadataTypeId\":11}],\"metadataTypes\":[{\"type\":\"b256\",\"metadataTypeId\":0},{\"type\":\"enum standards::src5::AccessError\",\"metadataTypeId\":1,\"components\":[{\"name\":\"NotOwner\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum standards::src5::State\",\"metadataTypeId\":2,\"components\":[{\"name\":\"Uninitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Initialized\",\"typeId\":3},{\"name\":\"Revoked\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum std::identity::Identity\",\"metadataTypeId\":3,\"components\":[{\"name\":\"Address\",\"typeId\":8},{\"name\":\"ContractId\",\"typeId\":9}]},{\"type\":\"enum std::option::Option\",\"metadataTypeId\":4,\"components\":[{\"name\":\"None\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Some\",\"typeId\":7}],\"typeParameters\":[7]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"metadataTypeId\":5,\"components\":[{\"name\":\"CannotReinitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"metadataTypeId\":6,\"components\":[{\"name\":\"CannotUninitialize\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"generic T\",\"metadataTypeId\":7},{\"type\":\"struct std::address::Address\",\"metadataTypeId\":8,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct std::contract_id::ContractId\",\"metadataTypeId\":9,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"metadataTypeId\":10,\"components\":[{\"name\":\"new_proxy_owner\",\"typeId\":2}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"metadataTypeId\":11,\"components\":[{\"name\":\"new_target\",\"typeId\":9}]}],\"functions\":[{\"inputs\":[],\"name\":\"proxy_target\",\"output\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [Option] - The new proxy contract to which all fallback calls will be passed or `None`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[{\"name\":\"new_target\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"}],\"name\":\"set_proxy_target\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Change the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called by the `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_target`: [ContractId] - The new proxy contract to which all fallback calls will be passed.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When not called by `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Write: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\",\"write\"]}]},{\"inputs\":[],\"name\":\"proxy_owner\",\"output\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the owner of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [State] - Represents the state of ownership for this contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[],\"name\":\"initialize_proxy\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Initializes the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method sets the storage values using the values of the configurable constants `INITIAL_TARGET` and `INITIAL_OWNER`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This then allows methods that write to storage to be called.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called once.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When `storage::SRC14.proxy_owner` is not [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `2`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]},{\"inputs\":[{\"name\":\"new_proxy_owner\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\"}],\"name\":\"set_proxy_owner\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Changes proxy ownership to the passed State.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can be used to transfer ownership between Identities or to revoke ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_proxy_owner`: [State] - The new state of the proxy ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the sender is not the current proxy owner.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the new state of the proxy ownership is [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]}],\"loggedTypes\":[{\"logId\":\"4571204900286667806\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\"},{\"logId\":\"2151606668983994881\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\"},{\"logId\":\"2161305517876418151\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\"},{\"logId\":\"4354576968059844266\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\"},{\"logId\":\"10870989709723147660\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\"},{\"logId\":\"10098701174489624218\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"}],\"messagesTypes\":[],\"configurables\":[{\"name\":\"INITIAL_TARGET\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"offset\":13368},{\"name\":\"INITIAL_OWNER\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"offset\":13320}]}",)); - - let proxy_contract = ProxyContract::new(proxy_contract_id, account.clone()); - - let result = proxy_contract - .methods() - .set_proxy_target(new_target) - .call() - .await?; - println_action_green( - "Updated", - &format!("proxy contract target to 0x{new_target}"), - ); - Ok(result) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::{BTreeMap, HashMap}; - - #[test] - fn test_format_base_asset_account_balances() { - let mut accounts_map: AccountsMap = BTreeMap::new(); - - let address1 = fuel_tx::Address::from_str( - "7bbd8a4ea06e94461b959ab18d35802bbac3cf47e2bf29195f7db2ce41630cd7", - ) - .expect("address1"); - - let address2 = fuel_tx::Address::from_str( - "99bd8a4ea06e94461b959ab18d35802bbac3cf47e2bf29195f7db2ce41630cd7", - ) - .expect("address2"); - - let base_asset_id = AssetId::zeroed(); - - accounts_map.insert(0, address1); - accounts_map.insert(1, address2); - - let mut account_balances: AccountBalances = Vec::new(); - let mut balance1 = HashMap::new(); - balance1.insert(base_asset_id.to_string(), 1_500_000_000); - balance1.insert("other_asset".to_string(), 2_000_000_000); - account_balances.push(balance1); - - let mut balance2 = HashMap::new(); - balance2.insert("other_asset".to_string(), 3_000_000_000); - account_balances.push(balance2); - - let address1_expected = - "0x7bBD8a4ea06E94461b959aB18d35802BbAC3cf47e2bF29195F7db2CE41630CD7"; - let address2_expected = - "0x99Bd8a4eA06E94461b959AB18d35802bBaC3Cf47E2Bf29195f7DB2cE41630cD7"; - let expected = vec![ - format!("[0] {address1_expected} - 1.5 ETH"), - format!("[1] {address2_expected} - 0 ETH"), - ]; - - let result = - format_base_asset_account_balances(&accounts_map, &account_balances, &base_asset_id) - .unwrap(); - assert_eq!(result, expected); - } -} diff --git a/forc-plugins/forc-client/test/data/big_contract/.gitignore b/forc-plugins/forc-client/test/data/big_contract/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/big_contract/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/big_contract/Forc.toml b/forc-plugins/forc-client/test/data/big_contract/Forc.toml deleted file mode 100644 index 539c466315e..00000000000 --- a/forc-plugins/forc-client/test/data/big_contract/Forc.toml +++ /dev/null @@ -1,8 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "big_contract" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } diff --git a/forc-plugins/forc-client/test/data/big_contract/big_contract-abi.json b/forc-plugins/forc-client/test/data/big_contract/big_contract-abi.json deleted file mode 100644 index f6967464149..00000000000 --- a/forc-plugins/forc-client/test/data/big_contract/big_contract-abi.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "programType": "contract", - "specVersion": "1.1", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "()", - "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "type": "(bool, u64)", - "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", - "metadataTypeId": 0 - }, - { - "type": "[bool; 3]", - "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", - "metadataTypeId": 5 - }, - { - "type": "[enum Location; 2]", - "concreteTypeId": "fb7957f865b6c8ee87b25381a2861c279107dc2fff0e6bd25586198dc65470d0", - "metadataTypeId": 1 - }, - { - "type": "[struct SimpleStruct; 3]", - "concreteTypeId": "96dffad5a46b9db38f00f4431235e7e9bab7a85e8ee9a514aa81063d429ceb7a", - "metadataTypeId": 7 - }, - { - "type": "[u64; 3]", - "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", - "metadataTypeId": 6 - }, - { - "type": "b256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - }, - { - "type": "bool", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "type": "enum Location", - "concreteTypeId": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb", - "metadataTypeId": 9 - }, - { - "type": "str[4]", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" - }, - { - "type": "struct Person", - "concreteTypeId": "a6b272bcf0f572840e2cc1241bdcf8d42db51eeb1aead750c8e608160f83f926", - "metadataTypeId": 11 - }, - { - "type": "struct SimpleStruct", - "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", - "metadataTypeId": 12 - }, - { - "type": "u16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - }, - { - "type": "u256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" - }, - { - "type": "u32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "type": "u64", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "type": "u8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "metadataTypes": [ - { - "type": "(_, _)", - "metadataTypeId": 0, - "components": [ - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "__tuple_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ] - }, - { - "type": "[_; 2]", - "metadataTypeId": 1, - "components": [ - { - "name": "__array_element", - "typeId": 9 - } - ] - }, - { - "type": "[_; 2]", - "metadataTypeId": 2, - "components": [ - { - "name": "__array_element", - "typeId": 8 - } - ] - }, - { - "type": "[_; 2]", - "metadataTypeId": 3, - "components": [ - { - "name": "__array_element", - "typeId": 12 - } - ] - }, - { - "type": "[_; 2]", - "metadataTypeId": 4, - "components": [ - { - "name": "__array_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ] - }, - { - "type": "[_; 3]", - "metadataTypeId": 5, - "components": [ - { - "name": "__array_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - } - ] - }, - { - "type": "[_; 3]", - "metadataTypeId": 6, - "components": [ - { - "name": "__array_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ] - }, - { - "type": "[_; 3]", - "metadataTypeId": 7, - "components": [ - { - "name": "__array_element", - "typeId": 12 - } - ] - }, - { - "type": "enum Color", - "metadataTypeId": 8, - "components": [ - { - "name": "Red", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "name": "Blue", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ] - }, - { - "type": "enum Location", - "metadataTypeId": 9, - "components": [ - { - "name": "Earth", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "Mars", - "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" - }, - { - "name": "SimpleJupiter", - "typeId": 8 - }, - { - "name": "Jupiter", - "typeId": 2 - }, - { - "name": "SimplePluto", - "typeId": 12 - }, - { - "name": "Pluto", - "typeId": 3 - } - ] - }, - { - "type": "str", - "metadataTypeId": 10 - }, - { - "type": "struct Person", - "metadataTypeId": 11, - "components": [ - { - "name": "name", - "typeId": 10 - }, - { - "name": "age", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "alive", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "location", - "typeId": 9 - }, - { - "name": "some_tuple", - "typeId": 0 - }, - { - "name": "some_array", - "typeId": 4 - }, - { - "name": "some_b256", - "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - } - ] - }, - { - "type": "struct SimpleStruct", - "metadataTypeId": 12, - "components": [ - { - "name": "a", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "b", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ] - } - ], - "functions": [ - { - "inputs": [ - { - "name": "aoe", - "concreteTypeId": "fb7957f865b6c8ee87b25381a2861c279107dc2fff0e6bd25586198dc65470d0" - } - ], - "name": "array_of_enum_input_output", - "output": "fb7957f865b6c8ee87b25381a2861c279107dc2fff0e6bd25586198dc65470d0", - "attributes": null - }, - { - "inputs": [], - "name": "assert_configurables", - "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "attributes": null - }, - { - "inputs": [ - { - "name": "loc", - "concreteTypeId": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb" - } - ], - "name": "enum_input_output", - "output": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb", - "attributes": null - }, - { - "inputs": [ - { - "name": "index", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ], - "name": "get_storage_location", - "output": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [ - { - "name": "index", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ], - "name": "get_storage_simple", - "output": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [ - { - "name": "index", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ], - "name": "get_storage_u16", - "output": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [], - "name": "large_blob", - "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "attributes": null - }, - { - "inputs": [ - { - "name": "value", - "concreteTypeId": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb" - } - ], - "name": "push_storage_location", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read", - "write" - ] - } - ] - }, - { - "inputs": [ - { - "name": "value", - "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be" - } - ], - "name": "push_storage_simple", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read", - "write" - ] - } - ] - }, - { - "inputs": [ - { - "name": "value", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - } - ], - "name": "push_storage_u16", - "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read", - "write" - ] - } - ] - }, - { - "inputs": [ - { - "name": "person", - "concreteTypeId": "a6b272bcf0f572840e2cc1241bdcf8d42db51eeb1aead750c8e608160f83f926" - } - ], - "name": "struct_input_output", - "output": "a6b272bcf0f572840e2cc1241bdcf8d42db51eeb1aead750c8e608160f83f926", - "attributes": null - } - ], - "loggedTypes": [], - "messagesTypes": [], - "configurables": [ - { - "name": "BOOL", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 392688 - }, - { - "name": "U8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "offset": 392832 - }, - { - "name": "U16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "offset": 392776 - }, - { - "name": "U32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", - "offset": 392816 - }, - { - "name": "U64", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", - "offset": 392824 - }, - { - "name": "U256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", - "offset": 392784 - }, - { - "name": "B256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", - "offset": 392656 - }, - { - "name": "CONFIGURABLE_STRUCT", - "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", - "offset": 392736 - }, - { - "name": "CONFIGURABLE_ENUM", - "concreteTypeId": "6966f221767e2951c5d68ba5e81e897449375a9f51f5e4c9e3b65f1dd7defabb", - "offset": 392696 - }, - { - "name": "ARRAY_BOOL", - "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", - "offset": 392512 - }, - { - "name": "ARRAY_U64", - "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", - "offset": 392632 - }, - { - "name": "ARRAY_LOCATION", - "concreteTypeId": "fb7957f865b6c8ee87b25381a2861c279107dc2fff0e6bd25586198dc65470d0", - "offset": 392520 - }, - { - "name": "ARRAY_SIMPLE_STRUCT", - "concreteTypeId": "96dffad5a46b9db38f00f4431235e7e9bab7a85e8ee9a514aa81063d429ceb7a", - "offset": 392600 - }, - { - "name": "TUPLE_BOOL_U64", - "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", - "offset": 392760 - }, - { - "name": "STR_4", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", - "offset": 392752 - } - ] -} \ No newline at end of file diff --git a/forc-plugins/forc-client/test/data/big_contract/src/main.sw b/forc-plugins/forc-client/test/data/big_contract/src/main.sw deleted file mode 100644 index f4aa9f59b5a..00000000000 --- a/forc-plugins/forc-client/test/data/big_contract/src/main.sw +++ /dev/null @@ -1,210 +0,0 @@ -contract; - -use std::storage::storage_vec::*; -use std::hash::*; - -abi MyContract { - fn large_blob() -> bool; - - fn enum_input_output(loc: Location) -> Location; - - fn struct_input_output(person: Person) -> Person; - - fn array_of_enum_input_output(aoe: [Location; 2]) -> [Location; 2]; - - #[storage(read, write)] - fn push_storage_u16(value: u16); - - #[storage(read)] - fn get_storage_u16(index: u64) -> u16; - - #[storage(read, write)] - fn push_storage_simple(value: SimpleStruct); - - #[storage(read)] - fn get_storage_simple(index: u64) -> SimpleStruct; - - #[storage(read, write)] - fn push_storage_location(value: Location); - - #[storage(read)] - fn get_storage_location(index: u64) -> Location; - - fn assert_configurables() -> bool; -} - -enum Location { - Earth: u64, - Mars: (), - SimpleJupiter: Color, - Jupiter: [Color; 2], - SimplePluto: SimpleStruct, - Pluto: [SimpleStruct; 2], -} - -enum Color { - Red: (), - Blue: u64, -} - -struct Person { - name: str, - age: u64, - alive: bool, - location: Location, - some_tuple: (bool, u64), - some_array: [u64; 2], - some_b256: b256, -} - -struct SimpleStruct { - a: bool, - b: u64, -} - -storage { - my_vec: StorageVec = StorageVec {}, - my_simple_vec: StorageVec = StorageVec {}, - my_location_vec: StorageVec = StorageVec {}, -} - -configurable { - BOOL: bool = true, - U8: u8 = 1, - U16: u16 = 2, - U32: u32 = 3, - U64: u32 = 4, - U256: u256 = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu256, - B256: b256 = 0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, - CONFIGURABLE_STRUCT: SimpleStruct = SimpleStruct { a: true, b: 5 }, - CONFIGURABLE_ENUM: Location = Location::Earth(1), - ARRAY_BOOL: [bool; 3] = [true, false, true], - ARRAY_U64: [u64; 3] = [9, 8, 7], - ARRAY_LOCATION: [Location; 2] = [Location::Earth(10), Location::Mars], - ARRAY_SIMPLE_STRUCT: [SimpleStruct; 3] = [ SimpleStruct { a: true, b: 5}, SimpleStruct { a: false, b: 0 }, SimpleStruct { a: true, b: u64::max() }], - TUPLE_BOOL_U64: (bool, u64) = (true, 11), - STR_4: str[4] = __to_str_array("abcd"), -} - -impl PartialEq for Color { - fn eq(self, other: Color) -> bool { - match (self, other) { - (Color::Red, Color::Red) => true, - (Color::Blue(inner1), Color::Blue(inner2)) => inner1 == inner2, - _ => false, - } - } -} - -impl PartialEq for SimpleStruct { - fn eq(self, other: SimpleStruct) -> bool { - self.a == other.a && self.b == other.b - } -} - -impl PartialEq for Location { - fn eq(self, other: Location) -> bool { - match (self, other) { - (Location::Earth(inner1), Location::Earth(inner2)) => inner1 == inner2, - (Location::Mars, Location::Mars) => true, - (Location::SimpleJupiter(inner1), Location::SimpleJupiter(inner2)) => inner1 == inner2, - (Location::Jupiter(inner1), Location::Jupiter(inner2)) => (inner1[0] == inner2[0] && inner1[1] == inner2[1]), - (Location::SimplePluto(inner1), Location::SimplePluto(inner2)) => inner1 == inner2, - (Location::Pluto(inner1), Location::Pluto(inner2)) => (inner1[0] == inner2[0] && inner1[1] == inner2[1]), - _ => false, - } - } -} - -impl MyContract for Contract { - fn large_blob() -> bool { - asm() { - blob i91000; - } - true - } - - fn enum_input_output(loc: Location) -> Location { - loc - } - - fn struct_input_output(person: Person) -> Person { - person - } - - fn array_of_enum_input_output(aoe: [Location; 2]) -> [Location; 2] { - aoe - } - - #[storage(read, write)] - fn push_storage_u16(value: u16) { - storage.my_vec.push(value); - } - - #[storage(read)] - fn get_storage_u16(index: u64) -> u16 { - storage.my_vec.get(index).unwrap().read() - } - - #[storage(read, write)] - fn push_storage_simple(value: SimpleStruct) { - storage.my_simple_vec.push(value); - } - - #[storage(read)] - fn get_storage_simple(index: u64) -> SimpleStruct { - storage.my_simple_vec.get(index).unwrap().read() - } - - #[storage(read, write)] - fn push_storage_location(value: Location) { - storage.my_location_vec.push(value); - } - - #[storage(read)] - fn get_storage_location(index: u64) -> Location { - storage.my_location_vec.get(index).unwrap().read() - } - - fn assert_configurables() -> bool { - assert(BOOL == true); - assert(U8 == 1); - assert(U16 == 2); - assert(U32 == 3); - assert(U64 == 4); - assert(U256 == 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu256); - assert(B256 == 0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB); - assert(CONFIGURABLE_STRUCT.a == true); - assert(CONFIGURABLE_STRUCT.b == 5); - assert(CONFIGURABLE_ENUM == Location::Earth(1)); - assert(ARRAY_BOOL[0] == true); - assert(ARRAY_BOOL[1] == false); - assert(ARRAY_BOOL[2] == true); - assert(ARRAY_U64[0] == 9); - assert(ARRAY_U64[1] == 8); - assert(ARRAY_U64[2] == 7); - assert(ARRAY_LOCATION[0] == Location::Earth(10)); - assert(ARRAY_LOCATION[1] == Location::Mars); - assert(ARRAY_SIMPLE_STRUCT[0].a == true); - assert(ARRAY_SIMPLE_STRUCT[0].b == 5); - assert(ARRAY_SIMPLE_STRUCT[1].a == false); - assert(ARRAY_SIMPLE_STRUCT[1].b == 0); - assert(ARRAY_SIMPLE_STRUCT[2].a == true); - assert(ARRAY_SIMPLE_STRUCT[2].b == u64::max()); - assert(ARRAY_LOCATION[1] == Location::Mars); - assert(ARRAY_LOCATION[1] == Location::Mars); - assert(TUPLE_BOOL_U64.0 == true); - assert(TUPLE_BOOL_U64.1 == 11); - assert(sha256_str_array(STR_4) == sha256("abcd")); - - // Assert address do not change - let addr_1 = asm(addr: &BOOL) { - addr: u64 - }; - let addr_2 = asm(addr: &BOOL) { - addr: u64 - }; - assert(addr_1 == addr_2); - true - } -} diff --git a/forc-plugins/forc-client/test/data/contract_with_dep/.gitignore b/forc-plugins/forc-client/test/data/contract_with_dep/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/contract_with_dep/Forc.toml b/forc-plugins/forc-client/test/data/contract_with_dep/Forc.toml deleted file mode 100644 index 4d1d6e8308b..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep/Forc.toml +++ /dev/null @@ -1,12 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -implicit-std = false -license = "Apache-2.0" -name = "contract_with_dep" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } - -[contract-dependencies] -standalone_contract = { path = "../standalone_contract", salt = "0x0000000000000000000000000000000000000000000000000000000000000001" } diff --git a/forc-plugins/forc-client/test/data/contract_with_dep/src/main.sw b/forc-plugins/forc-client/test/data/contract_with_dep/src/main.sw deleted file mode 100644 index 7d4a75493c6..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep/src/main.sw +++ /dev/null @@ -1,11 +0,0 @@ -contract; - -abi MyContract { - fn test_function() -> bool; -} - -impl MyContract for Contract { - fn test_function() -> bool { - true - } -} diff --git a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/.gitignore b/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/Forc.toml b/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/Forc.toml deleted file mode 100644 index fe10c17663d..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/Forc.toml +++ /dev/null @@ -1,13 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -implicit-std = false -license = "Apache-2.0" -name = "contract_with_dep_with_salt_conflict" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } - -[contract-dependencies] -contract_with_dep = { path = "../contract_with_dep" } -standalone_contract = { path = "../standalone_contract", salt = "0x0000000000000000000000000000000000000000000000000000000000000001" } diff --git a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/src/main.sw b/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/src/main.sw deleted file mode 100644 index 7d4a75493c6..00000000000 --- a/forc-plugins/forc-client/test/data/contract_with_dep_with_salt_conflict/src/main.sw +++ /dev/null @@ -1,11 +0,0 @@ -contract; - -abi MyContract { - fn test_function() -> bool; -} - -impl MyContract for Contract { - fn test_function() -> bool { - true - } -} diff --git a/forc-plugins/forc-client/test/data/deployed_predicate/.gitignore b/forc-plugins/forc-client/test/data/deployed_predicate/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_predicate/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/deployed_predicate/Forc.lock b/forc-plugins/forc-client/test/data/deployed_predicate/Forc.lock deleted file mode 100644 index 2be3d70a3ca..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_predicate/Forc.lock +++ /dev/null @@ -1,13 +0,0 @@ -[[package]] -name = "core" -source = "path+from-root-661979F06117CBCE" - -[[package]] -name = "deployed_predicate" -source = "member" -dependencies = ["std"] - -[[package]] -name = "std" -source = "path+from-root-661979F06117CBCE" -dependencies = ["core"] diff --git a/forc-plugins/forc-client/test/data/deployed_predicate/Forc.toml b/forc-plugins/forc-client/test/data/deployed_predicate/Forc.toml deleted file mode 100644 index 68b7cb9dff1..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_predicate/Forc.toml +++ /dev/null @@ -1,8 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "deployed_predicate" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } diff --git a/forc-plugins/forc-client/test/data/deployed_predicate/deployed_predicate-abi.json b/forc-plugins/forc-client/test/data/deployed_predicate/deployed_predicate-abi.json deleted file mode 100644 index 6c44d8cac2f..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_predicate/deployed_predicate-abi.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "programType": "predicate", - "specVersion": "1.1", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "bool", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "type": "enum EnumWithGeneric", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "metadataTypeId": 1, - "typeArguments": [ - "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - ] - }, - { - "type": "struct StructWithGeneric", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "metadataTypeId": 3, - "typeArguments": [ - "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - ] - }, - { - "type": "u8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "metadataTypes": [ - { - "type": "()", - "metadataTypeId": 0 - }, - { - "type": "enum EnumWithGeneric", - "metadataTypeId": 1, - "components": [ - { - "name": "VariantOne", - "typeId": 2 - }, - { - "name": "VariantTwo", - "typeId": 0 - } - ], - "typeParameters": [ - 2 - ] - }, - { - "type": "generic D", - "metadataTypeId": 2 - }, - { - "type": "struct StructWithGeneric", - "metadataTypeId": 3, - "components": [ - { - "name": "field_1", - "typeId": 2 - }, - { - "name": "field_2", - "typeId": 4 - } - ], - "typeParameters": [ - 2 - ] - }, - { - "type": "u64", - "metadataTypeId": 4 - } - ], - "functions": [ - { - "name": "main", - "inputs": [ - { - "name": "switch", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "u_8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - }, - { - "name": "some_struct", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075" - }, - { - "name": "some_enum", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272" - } - ], - "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "attributes": null - } - ], - "loggedTypes": [], - "messagesTypes": [], - "configurables": [ - { - "name": "BOOL", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 1896, - "indirect": false - }, - { - "name": "U8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "offset": 1936, - "indirect": false - }, - { - "name": "STRUCT", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "offset": 1920, - "indirect": false - }, - { - "name": "ENUM", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "offset": 1904, - "indirect": false - } - ], - "errorCodes": {} -} \ No newline at end of file diff --git a/forc-plugins/forc-client/test/data/deployed_predicate/src/main.sw b/forc-plugins/forc-client/test/data/deployed_predicate/src/main.sw deleted file mode 100644 index bf4f281a927..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_predicate/src/main.sw +++ /dev/null @@ -1,53 +0,0 @@ -predicate; - -#[allow(dead_code)] -enum EnumWithGeneric { - VariantOne: D, - VariantTwo: (), -} - -struct StructWithGeneric { - field_1: D, - field_2: u64, -} - -impl PartialEq for EnumWithGeneric -where - D: PartialEq, -{ - fn eq(self, other: Self) -> bool { - match (self, other) { - (EnumWithGeneric::VariantOne(d1), EnumWithGeneric::VariantOne(d2)) => d1 == d2, - (EnumWithGeneric::VariantTwo, EnumWithGeneric::VariantTwo) => true, - _ => false, - } - } -} - -impl PartialEq for StructWithGeneric -where - D: PartialEq, -{ - fn eq(self, other: Self) -> bool { - self.field_1 == other.field_1 && self.field_2 == other.field_2 - } -} - -configurable { - BOOL: bool = true, - U8: u8 = 8, - STRUCT: StructWithGeneric = StructWithGeneric { - field_1: 8, - field_2: 16, - }, - ENUM: EnumWithGeneric = EnumWithGeneric::VariantOne(true), -} - -fn main( - switch: bool, - u_8: u8, - some_struct: StructWithGeneric, - some_enum: EnumWithGeneric, -) -> bool { - switch == BOOL && u_8 == U8 && some_struct == STRUCT && some_enum == ENUM -} diff --git a/forc-plugins/forc-client/test/data/deployed_script/.gitignore b/forc-plugins/forc-client/test/data/deployed_script/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_script/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/deployed_script/Forc.toml b/forc-plugins/forc-client/test/data/deployed_script/Forc.toml deleted file mode 100644 index 3eb3cdf653b..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_script/Forc.toml +++ /dev/null @@ -1,8 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "deployed_script" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } diff --git a/forc-plugins/forc-client/test/data/deployed_script/deployed_script-abi.json b/forc-plugins/forc-client/test/data/deployed_script/deployed_script-abi.json deleted file mode 100644 index 91830caa2c7..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_script/deployed_script-abi.json +++ /dev/null @@ -1,325 +0,0 @@ -{ - "programType": "script", - "specVersion": "1.1", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "((bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], struct StructWithGeneric, enum EnumWithGeneric), bool, u64, u8)", - "concreteTypeId": "25fbba860b8a1983ebcfa3f135136266a7edb7ca3a7e1f8ec988135c12a9f873", - "metadataTypeId": 2 - }, - { - "type": "(u8, bool)", - "concreteTypeId": "e0128f7be9902d1fe16326cafe703b52038064a7997b03ebfc1c9dd607e1536c", - "metadataTypeId": 1 - }, - { - "type": "[u32; 3]", - "concreteTypeId": "d9fac01ab38fe10950758ae9604da330d6406a71fda3ef1ea818121261132d56", - "metadataTypeId": 4 - }, - { - "type": "b256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - }, - { - "type": "bool", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "type": "enum EnumWithGeneric", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "metadataTypeId": 5, - "typeArguments": [ - "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - ] - }, - { - "type": "str[4]", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" - }, - { - "type": "struct StructWithGeneric", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "metadataTypeId": 7, - "typeArguments": [ - "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - ] - }, - { - "type": "u16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - }, - { - "type": "u256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" - }, - { - "type": "u32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "type": "u64", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "type": "u8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "metadataTypes": [ - { - "type": "()", - "metadataTypeId": 0 - }, - { - "type": "(_, _)", - "metadataTypeId": 1, - "components": [ - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - }, - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - } - ] - }, - { - "type": "(_, _, _, _)", - "metadataTypeId": 2, - "components": [ - { - "name": "__tuple_element", - "typeId": 3 - }, - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "__tuple_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ] - }, - { - "type": "(_, _, _, _, _, _, _, _, _, _, _, _)", - "metadataTypeId": 3, - "components": [ - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - }, - { - "name": "__tuple_element", - "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - }, - { - "name": "__tuple_element", - "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "name": "__tuple_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "__tuple_element", - "typeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" - }, - { - "name": "__tuple_element", - "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - }, - { - "name": "__tuple_element", - "typeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" - }, - { - "name": "__tuple_element", - "typeId": 1 - }, - { - "name": "__tuple_element", - "typeId": 4 - }, - { - "name": "__tuple_element", - "typeId": 7, - "typeArguments": [ - { - "name": "", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ] - }, - { - "name": "__tuple_element", - "typeId": 5, - "typeArguments": [ - { - "name": "", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - } - ] - } - ] - }, - { - "type": "[_; 3]", - "metadataTypeId": 4, - "components": [ - { - "name": "__array_element", - "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - } - ] - }, - { - "type": "enum EnumWithGeneric", - "metadataTypeId": 5, - "components": [ - { - "name": "VariantOne", - "typeId": 6 - }, - { - "name": "VariantTwo", - "typeId": 0 - } - ], - "typeParameters": [ - 6 - ] - }, - { - "type": "generic D", - "metadataTypeId": 6 - }, - { - "type": "struct StructWithGeneric", - "metadataTypeId": 7, - "components": [ - { - "name": "field_1", - "typeId": 6 - }, - { - "name": "field_2", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ], - "typeParameters": [ - 6 - ] - } - ], - "functions": [ - { - "name": "main", - "inputs": [ - { - "name": "a", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "name": "contract_addr", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - } - ], - "output": "25fbba860b8a1983ebcfa3f135136266a7edb7ca3a7e1f8ec988135c12a9f873", - "attributes": null - } - ], - "loggedTypes": [ - { - "logId": "14454674236531057292", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "messagesTypes": [], - "configurables": [ - { - "name": "BOOL", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 7792, - "indirect": false - }, - { - "name": "U8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "offset": 7904, - "indirect": false - }, - { - "name": "U16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "offset": 7848, - "indirect": false - }, - { - "name": "U32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", - "offset": 7888, - "indirect": false - }, - { - "name": "U64", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", - "offset": 7896, - "indirect": false - }, - { - "name": "U256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", - "offset": 7856, - "indirect": false - }, - { - "name": "B256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", - "offset": 7760, - "indirect": false - }, - { - "name": "STR_4", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", - "offset": 7832, - "indirect": false - }, - { - "name": "TUPLE", - "concreteTypeId": "e0128f7be9902d1fe16326cafe703b52038064a7997b03ebfc1c9dd607e1536c", - "offset": 7840, - "indirect": false - }, - { - "name": "ARRAY", - "concreteTypeId": "d9fac01ab38fe10950758ae9604da330d6406a71fda3ef1ea818121261132d56", - "offset": 7744, - "indirect": false - }, - { - "name": "STRUCT", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "offset": 7816, - "indirect": false - }, - { - "name": "ENUM", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "offset": 7800, - "indirect": false - } - ], - "errorCodes": {} -} \ No newline at end of file diff --git a/forc-plugins/forc-client/test/data/deployed_script/deployed_script-loader-abi.json b/forc-plugins/forc-client/test/data/deployed_script/deployed_script-loader-abi.json deleted file mode 100644 index a48329fd829..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_script/deployed_script-loader-abi.json +++ /dev/null @@ -1,326 +0,0 @@ -{ - "programType": "script", - "specVersion": "1.2", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "((bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], struct StructWithGeneric, enum EnumWithGeneric), bool, u64, u8)", - "concreteTypeId": "25fbba860b8a1983ebcfa3f135136266a7edb7ca3a7e1f8ec988135c12a9f873", - "metadataTypeId": 2 - }, - { - "type": "(u8, bool)", - "concreteTypeId": "e0128f7be9902d1fe16326cafe703b52038064a7997b03ebfc1c9dd607e1536c", - "metadataTypeId": 1 - }, - { - "type": "[u32; 3]", - "concreteTypeId": "d9fac01ab38fe10950758ae9604da330d6406a71fda3ef1ea818121261132d56", - "metadataTypeId": 4 - }, - { - "type": "b256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - }, - { - "type": "bool", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "type": "enum EnumWithGeneric", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "metadataTypeId": 5, - "typeArguments": [ - "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - ] - }, - { - "type": "str[4]", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" - }, - { - "type": "struct StructWithGeneric", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "metadataTypeId": 7, - "typeArguments": [ - "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - ] - }, - { - "type": "u16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - }, - { - "type": "u256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" - }, - { - "type": "u32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "type": "u64", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "type": "u8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "metadataTypes": [ - { - "type": "()", - "metadataTypeId": 0 - }, - { - "type": "(_, _)", - "metadataTypeId": 1, - "components": [ - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - }, - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - } - ] - }, - { - "type": "(_, _, _, _)", - "metadataTypeId": 2, - "components": [ - { - "name": "__tuple_element", - "typeId": 3 - }, - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "__tuple_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ] - }, - { - "type": "(_, _, _, _, _, _, _, _, _, _, _, _)", - "metadataTypeId": 3, - "components": [ - { - "name": "__tuple_element", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "name": "__tuple_element", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - }, - { - "name": "__tuple_element", - "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" - }, - { - "name": "__tuple_element", - "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "name": "__tuple_element", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - }, - { - "name": "__tuple_element", - "typeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" - }, - { - "name": "__tuple_element", - "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - }, - { - "name": "__tuple_element", - "typeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" - }, - { - "name": "__tuple_element", - "typeId": 1 - }, - { - "name": "__tuple_element", - "typeId": 4 - }, - { - "name": "__tuple_element", - "typeId": 7, - "typeArguments": [ - { - "name": "", - "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ] - }, - { - "name": "__tuple_element", - "typeId": 5, - "typeArguments": [ - { - "name": "", - "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - } - ] - } - ] - }, - { - "type": "[_; 3]", - "metadataTypeId": 4, - "components": [ - { - "name": "__array_element", - "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - } - ] - }, - { - "type": "enum EnumWithGeneric", - "metadataTypeId": 5, - "components": [ - { - "name": "VariantOne", - "typeId": 6 - }, - { - "name": "VariantTwo", - "typeId": 0 - } - ], - "typeParameters": [ - 6 - ] - }, - { - "type": "generic D", - "metadataTypeId": 6 - }, - { - "type": "struct StructWithGeneric", - "metadataTypeId": 7, - "components": [ - { - "name": "field_1", - "typeId": 6 - }, - { - "name": "field_2", - "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" - } - ], - "typeParameters": [ - 6 - ] - } - ], - "functions": [ - { - "name": "main", - "inputs": [ - { - "name": "a", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" - }, - { - "name": "contract_addr", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" - } - ], - "output": "25fbba860b8a1983ebcfa3f135136266a7edb7ca3a7e1f8ec988135c12a9f873", - "attributes": null - } - ], - "loggedTypes": [ - { - "logId": "14454674236531057292", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "messagesTypes": [], - "configurables": [ - { - "name": "BOOL", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 136, - "indirect": false - }, - { - "name": "U8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "offset": 248, - "indirect": false - }, - { - "name": "U16", - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "offset": 192, - "indirect": false - }, - { - "name": "U32", - "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", - "offset": 232, - "indirect": false - }, - { - "name": "U64", - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", - "offset": 240, - "indirect": false - }, - { - "name": "U256", - "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", - "offset": 200, - "indirect": false - }, - { - "name": "B256", - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", - "offset": 104, - "indirect": false - }, - { - "name": "STR_4", - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", - "offset": 176, - "indirect": false - }, - { - "name": "TUPLE", - "concreteTypeId": "e0128f7be9902d1fe16326cafe703b52038064a7997b03ebfc1c9dd607e1536c", - "offset": 184, - "indirect": false - }, - { - "name": "ARRAY", - "concreteTypeId": "d9fac01ab38fe10950758ae9604da330d6406a71fda3ef1ea818121261132d56", - "offset": 88, - "indirect": false - }, - { - "name": "STRUCT", - "concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075", - "offset": 160, - "indirect": false - }, - { - "name": "ENUM", - "concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272", - "offset": 144, - "indirect": false - } - ], - "errorCodes": {}, - "panickingCalls": {} -} \ No newline at end of file diff --git a/forc-plugins/forc-client/test/data/deployed_script/deployed_script.bin b/forc-plugins/forc-client/test/data/deployed_script/deployed_script.bin deleted file mode 100644 index ceeff442db5..00000000000 Binary files a/forc-plugins/forc-client/test/data/deployed_script/deployed_script.bin and /dev/null differ diff --git a/forc-plugins/forc-client/test/data/deployed_script/src/main.sw b/forc-plugins/forc-client/test/data/deployed_script/src/main.sw deleted file mode 100644 index c47105d4d74..00000000000 --- a/forc-plugins/forc-client/test/data/deployed_script/src/main.sw +++ /dev/null @@ -1,72 +0,0 @@ -script; - -use std::logging::log; - -#[allow(dead_code)] -enum EnumWithGeneric { - VariantOne: D, - VariantTwo: (), -} - -struct StructWithGeneric { - field_1: D, - field_2: u64, -} - -abi MyContract { - fn test_function() -> bool; - - #[storage(read)] - fn test_function_read() -> u8; - - #[storage(read, write)] - fn test_function_write(value: u8) -> u8; -} - -configurable { - BOOL: bool = true, - U8: u8 = 8, - U16: u16 = 16, - U32: u32 = 32, - U64: u64 = 63, - U256: u256 = 0x0000000000000000000000000000000000000000000000000000000000000008u256, - B256: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101, - STR_4: str[4] = __to_str_array("fuel"), - TUPLE: (u8, bool) = (8, true), - ARRAY: [u32; 3] = [253, 254, 255], - STRUCT: StructWithGeneric = StructWithGeneric { - field_1: 8, - field_2: 16, - }, - ENUM: EnumWithGeneric = EnumWithGeneric::VariantOne(true), -} - -fn get_configurables() -> (bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], StructWithGeneric, EnumWithGeneric) { - (BOOL, U8, U16, U32, U64, U256, B256, STR_4, TUPLE, ARRAY, STRUCT, ENUM) -} - -fn basic_function_with_input(a: u32) -> bool { - if a % 2 == 0 { - true - }else { - false - } -} - -fn basic_function_without_input() -> u64 { - let a = 100; - let b = 25; - a*b -} - -fn main(a: u32, contract_addr: b256) -> ((bool, u8, u16, u32, u64, u256, b256, str[4], (u8, bool), [u32; 3], StructWithGeneric, EnumWithGeneric), bool, u64, u8) { - log(U8); - let configs = get_configurables(); - let with_in = basic_function_with_input(a); - let without_in = basic_function_without_input(); - - let contract_instance = abi(MyContract, contract_addr); - let from_contract = contract_instance.test_function_read(); - - return (configs, with_in, without_in, from_contract); -} diff --git a/forc-plugins/forc-client/test/data/standalone_contract/.gitignore b/forc-plugins/forc-client/test/data/standalone_contract/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock b/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock deleted file mode 100644 index 477b3a5fde7..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock +++ /dev/null @@ -1,8 +0,0 @@ -[[package]] -name = "standalone_contract" -source = "member" -dependencies = ["std"] - -[[package]] -name = "std" -source = "path+from-root-79BB3EA8498403DE" diff --git a/forc-plugins/forc-client/test/data/standalone_contract/Forc.toml b/forc-plugins/forc-client/test/data/standalone_contract/Forc.toml deleted file mode 100644 index 2324ff98894..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/Forc.toml +++ /dev/null @@ -1,9 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -implicit-std = false -license = "Apache-2.0" -name = "standalone_contract" - -[dependencies] -std = { path = "../../../../../sway-lib-std/" } diff --git a/forc-plugins/forc-client/test/data/standalone_contract/src/main.sw b/forc-plugins/forc-client/test/data/standalone_contract/src/main.sw deleted file mode 100644 index 49e0ac20ae2..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/src/main.sw +++ /dev/null @@ -1,32 +0,0 @@ -contract; - -storage { - value: u8 = 5, -} - -abi MyContract { - fn test_function() -> bool; - - #[storage(read)] - fn test_function_read() -> u8; - - #[storage(read, write)] - fn test_function_write(value: u8) -> u8; -} - -impl MyContract for Contract { - fn test_function() -> bool { - true - } - - #[storage(read)] - fn test_function_read() -> u8 { - storage.value.read() - } - - #[storage(read, write)] - fn test_function_write(value: u8) -> u8 { - storage.value.write(value); - storage.value.read() - } -} diff --git a/forc-plugins/forc-client/test/data/standalone_contract/standalone_contract-abi.json b/forc-plugins/forc-client/test/data/standalone_contract/standalone_contract-abi.json deleted file mode 100644 index 21ce0de4de5..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/standalone_contract-abi.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "programType": "contract", - "specVersion": "1.1", - "encodingVersion": "1", - "concreteTypes": [ - { - "type": "bool", - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" - }, - { - "type": "u8", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "metadataTypes": [], - "functions": [ - { - "inputs": [], - "name": "test_function", - "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "attributes": null - }, - { - "inputs": [], - "name": "test_function_read", - "output": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read" - ] - } - ] - }, - { - "inputs": [ - { - "name": "value", - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" - } - ], - "name": "test_function_write", - "output": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "attributes": [ - { - "name": "storage", - "arguments": [ - "read", - "write" - ] - } - ] - } - ], - "loggedTypes": [], - "messagesTypes": [], - "configurables": [] -} \ No newline at end of file diff --git a/forc-plugins/forc-client/tests/deploy.rs b/forc-plugins/forc-client/tests/deploy.rs deleted file mode 100644 index 2c36eca9173..00000000000 --- a/forc-plugins/forc-client/tests/deploy.rs +++ /dev/null @@ -1,1352 +0,0 @@ -use forc::cli::shared::Pkg; -use forc_client::{ - cmd, - op::{deploy, DeployedContract, DeployedExecutable, DeployedPackage}, - util::{account::ForcClientAccount, tx::update_proxy_contract_target}, - NodeTarget, -}; -use forc_pkg::manifest::Proxy; -use fuel_crypto::SecretKey; -use fuel_tx::{ContractId, Salt}; -use fuels::{ - macros::abigen, - types::{transaction::TxPolicies, AsciiString, Bits256, SizedAsciiString}, -}; -use fuels_accounts::{ - provider::Provider, signers::private_key::PrivateKeySigner, wallet::Wallet, Account, - ViewOnlyAccount, -}; -use portpicker::Port; -use rand::thread_rng; -use rexpect::spawn; -use std::{ - fs, - path::{Path, PathBuf}, - process::{Child, Command}, - str::FromStr, -}; -use tempfile::tempdir; -use toml_edit::{value, DocumentMut, InlineTable, Item, Table, Value}; - -fn get_workspace_root() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../") - .join("../") - .canonicalize() - .unwrap() -} - -fn test_data_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("test") - .join("data") - .canonicalize() - .unwrap() -} - -fn run_node() -> (Child, Port) { - let port = portpicker::pick_unused_port().expect("No ports free"); - - let child = Command::new("fuel-core") - .arg("run") - .arg("--debug") - .arg("--db-type") - .arg("in-memory") - .arg("--port") - .arg(port.to_string()) - .spawn() - .expect("Failed to start fuel-core"); - (child, port) -} - -/// Copy a directory recursively from `source` to `dest`. -fn copy_dir(source: &Path, dest: &Path) -> anyhow::Result<()> { - fs::create_dir_all(dest)?; - for e in fs::read_dir(source)? { - let entry = e?; - let file_type = entry.file_type()?; - if file_type.is_dir() { - copy_dir(&entry.path(), &dest.join(entry.file_name()))?; - } else { - fs::copy(entry.path(), dest.join(entry.file_name()))?; - } - } - Ok(()) -} - -/// Tries to get an `DeployedContract` out of the given `DeployedPackage`. -/// Panics otherwise. -fn expect_deployed_contract(deployed_package: DeployedPackage) -> DeployedContract { - if let DeployedPackage::Contract(contract) = deployed_package { - contract - } else { - println!("{deployed_package:?}"); - panic!("expected deployed package to be a contract") - } -} - -/// Tries to get a script (`DeployedExecutable`) out of given deployed package. -/// Panics otherwise. -fn expect_deployed_script(deployed_package: DeployedPackage) -> DeployedExecutable { - if let DeployedPackage::Script(script) = deployed_package { - script - } else { - panic!("expected deployed package to be a script") - } -} - -/// Tries to get a predicate (`DeployedExecutable`) out of given deployed package. -/// Panics otherwise. -fn expect_deployed_predicate(deployed_package: DeployedPackage) -> DeployedExecutable { - if let DeployedPackage::Predicate(predicate) = deployed_package { - predicate - } else { - panic!("expected deployed package to be a predicate") - } -} - -fn patch_manifest_file_with_path_std(manifest_dir: &Path) -> anyhow::Result<()> { - let toml_path = manifest_dir.join(sway_utils::constants::MANIFEST_FILE_NAME); - let toml_content = fs::read_to_string(&toml_path).unwrap(); - - let mut doc = toml_content.parse::().unwrap(); - let new_std_path = get_workspace_root().join("sway-lib-std"); - - let mut std_dependency = InlineTable::new(); - std_dependency.insert("path", Value::from(new_std_path.display().to_string())); - doc["dependencies"]["std"] = Item::Value(Value::InlineTable(std_dependency)); - - fs::write(&toml_path, doc.to_string()).unwrap(); - Ok(()) -} - -fn patch_manifest_file_with_proxy_table(manifest_dir: &Path, proxy: Proxy) -> anyhow::Result<()> { - let toml_path = manifest_dir.join(sway_utils::constants::MANIFEST_FILE_NAME); - let toml_content = fs::read_to_string(&toml_path)?; - let mut doc = toml_content.parse::()?; - - let proxy_table = doc.entry("proxy").or_insert(Item::Table(Table::new())); - let proxy_table = proxy_table.as_table_mut().unwrap(); - - proxy_table.insert("enabled", value(proxy.enabled)); - - if let Some(address) = proxy.address { - proxy_table.insert("address", value(address)); - } else { - proxy_table.remove("address"); - } - - fs::write(&toml_path, doc.to_string())?; - Ok(()) -} - -fn update_main_sw(tmp_dir: &Path) -> anyhow::Result<()> { - let main_sw_path = tmp_dir.join("src").join("main.sw"); - let content = fs::read_to_string(&main_sw_path)?; - let updated_content = content.replace("true", "false"); - fs::write(main_sw_path, updated_content)?; - Ok(()) -} - -async fn assert_big_contract_calls(wallet: Wallet, contract_id: ContractId) { - abigen!(Contract( - name = "BigContract", - abi = "forc-plugins/forc-client/test/data/big_contract/big_contract-abi.json" - )); - - let instance = BigContract::new(contract_id, wallet); - - let result = instance.methods().large_blob().call().await.unwrap().value; - assert!(result); - - let result = instance - .methods() - .enum_input_output(Location::Mars) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::Mars); - - // Test enum with "tuple like struct" with simple value. - let result = instance - .methods() - .enum_input_output(Location::Earth(u64::MAX)) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::Earth(u64::MAX)); - - // Test enum with "tuple like struct" with enum value. - let result = instance - .methods() - .enum_input_output(Location::SimpleJupiter(Color::Red)) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::SimpleJupiter(Color::Red)); - - // Test enum with "tuple like struct" with enum value. - let result = instance - .methods() - .enum_input_output(Location::SimpleJupiter(Color::Blue(u64::MAX))) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::SimpleJupiter(Color::Blue(u64::MAX))); - - // Test enum with "tuple like struct" with enum array value. - let result = instance - .methods() - .enum_input_output(Location::Jupiter([Color::Red, Color::Blue(u64::MAX)])) - .call() - .await - .unwrap() - .value; - assert_eq!( - result, - Location::Jupiter([Color::Red, Color::Blue(u64::MAX)]) - ); - - // Test enum with "tuple like struct" with struct array value. - let result = instance - .methods() - .enum_input_output(Location::SimplePluto(SimpleStruct { - a: true, - b: u64::MAX, - })) - .call() - .await - .unwrap() - .value; - assert_eq!( - result, - Location::SimplePluto(SimpleStruct { - a: true, - b: u64::MAX, - }) - ); - - let input = Person { - name: AsciiString::new("Alice".into()).unwrap(), - age: 42, - alive: true, - location: Location::Earth(1), - some_tuple: (false, 42), - some_array: [4, 2], - some_b_256: Bits256::zeroed(), - }; - let result = instance - .methods() - .struct_input_output(input.clone()) - .call() - .await - .unwrap() - .value; - assert_eq!(result, input); - - let _ = instance - .methods() - .push_storage_u16(42) - .call() - .await - .unwrap(); - let result = instance - .methods() - .get_storage_u16(0) - .call() - .await - .unwrap() - .value; - assert_eq!(result, 42); - - let _ = instance - .methods() - .push_storage_simple(SimpleStruct { - a: true, - b: u64::MAX, - }) - .call() - .await - .unwrap(); - let result = instance - .methods() - .get_storage_simple(0) - .call() - .await - .unwrap() - .value; - assert_eq!( - result, - SimpleStruct { - a: true, - b: u64::MAX, - } - ); - - let _ = instance - .methods() - .push_storage_location(Location::Mars) - .call() - .await - .unwrap(); - let result = instance - .methods() - .get_storage_location(0) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::Mars); - - let _ = instance - .methods() - .push_storage_location(Location::Earth(u64::MAX)) - .call() - .await - .unwrap(); - let result = instance - .methods() - .get_storage_location(1) - .call() - .await - .unwrap() - .value; - assert_eq!(result, Location::Earth(u64::MAX)); - - let _ = instance - .methods() - .push_storage_location(Location::Jupiter([Color::Red, Color::Blue(u64::MAX)])) - .call() - .await - .unwrap(); - let result = instance - .methods() - .get_storage_location(2) - .call() - .await - .unwrap() - .value; - assert_eq!( - result, - Location::Jupiter([Color::Red, Color::Blue(u64::MAX)]) - ); - - let result = instance - .methods() - .assert_configurables() - .call() - .await - .unwrap() - .value; - assert!(result); -} - -#[tokio::test] -async fn test_simple_deploy() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let contract_ids = deploy(cmd).await.unwrap(); - node.kill().unwrap(); - let expected = vec![DeployedPackage::Contract(DeployedContract { - id: ContractId::from_str( - "8eb886d62bea9ba2f9de9748d286d9d72e9055dea6c2d1fe37175d438e9568c0", - ) - .unwrap(), - proxy: None, - chunked: false, - })]; - - assert_eq!(contract_ids, expected) -} - -#[tokio::test] -async fn test_deploy_submit_only() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - - let target = NodeTarget { - node_url: Some(node_url), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - submit_only: true, - ..Default::default() - }; - let contract_ids = deploy(cmd).await.unwrap(); - node.kill().unwrap(); - let expected = vec![DeployedPackage::Contract(DeployedContract { - id: ContractId::from_str( - "8eb886d62bea9ba2f9de9748d286d9d72e9055dea6c2d1fe37175d438e9568c0", - ) - .unwrap(), - proxy: None, - chunked: false, - })]; - - assert_eq!(contract_ids, expected) -} - -#[tokio::test] -async fn test_deploy_fresh_proxy() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - let proxy = Proxy { - enabled: true, - address: None, - }; - patch_manifest_file_with_proxy_table(tmp_dir.path(), proxy).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let contract_ids = deploy(cmd).await.unwrap(); - node.kill().unwrap(); - let impl_contract = DeployedPackage::Contract(DeployedContract { - id: ContractId::from_str( - "8eb886d62bea9ba2f9de9748d286d9d72e9055dea6c2d1fe37175d438e9568c0", - ) - .unwrap(), - proxy: Some( - ContractId::from_str( - "13fc3802f96a2142891acb51b6b111bbf0e640832b64e999b6a186532592a55c", - ) - .unwrap(), - ), - chunked: false, - }); - let expected = vec![impl_contract]; - - assert_eq!(contract_ids, expected) -} - -#[tokio::test] -async fn test_proxy_contract_re_routes_call() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - let proxy = Proxy { - enabled: true, - address: None, - }; - patch_manifest_file_with_proxy_table(tmp_dir.path(), proxy).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_contract = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - // At this point we deployed a contract with proxy. - let proxy_contract_id = deployed_contract.proxy.unwrap(); - let impl_contract_id = deployed_contract.id; - // Make a contract call into proxy contract, and check if the initial - // contract returns a true. - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - - abigen!(Contract( - name = "ImplementationContract", - abi = "forc-plugins/forc-client/test/data/standalone_contract/standalone_contract-abi.json" - )); - - let impl_contract_a = ImplementationContract::new(proxy_contract_id, wallet_unlocked.clone()); - - // Test storage functions - let res = impl_contract_a - .methods() - .test_function_read() - .with_contract_ids(&[impl_contract_id]) - .call() - .await - .unwrap(); - assert_eq!(res.value, 5); - let res = impl_contract_a - .methods() - .test_function_write(8) - .with_contract_ids(&[impl_contract_id]) - .call() - .await - .unwrap(); - assert_eq!(res.value, 8); - - let res = impl_contract_a - .methods() - .test_function() - .with_contract_ids(&[impl_contract_id]) - .call() - .await - .unwrap(); - assert!(res.value); - - update_main_sw(tmp_dir.path()).unwrap(); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_contract = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - // proxy contract id should be the same. - let proxy_contract_after_update = deployed_contract.proxy.unwrap(); - assert_eq!(proxy_contract_id, proxy_contract_after_update); - let impl_contract_id_after_update = deployed_contract.id; - assert!(impl_contract_id != impl_contract_id_after_update); - let impl_contract_a = ImplementationContract::new(proxy_contract_after_update, wallet_unlocked); - - // Test storage functions - let res = impl_contract_a - .methods() - .test_function_read() - .with_contract_ids(&[impl_contract_id_after_update]) - .call() - .await - .unwrap(); - // Storage should be preserved from the previous target contract. - assert_eq!(res.value, 8); - let res = impl_contract_a - .methods() - .test_function_write(9) - .with_contract_ids(&[impl_contract_id_after_update]) - .call() - .await - .unwrap(); - assert_eq!(res.value, 9); - - let res = impl_contract_a - .methods() - .test_function() - .with_contract_ids(&[impl_contract_id_after_update]) - .call() - .await - .unwrap(); - assert!(!res.value); - node.kill().unwrap(); -} - -#[tokio::test] -async fn test_non_owner_fails_to_set_target() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - let proxy = Proxy { - enabled: true, - address: None, - }; - patch_manifest_file_with_proxy_table(tmp_dir.path(), proxy).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let contract_id = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - // Proxy contract's id. - let proxy_id = contract_id.proxy.unwrap(); - - // Create and fund an owner account and an attacker account. - let provider = Provider::connect(&node_url).await.unwrap(); - let attacker_secret_key = SecretKey::random(&mut thread_rng()); - let attacker_signer = PrivateKeySigner::new(attacker_secret_key); - let attacker_wallet = Wallet::new(attacker_signer.clone(), provider.clone()); - - let owner_secret_key = - SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let owner_signer = PrivateKeySigner::new(owner_secret_key); - let owner_wallet = Wallet::new(owner_signer, provider.clone()); - let consensus_parameters = provider.consensus_parameters().await.unwrap(); - let base_asset_id = consensus_parameters.base_asset_id(); - - // Fund attacker wallet so that it can try to make a set proxy target call. - owner_wallet - .transfer( - attacker_wallet.address(), - 100000, - *base_asset_id, - TxPolicies::default(), - ) - .await - .unwrap(); - - let dummy_contract_id_target = ContractId::default(); - abigen!(Contract(name = "ProxyContract", abi = "{\"programType\":\"contract\",\"specVersion\":\"1.1\",\"encodingVersion\":\"1\",\"concreteTypes\":[{\"type\":\"()\",\"concreteTypeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"type\":\"enum standards::src5::AccessError\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\",\"metadataTypeId\":1},{\"type\":\"enum standards::src5::State\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"metadataTypeId\":2},{\"type\":\"enum std::option::Option\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"metadataTypeId\":4,\"typeArguments\":[\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\",\"metadataTypeId\":5},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\",\"metadataTypeId\":6},{\"type\":\"str\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"},{\"type\":\"struct std::contract_id::ContractId\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\",\"metadataTypeId\":9},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\",\"metadataTypeId\":10},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\",\"metadataTypeId\":11}],\"metadataTypes\":[{\"type\":\"b256\",\"metadataTypeId\":0},{\"type\":\"enum standards::src5::AccessError\",\"metadataTypeId\":1,\"components\":[{\"name\":\"NotOwner\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum standards::src5::State\",\"metadataTypeId\":2,\"components\":[{\"name\":\"Uninitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Initialized\",\"typeId\":3},{\"name\":\"Revoked\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum std::identity::Identity\",\"metadataTypeId\":3,\"components\":[{\"name\":\"Address\",\"typeId\":8},{\"name\":\"ContractId\",\"typeId\":9}]},{\"type\":\"enum std::option::Option\",\"metadataTypeId\":4,\"components\":[{\"name\":\"None\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"},{\"name\":\"Some\",\"typeId\":7}],\"typeParameters\":[7]},{\"type\":\"enum sway_libs::ownership::errors::InitializationError\",\"metadataTypeId\":5,\"components\":[{\"name\":\"CannotReinitialized\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"enum sway_libs::upgradability::errors::SetProxyOwnerError\",\"metadataTypeId\":6,\"components\":[{\"name\":\"CannotUninitialize\",\"typeId\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\"}]},{\"type\":\"generic T\",\"metadataTypeId\":7},{\"type\":\"struct std::address::Address\",\"metadataTypeId\":8,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct std::contract_id::ContractId\",\"metadataTypeId\":9,\"components\":[{\"name\":\"bits\",\"typeId\":0}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyOwnerSet\",\"metadataTypeId\":10,\"components\":[{\"name\":\"new_proxy_owner\",\"typeId\":2}]},{\"type\":\"struct sway_libs::upgradability::events::ProxyTargetSet\",\"metadataTypeId\":11,\"components\":[{\"name\":\"new_target\",\"typeId\":9}]}],\"functions\":[{\"inputs\":[],\"name\":\"proxy_target\",\"output\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [Option] - The new proxy contract to which all fallback calls will be passed or `None`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[{\"name\":\"new_target\",\"concreteTypeId\":\"29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54\"}],\"name\":\"set_proxy_target\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Change the target contract of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called by the `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_target`: [ContractId] - The new proxy contract to which all fallback calls will be passed.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When not called by `proxy_owner`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Write: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\",\"write\"]}]},{\"inputs\":[],\"name\":\"proxy_owner\",\"output\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Returns the owner of the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Returns\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * [State] - Represents the state of ownership for this contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"read\"]}]},{\"inputs\":[],\"name\":\"initialize_proxy\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Initializes the proxy contract.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method sets the storage values using the values of the configurable constants `INITIAL_TARGET` and `INITIAL_OWNER`.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This then allows methods that write to storage to be called.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can only be called once.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When `storage::SRC14.proxy_owner` is not [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `2`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]},{\"inputs\":[{\"name\":\"new_proxy_owner\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\"}],\"name\":\"set_proxy_owner\",\"output\":\"2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d\",\"attributes\":[{\"name\":\"doc-comment\",\"arguments\":[\" Changes proxy ownership to the passed State.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Additional Information\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" This method can be used to transfer ownership between Identities or to revoke ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Arguments\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * `new_proxy_owner`: [State] - The new state of the proxy ownership.\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Reverts\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the sender is not the current proxy owner.\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * When the new state of the proxy ownership is [State::Uninitialized].\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" # Number of Storage Accesses\"]},{\"name\":\"doc-comment\",\"arguments\":[\"\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Reads: `1`\"]},{\"name\":\"doc-comment\",\"arguments\":[\" * Writes: `1`\"]},{\"name\":\"storage\",\"arguments\":[\"write\"]}]}],\"loggedTypes\":[{\"logId\":\"4571204900286667806\",\"concreteTypeId\":\"3f702ea3351c9c1ece2b84048006c8034a24cbc2bad2e740d0412b4172951d3d\"},{\"logId\":\"2151606668983994881\",\"concreteTypeId\":\"1ddc0adda1270a016c08ffd614f29f599b4725407c8954c8b960bdf651a9a6c8\"},{\"logId\":\"2161305517876418151\",\"concreteTypeId\":\"1dfe7feadc1d9667a4351761230f948744068a090fe91b1bc6763a90ed5d3893\"},{\"logId\":\"4354576968059844266\",\"concreteTypeId\":\"3c6e90ae504df6aad8b34a93ba77dc62623e00b777eecacfa034a8ac6e890c74\"},{\"logId\":\"10870989709723147660\",\"concreteTypeId\":\"96dd838b44f99d8ccae2a7948137ab6256c48ca4abc6168abc880de07fba7247\"},{\"logId\":\"10098701174489624218\",\"concreteTypeId\":\"8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a\"}],\"messagesTypes\":[],\"configurables\":[{\"name\":\"INITIAL_TARGET\",\"concreteTypeId\":\"0d79387ad3bacdc3b7aad9da3a96f4ce60d9a1b6002df254069ad95a3931d5c8\",\"offset\":13368},{\"name\":\"INITIAL_OWNER\",\"concreteTypeId\":\"192bc7098e2fe60635a9918afb563e4e5419d386da2bdbf0d716b4bc8549802c\",\"offset\":13320}]}",)); - - let wallet = Wallet::new(attacker_signer, provider.clone()); - let attacker_account = ForcClientAccount::Wallet(wallet); - // Try to change target of the proxy with a random wallet which is not the owner of the proxy. - let res = update_proxy_contract_target(&attacker_account, proxy_id, dummy_contract_id_target) - .await - .err() - .unwrap(); - - node.kill().unwrap(); - assert!(res - .to_string() - .starts_with("transaction reverted: NotOwner")); -} - -// TODO: https://github.com/FuelLabs/sway/issues/6283 -// Add interactive tests for the happy path cases. This requires starting the node with funded accounts and setting up -// the wallet with the correct password. The tests should be run in a separate test suite that is not run by default. -// It would also require overriding `default_wallet_path` function for tests, so as not to interfere with the user's wallet. - -#[test] -fn test_deploy_interactive_missing_wallet() -> Result<(), rexpect::error::Error> { - let (mut node, port) = run_node(); - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - - // Spawn the forc-deploy binary using cargo run - let project_dir = test_data_path().join("standalone_contract"); - let mut process = spawn( - &format!( - "cargo run --bin forc-deploy -- --node-url {node_url} -p {}", - project_dir.display() - ), - Some(300000), - )?; - - // Confirmation prompts - process - .exp_string("\u{1b}[1;32mConfirming\u{1b}[0m transactions [deploy standalone_contract]")?; - process.exp_string(&format!("Network: {node_url}"))?; - process.exp_regex("Could not find a wallet at")?; - process.send_line("n")?; - - process.process.exit()?; - node.kill().unwrap(); - Ok(()) -} - -#[tokio::test] -async fn chunked_deploy() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("big_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_contract = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - node.kill().unwrap(); - - assert!(deployed_contract.chunked); -} - -#[tokio::test] -async fn chunked_deploy_re_routes_calls() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("big_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_contract = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - - assert_big_contract_calls(wallet_unlocked, deployed_contract.id).await; - - node.kill().unwrap(); -} - -#[tokio::test] -async fn chunked_deploy_with_proxy_re_routes_call() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("big_contract"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - let proxy = Proxy { - enabled: true, - address: None, - }; - patch_manifest_file_with_proxy_table(tmp_dir.path(), proxy).unwrap(); - - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_contract = expect_deployed_contract(deploy(cmd).await.unwrap().remove(0)); - - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - - assert_big_contract_calls(wallet_unlocked, deployed_contract.id).await; - - node.kill().unwrap(); -} - -#[tokio::test] -async fn can_deploy_script() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("deployed_script"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - - expect_deployed_script(deploy(cmd).await.unwrap().remove(0)); - node.kill().unwrap(); -} - -#[tokio::test] -async fn deploy_script_calls() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("deployed_script"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - - expect_deployed_script(deploy(cmd).await.unwrap().remove(0)); - - // Deploy the contract the script is going to be calling. - let contract_tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, contract_tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(contract_tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(contract_tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_packages = deploy(cmd).await.unwrap().remove(0); - let contract = expect_deployed_contract(deployed_packages); - let contract_id = contract.id; - - abigen!(Script( - name = "MyScript", - abi = "forc-plugins/forc-client/test/data/deployed_script/deployed_script-abi.json" - )); - - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - - let loader_path = tmp_dir.path().join("out/deployed_script-loader.bin"); - let instance = MyScript::new(wallet_unlocked, &loader_path.display().to_string()); - - let contract_id_bits256 = Bits256(contract.id.into()); - let call_handler = instance - .main(10, contract_id_bits256) - .with_contract_ids(&[contract_id]) - .call() - .await - .unwrap(); - let (configs, with_input, without_input, from_contract) = call_handler.value; - let receipts = call_handler.tx_status.receipts; - - assert!(configs.0); // bool - assert_eq!(configs.1, 8); // u8 - assert_eq!(configs.2, 16); // u16 - assert_eq!(configs.3, 32); // u32 - assert_eq!(configs.4, 63); // u64 - assert_eq!(configs.5, 8.into()); // u256 - assert_eq!( - configs.6, - Bits256::from_hex_str("0x0101010101010101010101010101010101010101010101010101010101010101") - .unwrap() - ); // b256 - assert_eq!( - configs.7, - SizedAsciiString::new("fuel".to_string()).unwrap() - ); // str[4] - assert_eq!(configs.8, (8, true)); // tuple - assert_eq!(configs.9, [253, 254, 255]); // array - - let expected_struct = StructWithGeneric { - field_1: 8, - field_2: 16, - }; - assert_eq!(configs.10, expected_struct); // struct - - let expected_enum = EnumWithGeneric::VariantOne(true); - assert_eq!(configs.11, expected_enum); // enum - - assert!(with_input); // 10 % 2 == 0 - assert_eq!(without_input, 2500); // 25 * 100 = 2500 - - assert_eq!(from_contract, 5); - - receipts.iter().find(|receipt| { - if let fuel_tx::Receipt::LogData { data, .. } = receipt { - matches!(data.as_ref(), Some(bytes) if bytes.as_ref() == [0x08]) - } else { - false - } - }); - - node.kill().unwrap(); -} - -#[tokio::test] -async fn can_deploy_predicates() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("deployed_predicate"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - - expect_deployed_predicate(deploy(cmd).await.unwrap().remove(0)); - node.kill().unwrap(); -} - -#[tokio::test] -async fn deployed_predicate_call() { - let (mut node, port) = run_node(); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("deployed_predicate"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - expect_deployed_predicate(deploy(cmd).await.unwrap().remove(0)); - - abigen!(Predicate( - name = "MyPredicate", - abi = "forc-plugins/forc-client/test/data/deployed_predicate/deployed_predicate-abi.json" - )); - - let provider = Provider::connect(&node_url).await.unwrap(); - let consensus_parameters = provider.consensus_parameters().await.unwrap(); - let base_asset_id = consensus_parameters.base_asset_id(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider.clone()); - let loader_path = tmp_dir.path().join("out/deployed_predicate-loader.bin"); - let strct = StructWithGeneric { - field_1: 8, - field_2: 16, - }; - let enm = EnumWithGeneric::VariantOne(true); - let encoded_data = MyPredicateEncoder::default() - .encode_data(true, 8, strct, enm) - .unwrap(); - let predicate: fuels::prelude::Predicate = - fuels::prelude::Predicate::load_from(&loader_path.display().to_string()) - .unwrap() - .with_data(encoded_data) - .with_provider(provider); - - // lock some amount under the predicate - wallet_unlocked - .transfer( - predicate.address(), - 5000, - *base_asset_id, - TxPolicies::default(), - ) - .await - .unwrap(); - - // Check predicate balance. - let balance_before = predicate.get_asset_balance(base_asset_id).await.unwrap(); - assert_eq!(balance_before, 5000); - - // Try to spend it - let amount_to_unlock = 300; - let response = predicate - .transfer( - wallet_unlocked.address(), - amount_to_unlock, - *base_asset_id, - TxPolicies::default(), - ) - .await - .unwrap(); - - // Check predicate balance again. - let balance_after = predicate.get_asset_balance(base_asset_id).await.unwrap(); - let total_fee = response.tx_status.total_fee as u128; - let spent = balance_before - .checked_sub(balance_after) - .expect("predicate balance increased unexpectedly"); - // Provider::transfer currently over-estimates the required fee in some cases, so we check - // the actual spend from the predicate instead of trusting the reported fee blindly. - let actual_fee = spent - .checked_sub(amount_to_unlock as u128) - .expect("predicate spent less than the unlocked amount"); - assert!( - actual_fee <= total_fee, - "network fee {actual_fee} exceeded reported total_fee {total_fee}" - ); - - node.kill().unwrap(); -} - -/// Generates a script instance using SDK, and returns the result as a string. -async fn call_with_sdk_generated_overrides(node_url: &str, contract_id: ContractId) -> String { - let project_dir = test_data_path().join("deployed_script"); - abigen!(Script( - name = "MyScript", - abi = "forc-plugins/forc-client/test/data/deployed_script/deployed_script-abi.json" - )); - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - let bin_dir = project_dir.join("deployed_script.bin"); - let script_instance = MyScript::new(wallet_unlocked, bin_dir.display().to_string().as_str()); - - let strc = StructWithGeneric { - field_1: 1u8, - field_2: 2, - }; - let encoded = MyScriptConfigurables::default() - .with_BOOL(false) - .unwrap() - .with_U8(1) - .unwrap() - .with_U16(2) - .unwrap() - .with_U32(3) - .unwrap() - .with_U64(4) - .unwrap() - .with_U256(5.into()) - .unwrap() - .with_B256(Bits256::zeroed()) - .unwrap() - .with_ARRAY([1, 2, 3]) - .unwrap() - .with_STRUCT(strc) - .unwrap() - .with_ENUM(EnumWithGeneric::VariantTwo) - .unwrap(); - - let mut script_instance_with_configs = script_instance.with_configurables(encoded); - - let loader_from_sdk = script_instance_with_configs - .convert_into_loader() - .await - .unwrap(); - - let contract_ids_bits256 = Bits256(contract_id.into()); - format!( - "{:?}", - loader_from_sdk - .main(10, contract_ids_bits256) - .with_contract_ids(&[contract_id]) - .call() - .await - .unwrap() - .value - ) -} - -/// Generates a script instance using the shifted abi, and returns the result as a string. -async fn call_with_forc_generated_overrides(node_url: &str, contract_id: ContractId) -> String { - let provider = Provider::connect(&node_url).await.unwrap(); - let secret_key = SecretKey::from_str(forc_client::constants::DEFAULT_PRIVATE_KEY).unwrap(); - let signer = PrivateKeySigner::new(secret_key); - let wallet_unlocked = Wallet::new(signer, provider); - let tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("deployed_script"); - copy_dir(&project_dir, tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(tmp_dir.path()).unwrap(); - - let target = NodeTarget { - node_url: Some(node_url.to_string()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let pkg = Pkg { - path: Some(tmp_dir.path().display().to_string()), - ..Default::default() - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - - expect_deployed_script(deploy(cmd).await.unwrap().remove(0)); - - // Since `abigen!` macro does not allow for dynamic paths, we need to - // pre-generate the loader bin and abi and read them from project dir. Here - // we are ensuring forc-deploy indeed generated the files we are basing our - // tests below. - let generated_loader_abi_path = tmp_dir.path().join("out/deployed_script-loader-abi.json"); - let generated_loader_abi = fs::read_to_string(generated_loader_abi_path).unwrap(); - - // this path is basically, `forc-plugins/forc-client/test/data/deployed_script/deployed_script-loader-abi.json`. - let used_loader_abi_path = project_dir.join("deployed_script-loader-abi.json"); - let used_loader_abi = fs::read_to_string(used_loader_abi_path).unwrap(); - - assert_eq!(generated_loader_abi, used_loader_abi); - - let generated_loader_bin = tmp_dir.path().join("out/deployed_script-loader.bin"); - abigen!(Script( - name = "MyScript", - abi = "forc-plugins/forc-client/test/data/deployed_script/deployed_script-loader-abi.json" - )); - let forc_generated_script_instance = MyScript::new( - wallet_unlocked, - generated_loader_bin.display().to_string().as_str(), - ); - let strc = StructWithGeneric { - field_1: 1u8, - field_2: 2, - }; - let encoded = MyScriptConfigurables::default() - .with_BOOL(false) - .unwrap() - .with_U8(1) - .unwrap() - .with_U16(2) - .unwrap() - .with_U32(3) - .unwrap() - .with_U64(4) - .unwrap() - .with_U256(5.into()) - .unwrap() - .with_B256(Bits256::zeroed()) - .unwrap() - .with_ARRAY([1, 2, 3]) - .unwrap() - .with_STRUCT(strc) - .unwrap() - .with_ENUM(EnumWithGeneric::VariantTwo) - .unwrap(); - - let forc_generated_script_with_configs = - forc_generated_script_instance.with_configurables(encoded); - let contract_ids_bits256 = Bits256(contract_id.into()); - format!( - "{:?}", - forc_generated_script_with_configs - .main(10, contract_ids_bits256) - .with_contract_ids(&[contract_id]) - .call() - .await - .unwrap() - .value - ) -} - -#[tokio::test] -async fn offset_shifted_abi_works() { - // To test if offset shifted abi works or not, we generate a loader - // contract using sdk and give a configurable override, and call the - // main function. - - // We also create the shifted abi using forc-deploy and create a script - // instance using this new shifted abi, and generate a normal script out of - // the loader binary generated again by forc-deploy. - - // We then override the configurables with the same values as sdk flow on - // this script, generated with loader abi and bin coming from forc-deploy. - - // If returned value is equal, than the configurables work correctly. - let (mut node, port) = run_node(); - // Deploy the contract the script is going to be calling. - let contract_tmp_dir = tempdir().unwrap(); - let project_dir = test_data_path().join("standalone_contract"); - copy_dir(&project_dir, contract_tmp_dir.path()).unwrap(); - patch_manifest_file_with_path_std(contract_tmp_dir.path()).unwrap(); - - let pkg = Pkg { - path: Some(contract_tmp_dir.path().display().to_string()), - ..Default::default() - }; - - let node_url = format!("http://127.0.0.1:{port}/v1/graphql"); - let target = NodeTarget { - node_url: Some(node_url.clone()), - target: None, - testnet: false, - mainnet: false, - devnet: false, - }; - let cmd = cmd::Deploy { - pkg, - salt: Some(vec![format!("{}", Salt::default())]), - node: target, - default_signer: true, - ..Default::default() - }; - let deployed_packages = deploy(cmd).await.unwrap().remove(0); - let contract = expect_deployed_contract(deployed_packages); - let contract_id = contract.id; - // Generating the sdk loader bytecode with configurables. - let loader_with_configs_from_sdk = - call_with_sdk_generated_overrides(&node_url, contract_id).await; - - // Generating the forc-deploy loader bytecode and loader abi. - let loader_with_configs_from_forc = - call_with_forc_generated_overrides(&node_url, contract_id).await; - pretty_assertions::assert_eq!(loader_with_configs_from_forc, loader_with_configs_from_sdk); - - node.kill().unwrap() -} diff --git a/forc-plugins/forc-mcp/Cargo.toml b/forc-plugins/forc-mcp/Cargo.toml index 90594df7930..1baa521602f 100644 --- a/forc-plugins/forc-mcp/Cargo.toml +++ b/forc-plugins/forc-mcp/Cargo.toml @@ -23,7 +23,6 @@ axum.workspace = true chrono.workspace = true clap.workspace = true forc-client.workspace = true -forc-tx.workspace = true fuel-crypto.workspace = true fuels-core.workspace = true hex.workspace = true diff --git a/forc-plugins/forc-mcp/src/forc_call/mod.rs b/forc-plugins/forc-mcp/src/forc_call/mod.rs index 34f99b4825a..f9c81dc304f 100644 --- a/forc-plugins/forc-mcp/src/forc_call/mod.rs +++ b/forc-plugins/forc-mcp/src/forc_call/mod.rs @@ -512,7 +512,7 @@ fn build_call_command( devnet: false, }; - let gas = gas_price.map(|price| forc_tx::Gas { + let gas = gas_price.map(|price| forc_client::cmd::call::Gas { price: Some(price), script_gas_limit: None, max_fee: None, @@ -767,7 +767,7 @@ mod tests { // Deploy the test contract let contract_id = Contract::load_from( - "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types.bin", + "test/data/contract_with_types/contract_with_types.bin", LoadConfiguration::default(), )? .deploy(&wallet, TxPolicies::default()) @@ -775,7 +775,7 @@ mod tests { .contract_id; // Use the existing ABI file directly (no temp file needed) - let abi_path = "../../forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json"; + let abi_path = "test/data/contract_with_types/contract_with_types-abi.json"; Ok(E2ETestFixture { contract_id: format!("0x{contract_id}"), diff --git a/forc-plugins/forc-client/test/data/contract_with_types/Forc.toml b/forc-plugins/forc-mcp/test/data/contract_with_types/Forc.toml similarity index 100% rename from forc-plugins/forc-client/test/data/contract_with_types/Forc.toml rename to forc-plugins/forc-mcp/test/data/contract_with_types/Forc.toml diff --git a/forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json b/forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types-abi.json similarity index 100% rename from forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-abi.json rename to forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types-abi.json diff --git a/forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-storage_slots.json b/forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types-storage_slots.json similarity index 100% rename from forc-plugins/forc-client/test/data/contract_with_types/contract_with_types-storage_slots.json rename to forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types-storage_slots.json diff --git a/forc-plugins/forc-client/test/data/contract_with_types/contract_with_types.bin b/forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types.bin similarity index 100% rename from forc-plugins/forc-client/test/data/contract_with_types/contract_with_types.bin rename to forc-plugins/forc-mcp/test/data/contract_with_types/contract_with_types.bin diff --git a/forc-plugins/forc-client/test/data/contract_with_types/src/main.sw b/forc-plugins/forc-mcp/test/data/contract_with_types/src/main.sw similarity index 100% rename from forc-plugins/forc-client/test/data/contract_with_types/src/main.sw rename to forc-plugins/forc-mcp/test/data/contract_with_types/src/main.sw