diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml deleted file mode 100644 index 62469fd7..00000000 --- a/.github/workflows/bindings-ts.yml +++ /dev/null @@ -1,43 +0,0 @@ - -name: bindings typescript - -on: - push: - branches: [main, release/**] - pull_request: - -jobs: - test: - name: test generated libraries - runs-on: ubuntu-22.04 - services: - rpc: - image: stellar/quickstart:soroban-dev@sha256:0ad51035cf7caba2fd99c7c1fad0945df6932be7d5c893e1520ccdef7d6a6ffe - ports: - - 8000:8000 - env: - ENABLE_LOGS: true - NETWORK: local - ENABLE_SOROBAN_RPC: true - options: >- - --health-cmd "curl --no-progress-meter --fail-with-body -X POST \"http://localhost:8000/soroban/rpc\" -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":8675309,\"method\":\"getNetwork\"}' && curl --no-progress-meter \"http://localhost:8000/friendbot\" | grep '\"invalid_field\": \"addr\"'" - --health-interval 10s - --health-timeout 5s - --health-retries 50 - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - run: rustup update - - run: cargo build - - run: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - run: npm ci && npm run test - working-directory: cmd/crates/soroban-spec-typescript/ts-tests diff --git a/.github/workflows/full-help-docs.yml b/.github/workflows/full-help-docs.yml deleted file mode 100644 index c8fbd9e0..00000000 --- a/.github/workflows/full-help-docs.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: CLI Help Doc - -on: [push, pull_request] - -permissions: - contents: read - # Optional: allow read access to pull request. Use with `only-new-issues` option. - pull-requests: read - -jobs: - doc_check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: stellar/actions/rust-cache@main - - run: rustup update - - name: Generate help doc - # this looks goofy to get GITHUB_OUTPUT to work with multi-line return values; - # see https://stackoverflow.com/a/74266196/249801 - run: | - cargo md-gen - raw_diff=$(git diff docs/soroban-cli-full-docs.md) - if [ "$raw_diff" != "" ]; then echo ""; echo "Unexpected docs change:"; echo "======================="; echo ""; echo "$raw_diff"; echo ""; echo "======================="; echo ""; fi - echo diff=$raw_diff >> $GITHUB_OUTPUT - id: doc-gen - - - name: Check diff - if: steps.doc-gen.outputs.diff != '' - uses: actions/github-script@v3 - with: - script: | - core.setFailed('Expected `doc-gen` to generate no diffs, but got diff shown in previous step.\n\nUpdate the full help docs:\n\n cargo md-gen\n\nDo this automatically on every commit by installing the pre-commit hook as explained in the README.') diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9028df78..9fd16cda 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -77,7 +77,7 @@ jobs: - if: startsWith(matrix.target, 'x86_64') # specify directories explicitly to avoid building the preflight library (otherwise it will fail with missing symbols) run: | - for I in cmd/soroban-cli cmd/crates/* cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world ; do + for I in cmd/crates/* cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world ; do cargo test --target ${{ matrix.target }} --manifest-path $I/Cargo.toml done @@ -118,7 +118,7 @@ jobs: cargo-hack-feature-options: --features opt --ignore-unknown-features uses: stellar/actions/.github/workflows/rust-publish-dry-run-v2.yml@main with: - crates: soroban-spec-tools soroban-spec-json soroban-spec-typescript soroban-test soroban-cli + crates: soroban-test runs-on: ${{ matrix.os }} target: ${{ matrix.target }} cargo-hack-feature-options: ${{ matrix.cargo-hack-feature-options }} diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index 459b391e..eb1a2616 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -126,4 +126,5 @@ jobs: go-version: ${{ matrix.go }} - name: Run Soroban RPC Integration Tests run: | + make install_rust go test -race -run '${{ matrix.test }}' -timeout 60m -v ./cmd/soroban-rpc/internal/test/... diff --git a/Cargo.lock b/Cargo.lock index 451235fa..938336ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,7 +142,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -273,7 +273,7 @@ dependencies = [ "num-bigint", "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -314,7 +314,6 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ - "jobserver", "libc", ] @@ -349,15 +348,6 @@ dependencies = [ "clap_derive", ] -[[package]] -name = "clap-markdown" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325f50228f76921784b6d9f2d62de6778d834483248eefecd27279174797e579" -dependencies = [ - "clap", -] - [[package]] name = "clap_builder" version = "4.4.11" @@ -388,7 +378,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -397,16 +387,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -568,7 +548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" dependencies = [ "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -609,51 +589,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", -] - -[[package]] -name = "cxx" -version = "1.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7129e341034ecb940c9072817cd9007974ea696844fc4dd582dc1653a7fbe2e8" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a24f3f5f8eed71936f21e570436f024f5c2e25628f7496aa7ccd03b90109d5" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.39", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06fdd177fc61050d63f67f5bd6351fac6ab5526694ea8e359cd9cd3b75857f44" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -677,7 +613,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.39", + "syn", ] [[package]] @@ -688,7 +624,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -719,15 +655,9 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "difflib" version = "0.4.0" @@ -1383,15 +1313,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.66" @@ -1506,15 +1427,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "link-cplusplus" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -1634,7 +1546,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -1710,7 +1622,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -1814,7 +1726,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -1916,16 +1828,6 @@ dependencies = [ "soroban-simulation", ] -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] - [[package]] name = "prettyplease" version = "0.2.15" @@ -1933,7 +1835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn", ] [[package]] @@ -2176,12 +2078,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "ryu" version = "1.0.16" @@ -2212,12 +2108,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scratch" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" - [[package]] name = "sct" version = "0.7.1" @@ -2314,7 +2204,7 @@ checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -2354,7 +2244,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -2485,20 +2375,18 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] name = "soroban-cli" version = "20.2.0" +source = "git+https://github.com/stellar/soroban-tools?rev=6a19e181a5699c33b409f1c39f6fdc6b784769e0#6a19e181a5699c33b409f1c39f6fdc6b784769e0" dependencies = [ - "assert_cmd", - "assert_fs", "base64 0.21.5", "cargo_metadata", "chrono", "clap", - "clap-markdown", "clap_complete", "crate-git-revision 0.0.4", "csv", @@ -2517,7 +2405,6 @@ dependencies = [ "num-bigint", "openssl", "pathdiff", - "predicates 2.1.5", "rand", "regex", "rpassword", @@ -2531,10 +2418,10 @@ dependencies = [ "soroban-env-host", "soroban-ledger-snapshot", "soroban-sdk", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", "soroban-spec-json", "soroban-spec-rust", - "soroban-spec-tools", + "soroban-spec-tools 20.2.0 (git+https://github.com/stellar/soroban-tools?rev=6a19e181a5699c33b409f1c39f6fdc6b784769e0)", "soroban-spec-typescript", "stellar-strkey 0.0.7", "stellar-xdr", @@ -2547,7 +2434,6 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", - "wasm-opt", "wasmparser 0.90.0", "which", ] @@ -2615,7 +2501,7 @@ dependencies = [ "serde", "serde_json", "stellar-xdr", - "syn 2.0.39", + "syn", ] [[package]] @@ -2667,10 +2553,10 @@ dependencies = [ "rustc_version", "sha2 0.10.8", "soroban-env-common", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", "soroban-spec-rust", "stellar-xdr", - "syn 2.0.39", + "syn", ] [[package]] @@ -2685,6 +2571,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "soroban-spec" +version = "20.1.0" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=811ce3da801c03a21d5fa80fda187c0f1012240f#811ce3da801c03a21d5fa80fda187c0f1012240f" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser 0.88.0", +] + [[package]] name = "soroban-spec" version = "20.1.0" @@ -2699,13 +2596,13 @@ dependencies = [ [[package]] name = "soroban-spec-json" version = "20.2.0" +source = "git+https://github.com/stellar/soroban-tools?rev=6a19e181a5699c33b409f1c39f6fdc6b784769e0#6a19e181a5699c33b409f1c39f6fdc6b784769e0" dependencies = [ - "pretty_assertions", "serde", "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", "stellar-xdr", "thiserror", ] @@ -2719,49 +2616,63 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", "stellar-xdr", - "syn 2.0.39", + "syn", "thiserror", ] [[package]] name = "soroban-spec-tools" version = "20.2.0" +source = "git+https://github.com/stellar/soroban-tools?rev=6a19e181a5699c33b409f1c39f6fdc6b784769e0#6a19e181a5699c33b409f1c39f6fdc6b784769e0" dependencies = [ "base64 0.21.5", "ethnum", "hex", "itertools 0.10.5", "serde_json", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", + "stellar-strkey 0.0.7", + "stellar-xdr", + "thiserror", + "wasmparser 0.90.0", +] + +[[package]] +name = "soroban-spec-tools" +version = "20.2.0" +source = "git+https://github.com/stellar/soroban-tools?rev=7ee51fa731aa21365363aef687bae6040b81aa7a#7ee51fa731aa21365363aef687bae6040b81aa7a" +dependencies = [ + "base64 0.21.5", + "ethnum", + "hex", + "itertools 0.10.5", + "serde_json", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=811ce3da801c03a21d5fa80fda187c0f1012240f)", "stellar-strkey 0.0.7", "stellar-xdr", "thiserror", - "tokio", "wasmparser 0.90.0", - "which", ] [[package]] name = "soroban-spec-typescript" version = "20.2.0" +source = "git+https://github.com/stellar/soroban-tools?rev=6a19e181a5699c33b409f1c39f6fdc6b784769e0#6a19e181a5699c33b409f1c39f6fdc6b784769e0" dependencies = [ "base64 0.21.5", "heck", "include_dir", "itertools 0.10.5", - "pretty_assertions", "prettyplease", "serde", "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", "stellar-xdr", - "temp-dir", "thiserror", - "walkdir", ] [[package]] @@ -2779,8 +2690,8 @@ dependencies = [ "soroban-env-host", "soroban-ledger-snapshot", "soroban-sdk", - "soroban-spec", - "soroban-spec-tools", + "soroban-spec 20.1.0 (git+https://github.com/stellar/rs-soroban-sdk?rev=e6c2c900ab82b5f6eec48f69cb2cb519e19819cb)", + "soroban-spec-tools 20.2.0 (git+https://github.com/stellar/soroban-tools?rev=7ee51fa731aa21365363aef687bae6040b81aa7a)", "stellar-strkey 0.0.7", "thiserror", "tokio", @@ -2875,42 +2786,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.39" @@ -2922,12 +2803,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "temp-dir" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" - [[package]] name = "tempfile" version = "3.8.1" @@ -3025,7 +2900,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -3128,7 +3003,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -3233,7 +3108,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -3308,12 +3183,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - [[package]] name = "untrusted" version = "0.9.0" @@ -3410,7 +3279,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn", "wasm-bindgen-shared", ] @@ -3432,7 +3301,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3443,46 +3312,6 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" -[[package]] -name = "wasm-opt" -version = "0.114.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effbef3bd1dde18acb401f73e740a6f3d4a1bc651e9773bddc512fe4d8d68f67" -dependencies = [ - "anyhow", - "libc", - "strum", - "strum_macros", - "tempfile", - "thiserror", - "wasm-opt-cxx-sys", - "wasm-opt-sys", -] - -[[package]] -name = "wasm-opt-cxx-sys" -version = "0.114.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c09e24eb283919ace2ed5733bda4842a59ce4c8de110ef5c6d98859513d17047" -dependencies = [ - "anyhow", - "cxx", - "cxx-build", - "wasm-opt-sys", -] - -[[package]] -name = "wasm-opt-sys" -version = "0.114.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f2f817bed2e8d65eb779fa37317e74de15585751f903c9118342d1970703a4" -dependencies = [ - "anyhow", - "cc", - "cxx", - "cxx-build", -] - [[package]] name = "wasmi_arena" version = "0.4.0" @@ -3711,12 +3540,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "zeroize" version = "1.7.0" @@ -3734,5 +3557,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index c88cd4ab..24f2e66e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,12 @@ [workspace] resolver = "2" members = [ - "cmd/soroban-cli", - "cmd/crates/*", + "cmd/crates/soroban-test", "cmd/crates/soroban-test/tests/fixtures/test-wasms/*", "cmd/crates/soroban-test/tests/fixtures/hello", "cmd/soroban-rpc/lib/preflight", ] -default-members = ["cmd/soroban-cli", "cmd/crates/soroban-test"] +default-members = ["cmd/crates/soroban-test"] exclude = ["cmd/crates/soroban-test/tests/fixtures/hello"] [workspace.package] @@ -40,15 +39,18 @@ rev = "e6c2c900ab82b5f6eec48f69cb2cb519e19819cb" [workspace.dependencies.soroban-spec-json] version = "20.2.0" -path = "./cmd/crates/soroban-spec-json" +git = "https://github.com/stellar/soroban-tools" +rev = "7ee51fa731aa21365363aef687bae6040b81aa7a" [workspace.dependencies.soroban-spec-typescript] version = "20.2.0" -path = "./cmd/crates/soroban-spec-typescript" +git = "https://github.com/stellar/soroban-tools" +rev = "7ee51fa731aa21365363aef687bae6040b81aa7a" [workspace.dependencies.soroban-spec-tools] version = "20.2.0" -path = "./cmd/crates/soroban-spec-tools" +git = "https://github.com/stellar/soroban-tools" +rev = "7ee51fa731aa21365363aef687bae6040b81aa7a" [workspace.dependencies.soroban-sdk] version = "=20.1.0" @@ -67,7 +69,8 @@ rev = "e6c2c900ab82b5f6eec48f69cb2cb519e19819cb" [workspace.dependencies.soroban-cli] version = "20.2.0" -path = "cmd/soroban-cli" +git = "https://github.com/stellar/soroban-tools" +rev = "6a19e181a5699c33b409f1c39f6fdc6b784769e0" [workspace.dependencies.stellar-xdr] version = "=20.0.2" diff --git a/Makefile b/Makefile index 000b2aef..39e3d743 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,10 @@ ifeq ($(strip $(REPOSITORY_VERSION)),) endif REPOSITORY_BRANCH := "$(shell git rev-parse --abbrev-ref HEAD)" BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%dT%H:%M:%S') -GOLDFLAGS := -X 'github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/config.Version=${REPOSITORY_VERSION}' \ - -X 'github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/config.CommitHash=${REPOSITORY_COMMIT_HASH}' \ - -X 'github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}' \ - -X 'github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/config.Branch=${REPOSITORY_BRANCH}' +GOLDFLAGS := -X 'github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/config.Version=${REPOSITORY_VERSION}' \ + -X 'github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/config.CommitHash=${REPOSITORY_COMMIT_HASH}' \ + -X 'github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}' \ + -X 'github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/config.Branch=${REPOSITORY_BRANCH}' # The following works around incompatibility between the rust and the go linkers - @@ -41,7 +41,7 @@ Cargo.lock: Cargo.toml cargo update --workspace install_rust: Cargo.lock - cargo install --path ./cmd/soroban-cli --debug + cargo install soroban-cli --version 20.2.0 cargo install --path ./cmd/crates/soroban-test/tests/fixtures/hello --root ./target --debug --quiet install: install_rust build-libpreflight @@ -53,9 +53,6 @@ build_rust: Cargo.lock build_go: build-libpreflight go build -ldflags="${GOLDFLAGS}" ${MACOS_MIN_VER} ./... -# regenerate the example lib in `cmd/crates/soroban-spec-typsecript/fixtures/ts` -build-snapshot: typescript-bindings-fixtures - build: build_rust build_go build-libpreflight: Cargo.lock @@ -100,14 +97,6 @@ lint-changes: lint: golangci-lint run ./... -typescript-bindings-fixtures: build-test-wasms - cargo run -- contract bindings typescript \ - --wasm ./target/wasm32-unknown-unknown/test-wasms/test_custom_types.wasm \ - --contract-id CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK \ - --network futurenet \ - --output-dir ./cmd/crates/soroban-spec-typescript/fixtures/test_custom_types \ - --overwrite - # PHONY lists all the targets that aren't file names, so that make would skip the timestamp based check. -.PHONY: publish clean fmt watch check e2e-test test build-test-wasms install build build-soroban-rpc build-libpreflight lint lint-changes build-snapshot typescript-bindings-fixtures +.PHONY: publish clean fmt watch check e2e-test test build-test-wasms install build build-soroban-rpc build-libpreflight lint lint-changes diff --git a/cmd/crates/soroban-spec-json/Cargo.toml b/cmd/crates/soroban-spec-json/Cargo.toml deleted file mode 100644 index 04051ea6..00000000 --- a/cmd/crates/soroban-spec-json/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "soroban-spec-json" -description = "Soroban contract spec utilities for generating JSON." -homepage = "https://github.com/stellar/soroban-tools" -repository = "https://github.com/stellar/soroban-tools" -authors = ["Stellar Development Foundation "] -readme = "README.md" -license = "Apache-2.0" -version.workspace = true -edition = "2021" -rust-version.workspace = true - -[dependencies] -soroban-spec = { workspace = true } -thiserror = "1.0.32" -serde = "1.0.82" -serde_derive = "1.0.82" -serde_json = "1.0.82" -sha2 = "0.9.9" - -[dependencies.stellar-xdr] -workspace = true -features = ["curr", "std", "serde"] - -[dev_dependencies] -pretty_assertions = "1.2.1" diff --git a/cmd/crates/soroban-spec-json/README.md b/cmd/crates/soroban-spec-json/README.md deleted file mode 100644 index cf3c38c4..00000000 --- a/cmd/crates/soroban-spec-json/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# soroban-spec-json - -Generation of JSON that describes a Soroban contract specification / interface. diff --git a/cmd/crates/soroban-spec-json/src/lib.rs b/cmd/crates/soroban-spec-json/src/lib.rs deleted file mode 100644 index cdc64665..00000000 --- a/cmd/crates/soroban-spec-json/src/lib.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::{fs, io}; - -pub mod types; - -use sha2::{Digest, Sha256}; - -use stellar_xdr::curr::ScSpecEntry; -use types::Entry; - -use soroban_spec::read::{from_wasm, FromWasmError}; - -#[derive(thiserror::Error, Debug)] -pub enum GenerateFromFileError { - #[error("reading file: {0}")] - Io(io::Error), - #[error("sha256 does not match, expected: {expected}")] - VerifySha256 { expected: String }, - #[error("parsing contract spec: {0}")] - Parse(stellar_xdr::curr::Error), - #[error("getting contract spec: {0}")] - GetSpec(FromWasmError), -} - -/// # Errors -/// -/// Will return an error if the file cannot be read, or the wasm cannot be parsed. -pub fn generate_from_file( - file: &str, - verify_sha256: Option<&str>, -) -> Result { - // Read file. - let wasm = fs::read(file).map_err(GenerateFromFileError::Io)?; - - // Produce hash for file. - let sha256 = Sha256::digest(&wasm); - let sha256 = format!("{sha256:x}"); - - if let Some(verify_sha256) = verify_sha256 { - if verify_sha256 != sha256 { - return Err(GenerateFromFileError::VerifySha256 { expected: sha256 }); - } - } - - // Generate code. - let json = generate_from_wasm(&wasm).map_err(GenerateFromFileError::GetSpec)?; - Ok(json) -} - -/// # Errors -/// -/// Will return an error if the wasm cannot be parsed. -pub fn generate_from_wasm(wasm: &[u8]) -> Result { - let spec = from_wasm(wasm)?; - let json = generate(&spec); - Ok(json) -} - -/// # Panics -/// -/// If `serde_json::to_string_pretty` fails to serialize the spec entries. -pub fn generate(spec: &[ScSpecEntry]) -> String { - let collected: Vec<_> = spec.iter().map(Entry::from).collect(); - serde_json::to_string_pretty(&collected).expect("serialization of the spec entries should not have any failure cases as all keys are strings and the serialize implementations are derived") -} - -#[allow(clippy::too_many_lines)] -#[cfg(test)] -mod test { - use pretty_assertions::assert_eq; - use soroban_spec::read::from_wasm; - - use super::generate; - - const EXAMPLE_WASM: &[u8] = - include_bytes!("../../../../target/wasm32-unknown-unknown/test-wasms/test_udt.wasm"); - - #[test] - fn example() { - let entries = from_wasm(EXAMPLE_WASM).unwrap(); - let json = generate(&entries); - assert_eq!( - json, - r#"[ - { - "type": "enum", - "doc": "", - "name": "UdtEnum2", - "cases": [ - { - "doc": "", - "name": "A", - "value": 10 - }, - { - "doc": "", - "name": "B", - "value": 15 - } - ] - }, - { - "type": "union", - "doc": "", - "name": "UdtEnum", - "cases": [ - { - "doc": "", - "name": "UdtA", - "values": [] - }, - { - "doc": "", - "name": "UdtB", - "values": [ - { - "type": "custom", - "name": "UdtStruct" - } - ] - }, - { - "doc": "", - "name": "UdtC", - "values": [ - { - "type": "custom", - "name": "UdtEnum2" - } - ] - }, - { - "doc": "", - "name": "UdtD", - "values": [ - { - "type": "custom", - "name": "UdtTuple" - } - ] - } - ] - }, - { - "type": "struct", - "doc": "", - "name": "UdtTuple", - "fields": [ - { - "doc": "", - "name": "0", - "value": { - "type": "i64" - } - }, - { - "doc": "", - "name": "1", - "value": { - "type": "vec", - "element": { - "type": "i64" - } - } - } - ] - }, - { - "type": "struct", - "doc": "", - "name": "UdtStruct", - "fields": [ - { - "doc": "", - "name": "a", - "value": { - "type": "i64" - } - }, - { - "doc": "", - "name": "b", - "value": { - "type": "i64" - } - }, - { - "doc": "", - "name": "c", - "value": { - "type": "vec", - "element": { - "type": "i64" - } - } - } - ] - }, - { - "type": "function", - "doc": "", - "name": "add", - "inputs": [ - { - "doc": "", - "name": "a", - "value": { - "type": "custom", - "name": "UdtEnum" - } - }, - { - "doc": "", - "name": "b", - "value": { - "type": "custom", - "name": "UdtEnum" - } - } - ], - "outputs": [ - { - "type": "i64" - } - ] - } -]"#, - ); - } -} diff --git a/cmd/crates/soroban-spec-json/src/types.rs b/cmd/crates/soroban-spec-json/src/types.rs deleted file mode 100644 index 8e3b2177..00000000 --- a/cmd/crates/soroban-spec-json/src/types.rs +++ /dev/null @@ -1,245 +0,0 @@ -use serde::Serialize; -use stellar_xdr::curr::{ - ScSpecEntry, ScSpecFunctionInputV0, ScSpecTypeDef, ScSpecUdtEnumCaseV0, - ScSpecUdtErrorEnumCaseV0, ScSpecUdtStructFieldV0, ScSpecUdtUnionCaseV0, -}; - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct StructField { - pub doc: String, - pub name: String, - pub value: Type, -} - -impl From<&ScSpecUdtStructFieldV0> for StructField { - fn from(f: &ScSpecUdtStructFieldV0) -> Self { - StructField { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - value: (&f.type_).into(), - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct FunctionInput { - pub doc: String, - pub name: String, - pub value: Type, -} - -impl From<&ScSpecFunctionInputV0> for FunctionInput { - fn from(f: &ScSpecFunctionInputV0) -> Self { - FunctionInput { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - value: (&f.type_).into(), - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UnionCase { - pub doc: String, - pub name: String, - pub values: Vec, -} - -impl From<&ScSpecUdtUnionCaseV0> for UnionCase { - fn from(c: &ScSpecUdtUnionCaseV0) -> Self { - let (doc, name, values) = match c { - ScSpecUdtUnionCaseV0::VoidV0(v) => ( - v.doc.to_utf8_string_lossy(), - v.name.to_utf8_string_lossy(), - vec![], - ), - ScSpecUdtUnionCaseV0::TupleV0(t) => ( - t.doc.to_utf8_string_lossy(), - t.name.to_utf8_string_lossy(), - t.type_.iter().map(Type::from).collect(), - ), - }; - UnionCase { doc, name, values } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct EnumCase { - pub doc: String, - pub name: String, - pub value: u32, -} - -impl From<&ScSpecUdtEnumCaseV0> for EnumCase { - fn from(c: &ScSpecUdtEnumCaseV0) -> Self { - EnumCase { - doc: c.doc.to_utf8_string_lossy(), - name: c.name.to_utf8_string_lossy(), - value: c.value, - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ErrorEnumCase { - pub doc: String, - pub name: String, - pub value: u32, -} - -impl From<&ScSpecUdtErrorEnumCaseV0> for EnumCase { - fn from(c: &ScSpecUdtErrorEnumCaseV0) -> Self { - EnumCase { - doc: c.doc.to_utf8_string_lossy(), - name: c.name.to_utf8_string_lossy(), - value: c.value, - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(tag = "type")] -#[serde(rename_all = "camelCase")] -pub enum Type { - Void, - Val, - U64, - I64, - U32, - I32, - U128, - I128, - U256, - I256, - Bool, - Symbol, - Error, - Bytes, - String, - Address, - Timepoint, - Duration, - Map { key: Box, value: Box }, - Option { value: Box }, - Result { value: Box, error: Box }, - Vec { element: Box }, - BytesN { n: u32 }, - Tuple { elements: Vec }, - Custom { name: String }, -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(tag = "type")] -#[serde(rename_all = "camelCase")] -pub enum Entry { - Function { - doc: String, - name: String, - inputs: Vec, - outputs: Vec, - }, - Struct { - doc: String, - name: String, - fields: Vec, - }, - Union { - doc: String, - name: String, - cases: Vec, - }, - Enum { - doc: String, - name: String, - cases: Vec, - }, - ErrorEnum { - doc: String, - name: String, - cases: Vec, - }, -} - -impl From<&ScSpecTypeDef> for Type { - fn from(spec: &ScSpecTypeDef) -> Self { - match spec { - ScSpecTypeDef::Map(map) => Type::Map { - key: Box::new(Type::from(map.key_type.as_ref())), - value: Box::new(Type::from(map.value_type.as_ref())), - }, - ScSpecTypeDef::Option(opt) => Type::Option { - value: Box::new(Type::from(opt.value_type.as_ref())), - }, - ScSpecTypeDef::Result(res) => Type::Result { - value: Box::new(Type::from(res.ok_type.as_ref())), - error: Box::new(Type::from(res.error_type.as_ref())), - }, - ScSpecTypeDef::Tuple(tuple) => Type::Tuple { - elements: tuple.value_types.iter().map(Type::from).collect(), - }, - ScSpecTypeDef::Vec(vec) => Type::Vec { - element: Box::new(Type::from(vec.element_type.as_ref())), - }, - ScSpecTypeDef::Udt(udt) => Type::Custom { - name: udt.name.to_utf8_string_lossy(), - }, - ScSpecTypeDef::BytesN(b) => Type::BytesN { n: b.n }, - ScSpecTypeDef::Val => Type::Val, - ScSpecTypeDef::U64 => Type::U64, - ScSpecTypeDef::I64 => Type::I64, - ScSpecTypeDef::U32 => Type::U32, - ScSpecTypeDef::I32 => Type::I32, - ScSpecTypeDef::U128 => Type::U128, - ScSpecTypeDef::I128 => Type::I128, - ScSpecTypeDef::U256 => Type::U256, - ScSpecTypeDef::I256 => Type::I256, - ScSpecTypeDef::Bool => Type::Bool, - ScSpecTypeDef::Symbol => Type::Symbol, - ScSpecTypeDef::Error => Type::Error, - ScSpecTypeDef::Bytes => Type::Bytes, - ScSpecTypeDef::String => Type::String, - ScSpecTypeDef::Address => Type::Address, - ScSpecTypeDef::Void => Type::Void, - ScSpecTypeDef::Timepoint => Type::Timepoint, - ScSpecTypeDef::Duration => Type::Duration, - } - } -} - -impl From<&ScSpecEntry> for Entry { - fn from(spec: &ScSpecEntry) -> Self { - match spec { - ScSpecEntry::FunctionV0(f) => Entry::Function { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - inputs: f.inputs.iter().map(FunctionInput::from).collect(), - outputs: f.outputs.iter().map(Type::from).collect(), - }, - ScSpecEntry::UdtStructV0(s) => Entry::Struct { - doc: s.doc.to_utf8_string_lossy(), - name: s.name.to_utf8_string_lossy(), - fields: s.fields.iter().map(StructField::from).collect(), - }, - ScSpecEntry::UdtUnionV0(u) => Entry::Union { - doc: u.doc.to_utf8_string_lossy(), - name: u.name.to_utf8_string_lossy(), - cases: u.cases.iter().map(UnionCase::from).collect(), - }, - ScSpecEntry::UdtEnumV0(e) => Entry::Enum { - doc: e.doc.to_utf8_string_lossy(), - name: e.name.to_utf8_string_lossy(), - cases: e.cases.iter().map(EnumCase::from).collect(), - }, - ScSpecEntry::UdtErrorEnumV0(e) => Entry::Enum { - doc: e.doc.to_utf8_string_lossy(), - name: e.name.to_utf8_string_lossy(), - cases: e.cases.iter().map(EnumCase::from).collect(), - }, - } - } -} diff --git a/cmd/crates/soroban-spec-tools/Cargo.toml b/cmd/crates/soroban-spec-tools/Cargo.toml deleted file mode 100644 index 61a32c13..00000000 --- a/cmd/crates/soroban-spec-tools/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "soroban-spec-tools" -description = "Tools for using a contract's XDR spec" -homepage = "https://github.com/stellar/soroban-tools" -repository = "https://github.com/stellar/soroban-tools" -authors = ["Stellar Development Foundation "] -license = "Apache-2.0" -readme = "README.md" -version.workspace = true -edition = "2021" -rust-version.workspace = true -autobins = false - - -[lib] -crate-type = ["rlib"] - - -[dependencies] -soroban-spec = { workspace = true } -stellar-strkey = { workspace = true } -stellar-xdr = { workspace = true, features = ["curr", "std", "serde"] } -serde_json = { workspace = true } -itertools = { workspace = true } -ethnum = { workspace = true } -hex = { workspace = true } -wasmparser = { workspace = true } -base64 = { workspace = true } -thiserror = "1.0.31" -# soroban-ledger-snapshot = { workspace = true } -# soroban-sdk = { workspace = true } -# sep5 = { workspace = true } - - -[dev-dependencies] -which = { workspace = true } -tokio = "1.28.1" diff --git a/cmd/crates/soroban-spec-tools/README.md b/cmd/crates/soroban-spec-tools/README.md deleted file mode 100644 index d2b00654..00000000 --- a/cmd/crates/soroban-spec-tools/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# soroban-spec-tools - -Tools and utilities for soroban specification / interface. diff --git a/cmd/crates/soroban-spec-tools/src/lib.rs b/cmd/crates/soroban-spec-tools/src/lib.rs deleted file mode 100644 index 7f310fd9..00000000 --- a/cmd/crates/soroban-spec-tools/src/lib.rs +++ /dev/null @@ -1,1468 +0,0 @@ -#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)] -use std::str::FromStr; - -use itertools::Itertools; -use serde_json::{json, Value}; -use stellar_xdr::curr::{ - AccountId, BytesM, ContractExecutable, Error as XdrError, Hash, Int128Parts, Int256Parts, - PublicKey, ScAddress, ScBytes, ScContractInstance, ScMap, ScMapEntry, ScNonceKey, ScSpecEntry, - ScSpecFunctionV0, ScSpecTypeDef as ScType, ScSpecTypeMap, ScSpecTypeOption, ScSpecTypeResult, - ScSpecTypeTuple, ScSpecTypeUdt, ScSpecTypeVec, ScSpecUdtEnumV0, ScSpecUdtErrorEnumCaseV0, - ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionCaseTupleV0, ScSpecUdtUnionCaseV0, - ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0, ScString, ScSymbol, ScVal, ScVec, StringM, - UInt128Parts, UInt256Parts, Uint256, VecM, -}; - -pub mod utils; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("an unknown error occurred")] - Unknown, - #[error("Invalid pair {0:#?} {1:#?}")] - InvalidPair(ScVal, ScType), - #[error("value is not parseable to {0:#?}")] - InvalidValue(Option), - #[error("Unknown case {0} for {1}")] - EnumCase(String, String), - #[error("Enum {0} missing value for type {1}")] - EnumMissingSecondValue(String, String), - #[error("Enum {0} is illformed")] - IllFormedEnum(String), - #[error("Unknown const case {0}")] - EnumConst(u32), - #[error("Enum const value must be a u32 or smaller")] - EnumConstTooLarge(u64), - #[error("Missing Entry {0}")] - MissingEntry(String), - #[error("Missing Spec")] - MissingSpec, - #[error(transparent)] - Xdr(XdrError), - #[error(transparent)] - Serde(#[from] serde_json::Error), - #[error(transparent)] - Ethnum(#[from] core::num::ParseIntError), - - #[error("Missing key {0} in map")] - MissingKey(String), - #[error("Failed to convert {0} to number")] - FailedNumConversion(serde_json::Number), - #[error("First argument in an enum must be a sybmol")] - EnumFirstValueNotSymbol, - #[error("Failed to find enum case {0}")] - FailedToFindEnumCase(String), - #[error(transparent)] - FailedSilceToByte(#[from] std::array::TryFromSliceError), - #[error(transparent)] - Infallible(#[from] std::convert::Infallible), - #[error("Missing Error case {0}")] - MissingErrorCase(u32), - #[error(transparent)] - Spec(#[from] soroban_spec::read::FromWasmError), - #[error(transparent)] - Base64Spec(#[from] soroban_spec::read::ParseSpecBase64Error), -} - -#[derive(Default, Clone)] -pub struct Spec(pub Option>); - -impl TryInto for &[u8] { - type Error = soroban_spec::read::FromWasmError; - - fn try_into(self) -> Result { - let spec = soroban_spec::read::from_wasm(self)?; - Ok(Spec::new(spec)) - } -} - -impl Spec { - pub fn new(entries: Vec) -> Self { - Self(Some(entries)) - } - - pub fn from_wasm(wasm: &[u8]) -> Result { - let spec = soroban_spec::read::from_wasm(wasm)?; - Ok(Spec::new(spec)) - } - - pub fn parse_base64(base64: &str) -> Result { - let spec = soroban_spec::read::parse_base64(base64.as_bytes())?; - Ok(Spec::new(spec)) - } -} - -impl Spec { - /// # Errors - /// Could fail to find User Defined Type - pub fn doc(&self, name: &str, type_: &ScType) -> Result, Error> { - let mut str = match type_ { - ScType::Val - | ScType::U64 - | ScType::I64 - | ScType::U128 - | ScType::I128 - | ScType::U32 - | ScType::I32 - | ScType::Result(_) - | ScType::Vec(_) - | ScType::Map(_) - | ScType::Tuple(_) - | ScType::BytesN(_) - | ScType::Symbol - | ScType::Error - | ScType::Bytes - | ScType::Void - | ScType::Timepoint - | ScType::Duration - | ScType::U256 - | ScType::I256 - | ScType::String - | ScType::Bool => String::new(), - ScType::Address => String::from( - "Can be public key (G13..), a contract hash (6c45307) or an identity (alice), ", - ), - ScType::Option(type_) => return self.doc(name, &type_.value_type), - ScType::Udt(ScSpecTypeUdt { name }) => { - let spec_type = self.find(&name.to_utf8_string_lossy())?; - match spec_type { - ScSpecEntry::FunctionV0(ScSpecFunctionV0 { doc, .. }) - | ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { doc, .. }) - | ScSpecEntry::UdtUnionV0(ScSpecUdtUnionV0 { doc, .. }) - | ScSpecEntry::UdtEnumV0(ScSpecUdtEnumV0 { doc, .. }) - | ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { doc, .. }) => doc, - } - .to_utf8_string_lossy() - } - }; - if let Some(mut ex) = self.example(type_) { - if ex.contains(' ') { - ex = format!("'{ex}'"); - } else if ex.contains('"') { - ex = ex.replace('"', ""); - } - if matches!(type_, ScType::Bool) { - ex = String::new(); - } - let sep = if str.is_empty() { "" } else { "\n" }; - str = format!("{str}{sep}Example:\n --{name} {ex}"); - if ex.contains('"') {} - } - if str.is_empty() { - Ok(None) - } else { - Ok(Some(Box::leak(str.into_boxed_str()))) - } - } - - /// # Errors - /// - /// Might return errors - pub fn find(&self, name: &str) -> Result<&ScSpecEntry, Error> { - self.0 - .as_ref() - .and_then(|specs| { - specs.iter().find(|e| { - let entry_name = match e { - ScSpecEntry::FunctionV0(x) => x.name.to_utf8_string_lossy(), - ScSpecEntry::UdtStructV0(x) => x.name.to_utf8_string_lossy(), - ScSpecEntry::UdtUnionV0(x) => x.name.to_utf8_string_lossy(), - ScSpecEntry::UdtEnumV0(x) => x.name.to_utf8_string_lossy(), - ScSpecEntry::UdtErrorEnumV0(x) => x.name.to_utf8_string_lossy(), - }; - name == entry_name - }) - }) - .ok_or_else(|| Error::MissingEntry(name.to_owned())) - } - - /// # Errors - /// - /// Might return errors - pub fn find_function(&self, name: &str) -> Result<&ScSpecFunctionV0, Error> { - match self.find(name)? { - ScSpecEntry::FunctionV0(f) => Ok(f), - _ => Err(Error::MissingEntry(name.to_owned())), - } - } - // - /// # Errors - /// - pub fn find_functions(&self) -> Result, Error> { - Ok(self - .0 - .as_ref() - .ok_or(Error::MissingSpec)? - .iter() - .filter_map(|e| match e { - ScSpecEntry::FunctionV0(x) => Some(x), - _ => None, - })) - } - - /// # Errors - /// - pub fn find_error_type(&self, value: u32) -> Result<&ScSpecUdtErrorEnumCaseV0, Error> { - if let ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { cases, .. }) = - self.find("Error")? - { - if let Some(case) = cases.iter().find(|case| value == case.value) { - return Ok(case); - } - } - Err(Error::MissingErrorCase(value)) - } - - /// # Errors - /// - /// Might return errors - pub fn from_string_primitive(s: &str, t: &ScType) -> Result { - Self::default().from_string(s, t) - } - - /// # Errors - /// - /// Might return errors - #[allow(clippy::wrong_self_convention)] - pub fn from_string(&self, s: &str, t: &ScType) -> Result { - if let ScType::Option(b) = t { - if s == "null" { - return Ok(ScVal::Void); - } - let ScSpecTypeOption { value_type } = b.as_ref().clone(); - let v = value_type.as_ref().clone(); - return self.from_string(s, &v); - } - // Parse as string and for special types assume Value::String - serde_json::from_str(s) - .map_or_else( - |e| match t { - ScType::Symbol - | ScType::String - | ScType::Bytes - | ScType::BytesN(_) - | ScType::U256 - | ScType::I256 - | ScType::U128 - | ScType::I128 - | ScType::Address => Ok(Value::String(s.to_owned())), - ScType::Udt(ScSpecTypeUdt { name }) - if matches!( - self.find(&name.to_utf8_string_lossy())?, - ScSpecEntry::UdtUnionV0(_) | ScSpecEntry::UdtStructV0(_) - ) => - { - Ok(Value::String(s.to_owned())) - } - _ => Err(Error::Serde(e)), - }, - |val| match t { - ScType::U128 | ScType::I128 | ScType::U256 | ScType::I256 => { - Ok(Value::String(s.to_owned())) - } - _ => Ok(val), - }, - ) - .and_then(|raw| self.from_json(&raw, t)) - } - - /// # Errors - /// - /// Might return errors - #[allow(clippy::wrong_self_convention)] - pub fn from_json(&self, v: &Value, t: &ScType) -> Result { - let val: ScVal = match (t, v) { - ( - ScType::Bool - | ScType::U128 - | ScType::I128 - | ScType::U256 - | ScType::I256 - | ScType::I32 - | ScType::I64 - | ScType::U32 - | ScType::U64 - | ScType::String - | ScType::Symbol - | ScType::Address - | ScType::Bytes - | ScType::BytesN(_), - _, - ) => from_json_primitives(v, t)?, - - // Vec parsing - (ScType::Vec(elem), Value::Array(raw)) => { - let converted: ScVec = raw - .iter() - .map(|item| self.from_json(item, &elem.element_type)) - .collect::, Error>>()? - .try_into() - .map_err(Error::Xdr)?; - ScVal::Vec(Some(converted)) - } - - // Map parsing - (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?, - - // Option parsing - (ScType::Option(_), Value::Null) => ScVal::Void, - (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?, - - // Tuple parsing - (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?, - - // User defined types parsing - (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?, - - // TODO: Implement the rest of these - (_, raw) => serde_json::from_value(raw.clone()).map_err(Error::Serde)?, - }; - Ok(val) - } - - fn parse_udt(&self, name: &StringM<60>, value: &Value) -> Result { - let name = &name.to_utf8_string_lossy(); - match (self.find(name)?, value) { - (ScSpecEntry::UdtStructV0(strukt), Value::Object(map)) => { - if strukt - .fields - .iter() - .any(|f| f.name.to_utf8_string_lossy() == "0") - { - self.parse_tuple_strukt( - strukt, - &(0..map.len()) - .map(|i| map.get(&i.to_string()).unwrap().clone()) - .collect::>(), - ) - } else { - self.parse_strukt(strukt, map) - } - } - (ScSpecEntry::UdtStructV0(strukt), Value::Array(arr)) => { - self.parse_tuple_strukt(strukt, arr) - } - ( - ScSpecEntry::UdtUnionV0(union), - val @ (Value::Array(_) | Value::String(_) | Value::Object(_)), - ) => self.parse_union(union, val), - (ScSpecEntry::UdtEnumV0(enum_), Value::Number(num)) => parse_const_enum(num, enum_), - (s, v) => todo!("Not implemented for {s:#?} {v:#?}"), - } - } - - fn parse_tuple_strukt( - &self, - strukt: &ScSpecUdtStructV0, - array: &[Value], - ) -> Result { - let items = strukt - .fields - .to_vec() - .iter() - .zip(array.iter()) - .map(|(f, v)| { - let val = self.from_json(v, &f.type_)?; - Ok(val) - }) - .collect::, Error>>()?; - Ok(ScVal::Vec(Some(items.try_into().map_err(Error::Xdr)?))) - } - - fn parse_strukt( - &self, - strukt: &ScSpecUdtStructV0, - map: &serde_json::Map, - ) -> Result { - let items = strukt - .fields - .to_vec() - .iter() - .map(|f| { - let name = &f.name.to_utf8_string_lossy(); - let v = map - .get(name) - .ok_or_else(|| Error::MissingKey(name.clone()))?; - let val = self.from_json(v, &f.type_)?; - let key = StringM::from_str(name).unwrap(); - Ok(ScMapEntry { - key: ScVal::Symbol(key.into()), - val, - }) - }) - .collect::, Error>>()?; - let map = ScMap::sorted_from(items).map_err(Error::Xdr)?; - Ok(ScVal::Map(Some(map))) - } - - fn parse_union(&self, union: &ScSpecUdtUnionV0, value: &Value) -> Result { - let (enum_case, rest) = match value { - Value::String(s) => (s, None), - Value::Object(o) if o.len() == 1 => { - let res = o.values().next().map(|v| match v { - Value::Object(obj) if obj.contains_key("0") => { - let len = obj.len(); - Value::Array( - (0..len) - .map(|i| obj.get(&i.to_string()).unwrap().clone()) - .collect::>(), - ) - } - _ => v.clone(), - }); - (o.keys().next().unwrap(), res) - } - _ => todo!(), - }; - let case = union - .cases - .iter() - .find(|c| { - let name = match c { - ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name, - ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name, - }; - enum_case == &name.to_utf8_string_lossy() - }) - .ok_or_else(|| { - Error::EnumCase(enum_case.to_string(), union.name.to_utf8_string_lossy()) - })?; - - let mut res = vec![ScVal::Symbol(ScSymbol( - enum_case.try_into().map_err(Error::Xdr)?, - ))]; - - match (case, rest) { - (ScSpecUdtUnionCaseV0::VoidV0(_), _) | (ScSpecUdtUnionCaseV0::TupleV0(_), None) => (), - (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), Some(arr)) - if type_.len() == 1 => - { - res.push(self.from_json(&arr, &type_[0])?); - } - ( - ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), - Some(Value::Array(arr)), - ) => { - res.extend( - arr.iter() - .zip(type_.iter()) - .map(|(elem, ty)| self.from_json(elem, ty)) - .collect::, _>>()?, - ); - } - (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { .. }), Some(_)) => {} - }; - Ok(ScVal::Vec(Some(res.try_into().map_err(Error::Xdr)?))) - } - - fn parse_tuple( - &self, - t: &ScType, - tuple: &ScSpecTypeTuple, - items: &[Value], - ) -> Result { - let ScSpecTypeTuple { value_types } = tuple; - if items.len() != value_types.len() { - return Err(Error::InvalidValue(Some(t.clone()))); - }; - let parsed: Result, Error> = items - .iter() - .zip(value_types.iter()) - .map(|(item, t)| self.from_json(item, t)) - .collect(); - let converted: ScVec = parsed?.try_into().map_err(Error::Xdr)?; - Ok(ScVal::Vec(Some(converted))) - } - - fn parse_map( - &self, - map: &ScSpecTypeMap, - value_map: &serde_json::Map, - ) -> Result { - let ScSpecTypeMap { - key_type, - value_type, - } = map; - // TODO: What do we do if the expected key_type is not a string or symbol? - let parsed: Result, Error> = value_map - .iter() - .map(|(k, v)| -> Result { - let key = self.from_string(k, key_type)?; - let val = self.from_json(v, value_type)?; - Ok(ScMapEntry { key, val }) - }) - .collect(); - Ok(ScVal::Map(Some( - ScMap::sorted_from(parsed?).map_err(Error::Xdr)?, - ))) - } -} - -impl Spec { - /// # Errors - /// - /// Might return `Error::InvalidValue` - /// - /// # Panics - /// - /// May panic - pub fn xdr_to_json(&self, val: &ScVal, output: &ScType) -> Result { - Ok(match (val, output) { - (ScVal::Void, ScType::Val | ScType::Option(_) | ScType::Tuple(_)) - | (ScVal::Map(None) | ScVal::Vec(None), ScType::Option(_)) => Value::Null, - (ScVal::Bool(_), ScType::Bool) - | (ScVal::Void, ScType::Void) - | (ScVal::String(_), ScType::String) - | (ScVal::Symbol(_), ScType::Symbol) - | (ScVal::U64(_), ScType::U64) - | (ScVal::I64(_), ScType::I64) - | (ScVal::U32(_), ScType::U32) - | (ScVal::I32(_), ScType::I32) - | (ScVal::U128(_), ScType::U128) - | (ScVal::I128(_), ScType::I128) - | (ScVal::U256(_), ScType::U256) - | (ScVal::I256(_), ScType::I256) - | (ScVal::Duration(_), ScType::Duration) - | (ScVal::Timepoint(_), ScType::Timepoint) - | ( - ScVal::ContractInstance(_) - | ScVal::LedgerKeyContractInstance - | ScVal::LedgerKeyNonce(_), - _, - ) - | (ScVal::Address(_), ScType::Address) - | (ScVal::Bytes(_), ScType::Bytes | ScType::BytesN(_)) => to_json(val)?, - - (val, ScType::Result(inner)) => self.xdr_to_json(val, &inner.ok_type)?, - - (val, ScType::Option(inner)) => self.xdr_to_json(val, &inner.value_type)?, - (ScVal::Map(Some(_)) | ScVal::Vec(Some(_)) | ScVal::U32(_), type_) => { - self.sc_object_to_json(val, type_)? - } - - (ScVal::Error(_), ScType::Error) => todo!(), - (v, typed) => todo!("{v:#?} doesn't have a matching {typed:#?}"), - }) - } - - /// # Errors - /// - /// Might return an error - pub fn vec_m_to_json( - &self, - vec_m: &VecM, - type_: &ScType, - ) -> Result { - Ok(Value::Array( - vec_m - .to_vec() - .iter() - .map(|sc_val| self.xdr_to_json(sc_val, type_)) - .collect::, Error>>()?, - )) - } - - /// # Errors - /// - /// Might return an error - pub fn sc_map_to_json(&self, sc_map: &ScMap, type_: &ScSpecTypeMap) -> Result { - let v = sc_map - .iter() - .map(|ScMapEntry { key, val }| { - let key_s = self.xdr_to_json(key, &type_.key_type)?.to_string(); - let val_value = self.xdr_to_json(val, &type_.value_type)?; - Ok((key_s, val_value)) - }) - .collect::, Error>>()?; - Ok(Value::Object(v)) - } - - /// # Errors - /// - /// Might return an error - /// - /// # Panics - /// - /// May panic - pub fn udt_to_json(&self, name: &StringM<60>, sc_obj: &ScVal) -> Result { - let name = &name.to_utf8_string_lossy(); - let udt = self.find(name)?; - Ok(match (sc_obj, udt) { - (ScVal::Map(Some(map)), ScSpecEntry::UdtStructV0(strukt)) => serde_json::Value::Object( - strukt - .fields - .iter() - .zip(map.iter()) - .map(|(field, entry)| { - let val = self.xdr_to_json(&entry.val, &field.type_)?; - Ok((field.name.to_utf8_string_lossy(), val)) - }) - .collect::, Error>>()?, - ), - (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtStructV0(strukt)) => Value::Array( - strukt - .fields - .iter() - .zip(vec_.iter()) - .map(|(field, entry)| self.xdr_to_json(entry, &field.type_)) - .collect::, Error>>()?, - ), - (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtUnionV0(union)) => { - let v = vec_.to_vec(); - // let val = &v[0]; - let (first, rest) = match v.split_at(1) { - ([first], []) => (first, None), - ([first], rest) => (first, Some(rest)), - _ => return Err(Error::IllFormedEnum(union.name.to_utf8_string_lossy())), - }; - - let ScVal::Symbol(case_name) = first else { - return Err(Error::EnumFirstValueNotSymbol); - }; - let case = union - .cases - .iter() - .find(|case| { - let name = match case { - ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name, - ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name, - }; - name.as_vec() == case_name.as_vec() - }) - .ok_or_else(|| Error::FailedToFindEnumCase(case_name.to_utf8_string_lossy()))?; - - let case_name = case_name.to_utf8_string_lossy(); - match case { - ScSpecUdtUnionCaseV0::TupleV0(v) => { - let rest = rest.ok_or_else(|| { - Error::EnumMissingSecondValue( - union.name.to_utf8_string_lossy(), - case_name.clone(), - ) - })?; - let val = if v.type_.len() == 1 { - self.xdr_to_json(&rest[0], &v.type_[0])? - } else { - Value::Array( - v.type_ - .iter() - .zip(rest.iter()) - .map(|(type_, val)| self.xdr_to_json(val, type_)) - .collect::, Error>>()?, - ) - }; - - Value::Object([(case_name, val)].into_iter().collect()) - } - ScSpecUdtUnionCaseV0::VoidV0(_) => Value::String(case_name), - } - } - (ScVal::U32(v), ScSpecEntry::UdtEnumV0(_enum_)) => { - Value::Number(serde_json::Number::from(*v)) - } - (s, v) => todo!("Not implemented for {s:#?} {v:#?}"), - }) - } - - /// # Errors - /// - /// Might return an error - /// - /// # Panics - /// - /// Some types are not yet supported and will cause a panic if supplied - pub fn sc_object_to_json(&self, val: &ScVal, spec_type: &ScType) -> Result { - Ok(match (val, spec_type) { - (ScVal::Vec(Some(ScVec(vec_m))), ScType::Vec(type_)) => { - self.vec_m_to_json(vec_m, &type_.element_type)? - } - (ScVal::Vec(Some(ScVec(vec_m))), ScType::Tuple(tuple_type)) => Value::Array( - vec_m - .iter() - .zip(tuple_type.value_types.iter()) - .map(|(v, t)| self.xdr_to_json(v, t)) - .collect::, _>>()?, - ), - ( - sc_obj @ (ScVal::Vec(_) | ScVal::Map(_) | ScVal::U32(_)), - ScType::Udt(ScSpecTypeUdt { name }), - ) => self.udt_to_json(name, sc_obj)?, - - (ScVal::Map(Some(map)), ScType::Map(map_type)) => self.sc_map_to_json(map, map_type)?, - - (ScVal::U64(u64_), ScType::U64) => Value::Number(serde_json::Number::from(*u64_)), - - (ScVal::I64(i64_), ScType::I64) => Value::Number(serde_json::Number::from(*i64_)), - (int @ ScVal::U128(_), ScType::U128) => { - // Always output u128s as strings - let v: u128 = int - .clone() - .try_into() - .map_err(|()| Error::InvalidValue(Some(ScType::U128)))?; - Value::String(v.to_string()) - } - - (int @ ScVal::I128(_), ScType::I128) => { - // Always output u128s as strings - let v: i128 = int - .clone() - .try_into() - .map_err(|()| Error::InvalidValue(Some(ScType::I128)))?; - Value::String(v.to_string()) - } - - (ScVal::Bytes(v), ScType::Bytes | ScType::BytesN(_)) => { - Value::String(to_lower_hex(v.as_slice())) - } - - (ScVal::Bytes(_), ScType::Udt(_)) => todo!(), - - (ScVal::ContractInstance(_), _) => todo!(), - - (ScVal::Address(v), ScType::Address) => sc_address_to_json(v), - - (ok_val, ScType::Result(result_type)) => { - let ScSpecTypeResult { ok_type, .. } = result_type.as_ref(); - self.xdr_to_json(ok_val, ok_type)? - } - - (x, y) => return Err(Error::InvalidPair(x.clone(), y.clone())), - }) - } -} - -/// # Errors -/// -/// Might return an error -pub fn from_string_primitive(s: &str, t: &ScType) -> Result { - Spec::from_string_primitive(s, t) -} - -fn parse_const_enum(num: &serde_json::Number, enum_: &ScSpecUdtEnumV0) -> Result { - let num = num - .as_u64() - .ok_or_else(|| Error::FailedNumConversion(num.clone()))?; - let num = u32::try_from(num).map_err(|_| Error::EnumConstTooLarge(num))?; - enum_ - .cases - .iter() - .find(|c| c.value == num) - .ok_or(Error::EnumConst(num)) - .map(|c| ScVal::U32(c.value)) -} - -/// # Errors -/// -/// Might return an error -#[allow(clippy::too_many_lines)] -pub fn from_json_primitives(v: &Value, t: &ScType) -> Result { - let val: ScVal = match (t, v) { - // Boolean parsing - (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true), - (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false), - - // Number parsing - (ScType::U128, Value::String(s)) => { - let val: u128 = u128::from_str(s) - .map(Into::into) - .map_err(|_| Error::InvalidValue(Some(t.clone())))?; - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - ScVal::U128(UInt128Parts { - hi: u64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) - } - - (ScType::I128, Value::String(s)) => { - let val: i128 = i128::from_str(s) - .map(Into::into) - .map_err(|_| Error::InvalidValue(Some(t.clone())))?; - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - ScVal::I128(Int128Parts { - hi: i64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) - } - - // Number parsing - (ScType::U256, Value::String(s)) => { - let (hi, lo) = ethnum::U256::from_str_prefixed(s)?.into_words(); - let hi_bytes = hi.to_be_bytes(); - let (hi_hi, hi_lo) = hi_bytes.split_at(8); - let lo_bytes = lo.to_be_bytes(); - let (lo_hi, lo_lo) = lo_bytes.split_at(8); - ScVal::U256(UInt256Parts { - hi_hi: u64::from_be_bytes(hi_hi.try_into()?), - hi_lo: u64::from_be_bytes(hi_lo.try_into()?), - lo_hi: u64::from_be_bytes(lo_hi.try_into()?), - lo_lo: u64::from_be_bytes(lo_lo.try_into()?), - }) - } - (ScType::I256, Value::String(s)) => { - let (hi, lo) = ethnum::I256::from_str_prefixed(s)?.into_words(); - let hi_bytes = hi.to_be_bytes(); - let (hi_hi, hi_lo) = hi_bytes.split_at(8); - let lo_bytes = lo.to_be_bytes(); - let (lo_hi, lo_lo) = lo_bytes.split_at(8); - ScVal::I256(Int256Parts { - hi_hi: i64::from_be_bytes(hi_hi.try_into()?), - hi_lo: u64::from_be_bytes(hi_lo.try_into()?), - lo_hi: u64::from_be_bytes(lo_hi.try_into()?), - lo_lo: u64::from_be_bytes(lo_lo.try_into()?), - }) - } - - (ScType::I32, Value::Number(n)) => ScVal::I32( - n.as_i64() - .ok_or_else(|| Error::InvalidValue(Some(t.clone())))? - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))?, - ), - (ScType::U32, Value::Number(n)) => ScVal::U32( - n.as_u64() - .ok_or_else(|| Error::InvalidValue(Some(t.clone())))? - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))?, - ), - (ScType::I64, Value::Number(n)) => ScVal::I64( - n.as_i64() - .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?, - ), - (ScType::U64 | ScType::Timepoint | ScType::Duration, Value::Number(n)) => ScVal::U64( - n.as_u64() - .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?, - ), - - // Symbol parsing - (ScType::Symbol, Value::String(s)) => ScVal::Symbol(ScSymbol( - s.as_bytes() - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))?, - )), - - (ScType::Address, Value::String(s)) => sc_address_from_json(s)?, - - // Bytes parsing - (bytes @ ScType::BytesN(_), Value::Number(n)) => { - from_json_primitives(&Value::String(format!("{n}")), bytes)? - } - (ScType::BytesN(bytes), Value::String(s)) => ScVal::Bytes(ScBytes({ - if bytes.n == 32 { - // Bytes might be a strkey, try parsing it as one. Contract devs should use the new - // proper Address type, but for backwards compatibility some contracts might use a - // BytesN<32> to represent an Address. - if let Ok(key) = sc_address_from_json(s) { - return Ok(key); - } - } - // Bytes are not an address, just parse as a hex string - utils::padded_hex_from_str(s, bytes.n as usize) - .map_err(|_| Error::InvalidValue(Some(t.clone())))? - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))? - })), - (ScType::Bytes, Value::Number(n)) => { - from_json_primitives(&Value::String(format!("{n}")), &ScType::Bytes)? - } - (ScType::Bytes, Value::String(s)) => ScVal::Bytes( - hex::decode(s) - .map_err(|_| Error::InvalidValue(Some(t.clone())))? - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))?, - ), - (ScType::Bytes | ScType::BytesN(_), Value::Array(raw)) => { - let b: Result, Error> = raw - .iter() - .map(|item| { - item.as_u64() - .ok_or_else(|| Error::InvalidValue(Some(t.clone())))? - .try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone()))) - }) - .collect(); - let converted: BytesM<{ u32::MAX }> = b?.try_into().map_err(Error::Xdr)?; - ScVal::Bytes(ScBytes(converted)) - } - - (ScType::String, Value::String(s)) => ScVal::String(ScString( - s.try_into() - .map_err(|_| Error::InvalidValue(Some(t.clone())))?, - )), - // Todo make proper error Which shouldn't exist - (_, raw) => serde_json::from_value(raw.clone())?, - }; - Ok(val) -} - -/// # Errors -/// -/// Might return an error -pub fn to_string(v: &ScVal) -> Result { - #[allow(clippy::match_same_arms)] - Ok(match v { - // If symbols are a top-level thing we omit the wrapping quotes - // TODO: Decide if this is a good idea or not. - ScVal::Symbol(v) => std::str::from_utf8(v.as_slice()) - .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))? - .to_string(), - ScVal::LedgerKeyContractInstance => "LedgerKeyContractInstance".to_string(), - _ => serde_json::to_string(&to_json(v)?)?, - }) -} - -/// # Errors -/// -/// Might return an error -#[allow(clippy::too_many_lines)] -pub fn to_json(v: &ScVal) -> Result { - #[allow(clippy::match_same_arms)] - let val: Value = match v { - ScVal::Bool(b) => Value::Bool(*b), - ScVal::Void => Value::Null, - ScVal::LedgerKeyContractInstance => Value::String("LedgerKeyContractInstance".to_string()), - ScVal::U64(v) => Value::Number(serde_json::Number::from(*v)), - ScVal::Timepoint(tp) => Value::Number(serde_json::Number::from(tp.0)), - ScVal::Duration(d) => Value::Number(serde_json::Number::from(d.0)), - ScVal::I64(v) => Value::Number(serde_json::Number::from(*v)), - ScVal::U32(v) => Value::Number(serde_json::Number::from(*v)), - ScVal::I32(v) => Value::Number(serde_json::Number::from(*v)), - ScVal::Symbol(v) => Value::String( - std::str::from_utf8(v.as_slice()) - .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))? - .to_string(), - ), - ScVal::String(v) => Value::String( - std::str::from_utf8(v.as_slice()) - .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))? - .to_string(), - ), - ScVal::Vec(v) => { - let values: Result, Error> = v.as_ref().map_or_else( - || Ok(vec![]), - |v| { - v.iter() - .map(|item| -> Result { to_json(item) }) - .collect() - }, - ); - Value::Array(values?) - } - ScVal::Map(None) => Value::Object(serde_json::Map::with_capacity(0)), - ScVal::Map(Some(v)) => { - // TODO: What do we do if the key is not a string? - let mut m = serde_json::Map::::with_capacity(v.len()); - for ScMapEntry { key, val } in v.iter() { - let k: String = to_string(key)?; - let v: Value = to_json(val).map_err(|_| Error::InvalidValue(None))?; - m.insert(k, v); - } - Value::Object(m) - } - ScVal::Bytes(v) => Value::String(to_lower_hex(v.as_slice())), - ScVal::Address(v) => sc_address_to_json(v), - ScVal::U128(n) => { - let hi: [u8; 8] = n.hi.to_be_bytes(); - let lo: [u8; 8] = n.lo.to_be_bytes(); - let bytes = [hi, lo].concat(); - // Always output u128s as strings - let v = u128::from_be_bytes( - bytes - .as_slice() - .try_into() - .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?, - ) - .to_string(); - Value::String(v) - } - ScVal::I128(n) => { - let hi: [u8; 8] = n.hi.to_be_bytes(); - let lo: [u8; 8] = n.lo.to_be_bytes(); - let bytes = [hi, lo].concat(); - // Always output u128s as strings - let v = i128::from_be_bytes( - bytes - .as_slice() - .try_into() - .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?, - ) - .to_string(); - Value::String(v) - } - ScVal::U256(u256parts) => { - let bytes = [ - u256parts.hi_hi.to_be_bytes(), - u256parts.hi_lo.to_be_bytes(), - u256parts.lo_hi.to_be_bytes(), - u256parts.lo_lo.to_be_bytes(), - ] - .concat(); - let u256 = ethnum::U256::from_be_bytes( - bytes - .as_slice() - .try_into() - .map_err(|_| Error::InvalidValue(Some(ScType::U256)))?, - ); - Value::String(u256.to_string()) - } - ScVal::I256(i256parts) => { - let bytes = [ - i256parts.hi_hi.to_be_bytes(), - i256parts.hi_lo.to_be_bytes(), - i256parts.lo_hi.to_be_bytes(), - i256parts.lo_lo.to_be_bytes(), - ] - .concat(); - let i256 = ethnum::I256::from_be_bytes( - bytes - .as_slice() - .try_into() - .map_err(|_| Error::InvalidValue(Some(ScType::I256)))?, - ); - Value::String(i256.to_string()) - } - ScVal::ContractInstance(ScContractInstance { - executable: ContractExecutable::Wasm(hash), - .. - }) => json!({ "hash": hash }), - ScVal::ContractInstance(ScContractInstance { - executable: ContractExecutable::StellarAsset, - .. - }) => json!({"SAC": true}), - ScVal::LedgerKeyNonce(ScNonceKey { nonce }) => { - Value::Number(serde_json::Number::from(*nonce)) - } - ScVal::Error(e) => serde_json::to_value(e)?, - }; - Ok(val) -} - -fn sc_address_to_json(v: &ScAddress) -> Value { - match v { - ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => { - Value::String(stellar_strkey::ed25519::PublicKey(*k).to_string()) - } - ScAddress::Contract(Hash(h)) => Value::String(stellar_strkey::Contract(*h).to_string()), - } -} - -fn sc_address_from_json(s: &str) -> Result { - stellar_strkey::Strkey::from_string(s) - .map_err(|_| Error::InvalidValue(Some(ScType::Address))) - .map(|parsed| match parsed { - stellar_strkey::Strkey::PublicKeyEd25519(p) => Some(ScVal::Address( - ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(p.0)))), - )), - stellar_strkey::Strkey::Contract(c) => { - Some(ScVal::Address(ScAddress::Contract(Hash(c.0)))) - } - _ => None, - })? - .ok_or(Error::InvalidValue(Some(ScType::Address))) -} - -fn to_lower_hex(bytes: &[u8]) -> String { - let mut res = String::with_capacity(bytes.len()); - for b in bytes { - res.push_str(&format!("{b:02x}")); - } - res -} - -impl Spec { - #[must_use] - pub fn arg_value_name(&self, type_: &ScType, depth: usize) -> Option { - match type_ { - ScType::U64 => Some("u64".to_string()), - ScType::I64 => Some("i64".to_string()), - ScType::U128 => Some("u128".to_string()), - ScType::I128 => Some("i128".to_string()), - ScType::U32 => Some("u32".to_string()), - ScType::I32 => Some("i32".to_string()), - ScType::Bool => Some("bool".to_string()), - ScType::Symbol => Some("Symbol".to_string()), - ScType::Error => Some("Error".to_string()), - ScType::Bytes => Some("hex_bytes".to_string()), - ScType::Address => Some("Address".to_string()), - ScType::Void => Some("Null".to_string()), - ScType::Timepoint => Some("Timepoint".to_string()), - ScType::Duration => Some("Duration".to_string()), - ScType::U256 => Some("u256".to_string()), - ScType::I256 => Some("i256".to_string()), - ScType::String => Some("String".to_string()), - ScType::Option(val) => { - let ScSpecTypeOption { value_type } = val.as_ref(); - let inner = self.arg_value_name(value_type.as_ref(), depth + 1)?; - Some(format!("Option<{inner}>")) - } - ScType::Vec(val) => { - let ScSpecTypeVec { element_type } = val.as_ref(); - let inner = self.arg_value_name(element_type.as_ref(), depth + 1)?; - Some(format!("Array<{inner}>")) - } - ScType::Result(val) => { - let ScSpecTypeResult { - ok_type, - error_type, - } = val.as_ref(); - let ok = self.arg_value_name(ok_type.as_ref(), depth + 1)?; - let error = self.arg_value_name(error_type.as_ref(), depth + 1)?; - Some(format!("Result<{ok}, {error}>")) - } - ScType::Tuple(val) => { - let ScSpecTypeTuple { value_types } = val.as_ref(); - let names = value_types - .iter() - .map(|t| self.arg_value_name(t, depth + 1)) - .collect::>>()? - .join(", "); - Some(format!("Tuple<{names}>")) - } - ScType::Map(val) => { - let ScSpecTypeMap { - key_type, - value_type, - } = val.as_ref(); - let (key, val) = ( - self.arg_value_name(key_type.as_ref(), depth + 1)?, - self.arg_value_name(value_type.as_ref(), depth + 1)?, - ); - Some(format!("Map<{key}, {val}>")) - } - ScType::BytesN(t) => Some(format!("{}_hex_bytes", t.n)), - ScType::Udt(ScSpecTypeUdt { name }) => { - match self.find(&name.to_utf8_string_lossy()).ok()? { - ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. }) - if fields - .first() - .map(|f| f.name.to_utf8_string_lossy() == "0") - .unwrap_or_default() => - { - let fields = fields - .iter() - .map(|t| self.arg_value_name(&t.type_, depth + 1)) - .collect::>>()? - .join(", "); - Some(format!("[{fields}]")) - } - ScSpecEntry::UdtStructV0(strukt) => self.arg_value_udt(strukt, depth), - ScSpecEntry::UdtUnionV0(union) => self.arg_value_union(union, depth), - ScSpecEntry::UdtEnumV0(enum_) => Some(arg_value_enum(enum_)), - ScSpecEntry::FunctionV0(_) | ScSpecEntry::UdtErrorEnumV0(_) => None, - } - } - // No specific value name for these yet. - ScType::Val => None, - } - } - - fn arg_value_udt(&self, strukt: &ScSpecUdtStructV0, depth: usize) -> Option { - let inner = strukt - .fields - .iter() - .map(|f| (f.name.to_utf8_string_lossy(), &f.type_)) - .map(|(name, type_)| { - let type_ = self.arg_value_name(type_, depth + 1)?; - Some(format!("{name}: {type_}")) - }) - .collect::>>()? - .join(", "); - Some(format!("{{ {inner} }}")) - } - - fn arg_value_union(&self, union: &ScSpecUdtUnionV0, depth: usize) -> Option { - union - .cases - .iter() - .map(|f| { - Some(match f { - ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => { - name.to_utf8_string_lossy() - } - ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { - name, - type_, - .. - }) => format!( - "{}({})", - name.to_utf8_string_lossy(), - type_ - .iter() - .map(|type_| self.arg_value_name(type_, depth + 1)) - .collect::>>()? - .join(",") - ), - }) - }) - .collect::>>() - .map(|v| v.join(" | ")) - } -} - -fn arg_value_enum(enum_: &ScSpecUdtEnumV0) -> String { - enum_ - .cases - .iter() - .map(|case| case.value.to_string()) - .join(" | ") -} - -// Example implementation -impl Spec { - #[must_use] - pub fn example(&self, type_: &ScType) -> Option { - match type_ { - ScType::U64 => Some("42".to_string()), - ScType::I64 => Some("-42".to_string()), - ScType::U128 => Some("\"1000\"".to_string()), - ScType::I128 => Some("\"-100\"".to_string()), - ScType::U32 => Some("1".to_string()), - ScType::I32 => Some("-1".to_string()), - ScType::Bool => Some("true".to_string()), - ScType::Symbol => Some("\"hello\"".to_string()), - ScType::Error => Some("Error".to_string()), - ScType::Bytes => Some("\"beefface123\"".to_string()), - ScType::Address => { - Some("\"GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4\"".to_string()) - } - ScType::Void => Some("null".to_string()), - ScType::Timepoint => Some("1234".to_string()), - ScType::Duration => Some("9999".to_string()), - ScType::U256 => Some("\"2000\"".to_string()), - ScType::I256 => Some("\"-20000\"".to_string()), - ScType::String => Some("\"hello world\"".to_string()), - ScType::Option(val) => { - let ScSpecTypeOption { value_type } = val.as_ref(); - self.example(value_type.as_ref()) - } - ScType::Vec(val) => { - let ScSpecTypeVec { element_type } = val.as_ref(); - let inner = self.example(element_type.as_ref())?; - Some(format!("[ {inner} ]")) - } - ScType::Result(val) => { - let ScSpecTypeResult { - ok_type, - error_type, - } = val.as_ref(); - let ok = self.example(ok_type.as_ref())?; - let error = self.example(error_type.as_ref())?; - Some(format!("Result<{ok}, {error}>")) - } - ScType::Tuple(val) => { - let ScSpecTypeTuple { value_types } = val.as_ref(); - let names = value_types - .iter() - .map(|t| self.example(t)) - .collect::>>()? - .join(", "); - Some(format!("[{names}]")) - } - ScType::Map(map) => { - let ScSpecTypeMap { - key_type, - value_type, - } = map.as_ref(); - let (mut key, val) = ( - self.example(key_type.as_ref())?, - self.example(value_type.as_ref())?, - ); - if !matches!(key_type.as_ref(), ScType::Symbol) { - key = format!("\"{key}\""); - } - Some(format!("{{ {key}: {val} }}")) - } - ScType::BytesN(n) => { - let n = n.n as usize; - let res = if n % 2 == 0 { - "ef".repeat(n) - } else { - let mut s = "ef".repeat(n - 1); - s.push('e'); - s - }; - Some(format!("\"{res}\"")) - } - ScType::Udt(ScSpecTypeUdt { name }) => { - self.example_udts(name.to_utf8_string_lossy().as_ref()) - } - // No specific value name for these yet. - ScType::Val => None, - } - } - - fn example_udts(&self, name: &str) -> Option { - match self.find(name).ok() { - Some(ScSpecEntry::UdtStructV0(strukt)) => { - // Check if a tuple strukt - if !strukt.fields.is_empty() && strukt.fields[0].name.to_utf8_string_lossy() == "0" - { - let value_types = strukt - .fields - .iter() - .map(|f| f.type_.clone()) - .collect::>() - .try_into() - .ok()?; - return self.example(&ScType::Tuple(Box::new(ScSpecTypeTuple { value_types }))); - } - let inner = strukt - .fields - .iter() - .map(|f| (f.name.to_utf8_string_lossy(), &f.type_)) - .map(|(name, type_)| { - let type_ = self.example(type_)?; - let name = format!(r#""{name}""#); - Some(format!("{name}: {type_}")) - }) - .collect::>>()? - .join(", "); - Some(format!(r#"{{ {inner} }}"#)) - } - Some(ScSpecEntry::UdtUnionV0(union)) => self.example_union(union), - Some(ScSpecEntry::UdtEnumV0(enum_)) => { - enum_.cases.iter().next().map(|c| c.value.to_string()) - } - Some(ScSpecEntry::FunctionV0(_) | ScSpecEntry::UdtErrorEnumV0(_)) | None => None, - } - } - - fn example_union(&self, union: &ScSpecUdtUnionV0) -> Option { - let res = union - .cases - .iter() - .map(|case| match case { - ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => { - Some(format!("\"{}\"", name.to_utf8_string_lossy())) - } - ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { - name, type_, .. - }) => { - if type_.len() == 1 { - let single = self.example(&type_[0])?; - Some(format!("{{\"{}\":{single}}}", name.to_utf8_string_lossy())) - } else { - let names = type_ - .iter() - .map(|t| self.example(t)) - .collect::>>()? - .join(", "); - Some(format!("{{\"{}\":[{names}]}}", name.to_utf8_string_lossy())) - } - } - }) - .collect::>>()?; - Some(res.join("|")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use stellar_xdr::curr::ScSpecTypeBytesN; - - #[test] - fn from_json_primitives_bytesn() { - // TODO: Add test for parsing addresses - - // Check it parses hex-encoded bytes - let b = from_json_primitives( - &Value::String("beefface".to_string()), - &ScType::BytesN(ScSpecTypeBytesN { n: 4 }), - ) - .unwrap(); - assert_eq!( - b, - ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap())) - ); - - // Check it parses hex-encoded bytes when they are all numbers. Normally the json would - // interpret the CLI arg as a number, so we need a special case there. - let b = from_json_primitives( - &Value::Number(4554.into()), - &ScType::BytesN(ScSpecTypeBytesN { n: 2 }), - ) - .unwrap(); - assert_eq!( - b, - ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap())) - ); - } - - #[test] - fn from_json_primitives_bytes() { - // Check it parses hex-encoded bytes - let b = - from_json_primitives(&Value::String("beefface".to_string()), &ScType::Bytes).unwrap(); - assert_eq!( - b, - ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap())) - ); - - // Check it parses hex-encoded bytes when they are all numbers. Normally the json would - // interpret the CLI arg as a number, so we need a special case there. - let b = from_json_primitives(&Value::Number(4554.into()), &ScType::Bytes).unwrap(); - assert_eq!( - b, - ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap())) - ); - } - - #[test] - fn test_sc_address_from_json_strkey() { - // All zero contract address - match sc_address_from_json("CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4") { - Ok(addr) => assert_eq!(addr, ScVal::Address(ScAddress::Contract(Hash([0; 32])))), - Err(e) => panic!("Unexpected error: {e}"), - } - - // Real contract address - match sc_address_from_json("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") { - Ok(addr) => assert_eq!( - addr, - ScVal::Address(ScAddress::Contract( - [ - 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, - 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, - 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, - ] - .try_into() - .unwrap() - )) - ), - Err(e) => panic!("Unexpected error: {e}"), - } - - // All zero user account address - match sc_address_from_json("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF") { - Ok(addr) => assert_eq!( - addr, - ScVal::Address(ScAddress::Account(AccountId( - PublicKey::PublicKeyTypeEd25519([0; 32].try_into().unwrap()) - ))) - ), - Err(e) => panic!("Unexpected error: {e}"), - } - - // Real user account address - match sc_address_from_json("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") { - Ok(addr) => assert_eq!( - addr, - ScVal::Address(ScAddress::Account(AccountId( - PublicKey::PublicKeyTypeEd25519( - [ - 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, - 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, - 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, - ] - .try_into() - .unwrap() - ) - ))) - ), - Err(e) => panic!("Unexpected error: {e}"), - } - } -} diff --git a/cmd/crates/soroban-spec-tools/src/utils.rs b/cmd/crates/soroban-spec-tools/src/utils.rs deleted file mode 100644 index 66b153a1..00000000 --- a/cmd/crates/soroban-spec-tools/src/utils.rs +++ /dev/null @@ -1,294 +0,0 @@ -use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use hex::FromHexError; -use std::{ - fmt::Display, - io::{self, Cursor}, -}; - -use stellar_xdr::curr::{ - Limited, Limits, ReadXdr, ScEnvMetaEntry, ScMetaEntry, ScMetaV0, ScSpecEntry, ScSpecFunctionV0, - ScSpecUdtEnumV0, ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionV0, StringM, -}; - -pub struct ContractSpec { - pub env_meta_base64: Option, - pub env_meta: Vec, - pub meta_base64: Option, - pub meta: Vec, - pub spec_base64: Option, - pub spec: Vec, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("reading file {filepath}: {error}")] - CannotReadContractFile { - filepath: std::path::PathBuf, - error: io::Error, - }, - #[error("cannot parse wasm file {file}: {error}")] - CannotParseWasm { - file: std::path::PathBuf, - error: wasmparser::BinaryReaderError, - }, - #[error("xdr processing error: {0}")] - Xdr(#[from] stellar_xdr::curr::Error), - - #[error(transparent)] - Parser(#[from] wasmparser::BinaryReaderError), -} - -impl ContractSpec { - pub fn new(bytes: &[u8]) -> Result { - let mut env_meta: Option<&[u8]> = None; - let mut meta: Option<&[u8]> = None; - let mut spec: Option<&[u8]> = None; - for payload in wasmparser::Parser::new(0).parse_all(bytes) { - let payload = payload?; - if let wasmparser::Payload::CustomSection(section) = payload { - let out = match section.name() { - "contractenvmetav0" => &mut env_meta, - "contractmetav0" => &mut meta, - "contractspecv0" => &mut spec, - _ => continue, - }; - *out = Some(section.data()); - }; - } - - let mut env_meta_base64 = None; - let env_meta = if let Some(env_meta) = env_meta { - env_meta_base64 = Some(base64.encode(env_meta)); - let cursor = Cursor::new(env_meta); - let mut read = Limited::new(cursor, Limits::none()); - ScEnvMetaEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - let mut meta_base64 = None; - let meta = if let Some(meta) = meta { - meta_base64 = Some(base64.encode(meta)); - let cursor = Cursor::new(meta); - let mut read = Limited::new(cursor, Limits::none()); - ScMetaEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - let mut spec_base64 = None; - let spec = if let Some(spec) = spec { - spec_base64 = Some(base64.encode(spec)); - let cursor = Cursor::new(spec); - let mut read = Limited::new(cursor, Limits::none()); - ScSpecEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - Ok(ContractSpec { - env_meta_base64, - env_meta, - meta_base64, - meta, - spec_base64, - spec, - }) - } -} - -impl Display for ContractSpec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(env_meta) = &self.env_meta_base64 { - writeln!(f, "Env Meta: {env_meta}")?; - for env_meta_entry in &self.env_meta { - match env_meta_entry { - ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) => { - writeln!(f, " • Interface Version: {v}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Env Meta: None\n")?; - } - - if let Some(_meta) = &self.meta_base64 { - writeln!(f, "Contract Meta:")?; - for meta_entry in &self.meta { - match meta_entry { - ScMetaEntry::ScMetaV0(ScMetaV0 { key, val }) => { - writeln!(f, " • {key}: {val}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Contract Meta: None\n")?; - } - - if let Some(_spec_base64) = &self.spec_base64 { - writeln!(f, "Contract Spec:")?; - for spec_entry in &self.spec { - match spec_entry { - ScSpecEntry::FunctionV0(func) => write_func(f, func)?, - ScSpecEntry::UdtUnionV0(udt) => write_union(f, udt)?, - ScSpecEntry::UdtStructV0(udt) => write_struct(f, udt)?, - ScSpecEntry::UdtEnumV0(udt) => write_enum(f, udt)?, - ScSpecEntry::UdtErrorEnumV0(udt) => write_error(f, udt)?, - } - } - } else { - writeln!(f, "Contract Spec: None")?; - } - Ok(()) - } -} - -fn write_func(f: &mut std::fmt::Formatter<'_>, func: &ScSpecFunctionV0) -> std::fmt::Result { - writeln!(f, " • Function: {}", func.name.to_utf8_string_lossy())?; - if func.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - &indent(&func.doc.to_utf8_string_lossy(), 11).trim() - )?; - } - writeln!( - f, - " Inputs: {}", - indent(&format!("{:#?}", func.inputs), 5).trim() - )?; - writeln!( - f, - " Output: {}", - indent(&format!("{:#?}", func.outputs), 5).trim() - )?; - writeln!(f)?; - Ok(()) -} - -fn write_union(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtUnionV0) -> std::fmt::Result { - writeln!(f, " • Union: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_struct(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtStructV0) -> std::fmt::Result { - writeln!(f, " • Struct: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Fields:")?; - for field in udt.fields.iter() { - writeln!( - f, - " • {}: {}", - field.name.to_utf8_string_lossy(), - indent(&format!("{:#?}", field.type_), 8).trim() - )?; - if field.doc.len() > 0 { - writeln!(f, "{}", indent(&format!("{:#?}", field.doc), 8))?; - } - } - writeln!(f)?; - Ok(()) -} - -fn write_enum(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtEnumV0) -> std::fmt::Result { - writeln!(f, " • Enum: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_error(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtErrorEnumV0) -> std::fmt::Result { - writeln!(f, " • Error: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn indent(s: &str, n: usize) -> String { - let pad = " ".repeat(n); - s.lines() - .map(|line| format!("{pad}{line}")) - .collect::>() - .join("\n") -} - -fn format_name(lib: &StringM<80>, name: &StringM<60>) -> String { - if lib.len() > 0 { - format!( - "{}::{}", - lib.to_utf8_string_lossy(), - name.to_utf8_string_lossy() - ) - } else { - name.to_utf8_string_lossy() - } -} - -/// # Errors -/// -/// Might return an error -pub fn padded_hex_from_str(s: &str, n: usize) -> Result, FromHexError> { - if s.len() > n * 2 { - return Err(FromHexError::InvalidStringLength); - } - let mut decoded = vec![0u8; n]; - let padded = format!("{s:0>width$}", width = n * 2); - hex::decode_to_slice(padded, &mut decoded)?; - Ok(decoded) -} - -/// # Errors -/// -/// Might return an error -pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strkey::DecodeError> { - stellar_strkey::Contract::from_string(contract_id) - .map(|strkey| strkey.0) - .or_else(|_| { - // strkey failed, try to parse it as a hex string, for backwards compatibility. - padded_hex_from_str(contract_id, 32) - .map_err(|_| stellar_strkey::DecodeError::Invalid)? - .try_into() - .map_err(|_| stellar_strkey::DecodeError::Invalid) - }) - .map_err(|_| stellar_strkey::DecodeError::Invalid) -} diff --git a/cmd/crates/soroban-spec-typescript/Cargo.toml b/cmd/crates/soroban-spec-typescript/Cargo.toml deleted file mode 100644 index cc17073f..00000000 --- a/cmd/crates/soroban-spec-typescript/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "soroban-spec-typescript" -description = "Soroban contract spec utilities for generating JSON." -homepage = "https://github.com/stellar/soroban-tools" -repository = "https://github.com/stellar/soroban-tools" -authors = ["Stellar Development Foundation "] -readme = "README.md" -license = "Apache-2.0" -version.workspace = true -edition = "2021" -rust-version.workspace = true - -[dependencies] -soroban-spec = { workspace = true } -thiserror = "1.0.32" -serde = "1.0.82" -serde_derive = "1.0.82" -serde_json = "1.0.82" -sha2 = "0.9.9" -prettyplease = "0.2.4" -include_dir = { version = "0.7.3", features = ["glob"] } -heck = "0.4.1" -itertools = { workspace = true } -base64 = { workspace = true } - -[dependencies.stellar-xdr] -workspace = true -features = ["curr", "std", "serde", "base64"] - -[dev_dependencies] -temp-dir = "0.1.11" -pretty_assertions = "1.2.1" -walkdir = "2.3.3" diff --git a/cmd/crates/soroban-spec-typescript/README.md b/cmd/crates/soroban-spec-typescript/README.md deleted file mode 100644 index 4cc3f522..00000000 --- a/cmd/crates/soroban-spec-typescript/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# soroban-spec-json - -Generation of TypeScript client bindings from Soroban contract specification / -interface. diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/.gitignore b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/.gitignore deleted file mode 100644 index 72aae85f..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -out/ diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/README.md b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/README.md deleted file mode 100644 index 03f87f30..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# test_custom_types JS - -JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `test_custom_types` via Soroban RPC. - -This library was automatically generated by Soroban CLI using a command similar to: - -```bash -soroban contract bindings ts \ - --rpc-url https://rpc-futurenet.stellar.org:443 \ - --network-passphrase "Test SDF Future Network ; October 2022" \ - --contract-id CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK \ - --output-dir ./path/to/test_custom_types -``` - -The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily. - -# To publish or not to publish - -This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command. - -But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path: - -```json -"dependencies": { - "test_custom_types": "./path/to/this/folder" -} -``` - -However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract. - -```json -"scripts": { - "postinstall": "soroban contract bindings ts --rpc-url https://rpc-futurenet.stellar.org:443 --network-passphrase \"Test SDF Future Network ; October 2022\" --id CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK --name test_custom_types" -} -``` - -Obviously you need to adjust the above command based on the actual command you used to generate the library. - -# Use it - -Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods: - -```js -import { Contract, networks } from "test_custom_types" - -const contract = new Contract({ - ...networks.futurenet, // for example; check which networks this library exports - rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers -}) - -contract.| -``` - -As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code. diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.d.ts deleted file mode 100644 index 1d5e6f5e..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.d.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Account, Address, Operation, SorobanRpc, xdr } from "@stellar/stellar-sdk"; -import type { Memo, MemoType, Transaction } from "@stellar/stellar-sdk"; -import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; -export type Tx = Transaction, Operation[]>; -export declare class ExpiredStateError extends Error { -} -export declare class NeedsMoreSignaturesError extends Error { -} -export declare class WalletDisconnectedError extends Error { -} -export declare class SendResultOnlyError extends Error { -} -export declare class SendFailedError extends Error { -} -export declare class NoUnsignedNonInvokerAuthEntriesError extends Error { -} -type SendTx = SorobanRpc.Api.SendTransactionResponse; -type GetTx = SorobanRpc.Api.GetTransactionResponse; -export type u32 = number; -export type i32 = number; -export type u64 = bigint; -export type i64 = bigint; -export type u128 = bigint; -export type i128 = bigint; -export type u256 = bigint; -export type i256 = bigint; -export type Option = T | undefined; -export type Typepoint = bigint; -export type Duration = bigint; -export { Address }; -export interface Error_ { - message: string; -} -export interface Result { - unwrap(): T; - unwrapErr(): E; - isOk(): boolean; - isErr(): boolean; -} -export declare class Ok implements Result { - readonly value: T; - constructor(value: T); - unwrapErr(): E; - unwrap(): T; - isOk(): boolean; - isErr(): boolean; -} -export declare class Err implements Result { - readonly error: E; - constructor(error: E); - unwrapErr(): E; - unwrap(): never; - isOk(): boolean; - isErr(): boolean; -} -export declare const contractErrorPattern: RegExp; -type AssembledTransactionOptions = MethodOptions & ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal | Err) => T; -}; -export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; -export declare class AssembledTransaction { - options: AssembledTransactionOptions; - raw: Tx; - private simulation?; - private simulationResult?; - private simulationTransactionData?; - private server; - toJSON(): string; - static fromJSON(options: Omit, 'args'>, { tx, simulationResult, simulationTransactionData }: { - tx: XDR_BASE64; - simulationResult: { - auth: XDR_BASE64[]; - retval: XDR_BASE64; - }; - simulationTransactionData: XDR_BASE64; - }): AssembledTransaction; - private constructor(); - static fromSimulation(options: AssembledTransactionOptions): Promise>; - simulate: () => Promise; - get simulationData(): { - result: SorobanRpc.Api.SimulateHostFunctionResult; - transactionData: xdr.SorobanTransactionData; - }; - get result(): T; - parseError(errorMessage: string): Err | undefined; - getWallet: () => Promise; - getPublicKey: () => Promise; - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount: () => Promise; - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend: ({ secondsToWait, force }?: { - /** - * Wait `secondsToWait` seconds (default: 10) for both the transaction to SEND successfully (will keep trying if the server returns `TRY_AGAIN_LATER`), as well as for the transaction to COMPLETE (will keep checking if the server returns `PENDING`). - */ - secondsToWait?: number | undefined; - /** - * If `true`, sign and send the transaction even if it is a read call. - */ - force?: boolean | undefined; - }) => Promise>; - getStorageExpiration: () => Promise; - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy: ({ includeAlreadySigned, }?: { - /** - * Whether or not to include auth entries that have already been signed. Default: false - */ - includeAlreadySigned?: boolean | undefined; - }) => Promise; - preImageFor(entry: xdr.SorobanAuthorizationEntry, signatureExpirationLedger: number): xdr.HashIdPreimage; - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries: (expiration?: number | Promise) => Promise; - get isReadCall(): boolean; - hasRealInvoker: () => Promise; -} -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -declare class SentTransaction { - options: AssembledTransactionOptions; - assembled: AssembledTransaction; - server: SorobanRpc.Server; - signed: Tx; - sendTransactionResponse?: SendTx; - sendTransactionResponseAll?: SendTx[]; - getTransactionResponse?: GetTx; - getTransactionResponseAll?: GetTx[]; - constructor(options: AssembledTransactionOptions, assembled: AssembledTransaction); - static init: (options: AssembledTransactionOptions, assembled: AssembledTransaction, secondsToWait?: number) => Promise>; - private send; - get result(): T; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.js deleted file mode 100644 index c60a6e5f..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.js +++ /dev/null @@ -1,462 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AssembledTransaction = exports.NULL_ACCOUNT = exports.contractErrorPattern = exports.Err = exports.Ok = exports.Address = exports.NoUnsignedNonInvokerAuthEntriesError = exports.SendFailedError = exports.SendResultOnlyError = exports.WalletDisconnectedError = exports.NeedsMoreSignaturesError = exports.ExpiredStateError = void 0; -const stellar_sdk_1 = require("@stellar/stellar-sdk"); -Object.defineProperty(exports, "Address", { enumerable: true, get: function () { return stellar_sdk_1.Address; } }); -const buffer_1 = require("buffer"); -class ExpiredStateError extends Error { -} -exports.ExpiredStateError = ExpiredStateError; -class NeedsMoreSignaturesError extends Error { -} -exports.NeedsMoreSignaturesError = NeedsMoreSignaturesError; -class WalletDisconnectedError extends Error { -} -exports.WalletDisconnectedError = WalletDisconnectedError; -class SendResultOnlyError extends Error { -} -exports.SendResultOnlyError = SendResultOnlyError; -class SendFailedError extends Error { -} -exports.SendFailedError = SendFailedError; -class NoUnsignedNonInvokerAuthEntriesError extends Error { -} -exports.NoUnsignedNonInvokerAuthEntriesError = NoUnsignedNonInvokerAuthEntriesError; -; -; -class Ok { - value; - constructor(value) { - this.value = value; - } - unwrapErr() { - throw new Error('No error'); - } - unwrap() { - return this.value; - } - isOk() { - return true; - } - isErr() { - return !this.isOk(); - } -} -exports.Ok = Ok; -class Err { - error; - constructor(error) { - this.error = error; - } - unwrapErr() { - return this.error; - } - unwrap() { - throw new Error(this.error.message); - } - isOk() { - return false; - } - isErr() { - return !this.isOk(); - } -} -exports.Err = Err; -exports.contractErrorPattern = /Error\(Contract, #(\d+)\)/; -exports.NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; -class AssembledTransaction { - options; - raw; - simulation; - simulationResult; - simulationTransactionData; - server; - toJSON() { - return JSON.stringify({ - method: this.options.method, - tx: this.raw?.toXDR(), - simulationResult: { - auth: this.simulationData.result.auth.map(a => a.toXDR('base64')), - retval: this.simulationData.result.retval.toXDR('base64'), - }, - simulationTransactionData: this.simulationData.transactionData.toXDR('base64'), - }); - } - static fromJSON(options, { tx, simulationResult, simulationTransactionData }) { - const txn = new AssembledTransaction(options); - txn.raw = stellar_sdk_1.TransactionBuilder.fromXDR(tx, options.networkPassphrase); - txn.simulationResult = { - auth: simulationResult.auth.map(a => stellar_sdk_1.xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64')), - retval: stellar_sdk_1.xdr.ScVal.fromXDR(simulationResult.retval, 'base64'), - }; - txn.simulationTransactionData = stellar_sdk_1.xdr.SorobanTransactionData.fromXDR(simulationTransactionData, 'base64'); - return txn; - } - constructor(options) { - this.options = options; - this.server = new stellar_sdk_1.SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - } - static async fromSimulation(options) { - const tx = new AssembledTransaction(options); - const contract = new stellar_sdk_1.Contract(options.contractId); - tx.raw = new stellar_sdk_1.TransactionBuilder(await tx.getAccount(), { - fee: options.fee?.toString(10) ?? stellar_sdk_1.BASE_FEE, - networkPassphrase: options.networkPassphrase, - }) - .addOperation(contract.call(options.method, ...(options.args ?? []))) - .setTimeout(stellar_sdk_1.TimeoutInfinite) - .build(); - return await tx.simulate(); - } - simulate = async () => { - if (!this.raw) - throw new Error('Transaction has not yet been assembled'); - this.simulation = await this.server.simulateTransaction(this.raw); - if (stellar_sdk_1.SorobanRpc.Api.isSimulationSuccess(this.simulation)) { - this.raw = stellar_sdk_1.SorobanRpc.assembleTransaction(this.raw, this.simulation).build(); - } - return this; - }; - get simulationData() { - if (this.simulationResult && this.simulationTransactionData) { - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - }; - } - // else, we know we just did the simulation on this machine - const simulation = this.simulation; - if (stellar_sdk_1.SorobanRpc.Api.isSimulationError(simulation)) { - throw new Error(`Transaction simulation failed: "${simulation.error}"`); - } - if (stellar_sdk_1.SorobanRpc.Api.isSimulationRestore(simulation)) { - throw new ExpiredStateError(`You need to restore some contract state before you can invoke this method. ${JSON.stringify(simulation, null, 2)}`); - } - if (!simulation.result) { - throw new Error(`Expected an invocation simulation, but got no 'result' field. Simulation: ${JSON.stringify(simulation, null, 2)}`); - } - // add to object for serialization & deserialization - this.simulationResult = simulation.result; - this.simulationTransactionData = simulation.transactionData.build(); - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - }; - } - get result() { - try { - return this.options.parseResultXdr(this.simulationData.result.retval); - } - catch (e) { - let err = this.parseError(e.toString()); - if (err) - return err; - throw e; - } - } - parseError(errorMessage) { - if (!this.options.errorTypes) - return; - const match = errorMessage.match(exports.contractErrorPattern); - if (!match) - return; - let i = parseInt(match[1], 10); - let err = this.options.errorTypes[i]; - if (err) - return new Err(err); - } - getWallet = async () => { - return this.options.wallet ?? (await Promise.resolve().then(() => require("@stellar/freighter-api"))).default; - }; - getPublicKey = async () => { - const wallet = await this.getWallet(); - if (await wallet.isConnected() && await wallet.isAllowed()) { - return (await wallet.getUserInfo()).publicKey; - } - }; - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount = async () => { - const publicKey = await this.getPublicKey(); - return publicKey - ? await this.server.getAccount(publicKey) - : new stellar_sdk_1.Account(exports.NULL_ACCOUNT, "0"); - }; - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend = async ({ secondsToWait = 10, force = false } = {}) => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated'); - } - if (!force && this.isReadCall) { - throw new Error('This is a read call. It requires no signature or sending. Use `force: true` to sign and send anyway.'); - } - if (!await this.hasRealInvoker()) { - throw new WalletDisconnectedError('Wallet is not connected'); - } - if (this.raw.source !== (await this.getAccount()).accountId()) { - throw new Error(`You must submit the transaction with the account that originally created it. Please switch to the wallet with "${this.raw.source}" as its public key.`); - } - if ((await this.needsNonInvokerSigningBy()).length) { - throw new NeedsMoreSignaturesError('Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.'); - } - return await SentTransaction.init(this.options, this, secondsToWait); - }; - getStorageExpiration = async () => { - const entryRes = await this.server.getLedgerEntries(new stellar_sdk_1.Contract(this.options.contractId).getFootprint()); - if (!entryRes.entries || - !entryRes.entries.length || - !entryRes.entries[0].liveUntilLedgerSeq) - throw new Error('failed to get ledger entry'); - return entryRes.entries[0].liveUntilLedgerSeq; - }; - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy = async ({ includeAlreadySigned = false, } = {}) => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated'); - } - // We expect that any transaction constructed by these libraries has a - // single operation, which is an InvokeHostFunction operation. The host - // function being invoked is the contract method call. - if (!("operations" in this.raw)) { - throw new Error(`Unexpected Transaction type; no operations: ${JSON.stringify(this.raw)}`); - } - const rawInvokeHostFunctionOp = this.raw - .operations[0]; - return [...new Set((rawInvokeHostFunctionOp.auth ?? []).filter(entry => entry.credentials().switch() === - stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsAddress() && - (includeAlreadySigned || - entry.credentials().address().signature().switch().name === 'scvVoid')).map(entry => stellar_sdk_1.StrKey.encodeEd25519PublicKey(entry.credentials().address().address().accountId().ed25519())))]; - }; - preImageFor(entry, signatureExpirationLedger) { - const addrAuth = entry.credentials().address(); - return stellar_sdk_1.xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(new stellar_sdk_1.xdr.HashIdPreimageSorobanAuthorization({ - networkId: (0, stellar_sdk_1.hash)(buffer_1.Buffer.from(this.options.networkPassphrase)), - nonce: addrAuth.nonce(), - invocation: entry.rootInvocation(), - signatureExpirationLedger, - })); - } - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries = async ( - /** - * When to set each auth entry to expire. Could be any number of blocks in - * the future. Can be supplied as a promise or a raw number. Default: - * contract's current `persistent` storage expiration date/ledger - * number/block. - */ - expiration = this.getStorageExpiration()) => { - if (!this.raw) - throw new Error('Transaction has not yet been assembled or simulated'); - const needsNonInvokerSigningBy = await this.needsNonInvokerSigningBy(); - if (!needsNonInvokerSigningBy) - throw new NoUnsignedNonInvokerAuthEntriesError('No unsigned non-invoker auth entries; maybe you already signed?'); - const publicKey = await this.getPublicKey(); - if (!publicKey) - throw new Error('Could not get public key from wallet; maybe Freighter is not signed in?'); - if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) - throw new Error(`No auth entries for public key "${publicKey}"`); - const wallet = await this.getWallet(); - const rawInvokeHostFunctionOp = this.raw - .operations[0]; - const authEntries = rawInvokeHostFunctionOp.auth ?? []; - for (const [i, entry] of authEntries.entries()) { - if (entry.credentials().switch() !== - stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsAddress()) { - // if the invoker/source account, then the entry doesn't need explicit - // signature, since the tx envelope is already signed by the source - // account, so only check for sorobanCredentialsAddress - continue; - } - const pk = stellar_sdk_1.StrKey.encodeEd25519PublicKey(entry.credentials().address().address().accountId().ed25519()); - // this auth entry needs to be signed by a different account - // (or maybe already was!) - if (pk !== publicKey) - continue; - authEntries[i] = await (0, stellar_sdk_1.authorizeEntry)(entry, async (preimage) => buffer_1.Buffer.from(await wallet.signAuthEntry(preimage.toXDR('base64')), 'base64'), await expiration, this.options.networkPassphrase); - } - }; - get isReadCall() { - const authsCount = this.simulationData.result.auth.length; - const writeLength = this.simulationData.transactionData.resources().footprint().readWrite().length; - return (authsCount === 0) && (writeLength === 0); - } - hasRealInvoker = async () => { - const account = await this.getAccount(); - return account.accountId() !== exports.NULL_ACCOUNT; - }; -} -exports.AssembledTransaction = AssembledTransaction; -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -class SentTransaction { - options; - assembled; - server; - signed; - sendTransactionResponse; - sendTransactionResponseAll; - getTransactionResponse; - getTransactionResponseAll; - constructor(options, assembled) { - this.options = options; - this.assembled = assembled; - this.server = new stellar_sdk_1.SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - this.assembled = assembled; - } - static init = async (options, assembled, secondsToWait = 10) => { - const tx = new SentTransaction(options, assembled); - return await tx.send(secondsToWait); - }; - send = async (secondsToWait = 10) => { - const wallet = await this.assembled.getWallet(); - this.sendTransactionResponseAll = await withExponentialBackoff(async (previousFailure) => { - if (previousFailure) { - // Increment transaction sequence number and resimulate before trying again - // Soroban transaction can only have 1 operation - const op = this.assembled.raw.operations[0]; - this.assembled.raw = new stellar_sdk_1.TransactionBuilder(await this.assembled.getAccount(), { - fee: this.assembled.raw.fee, - networkPassphrase: this.options.networkPassphrase, - }) - .setTimeout(stellar_sdk_1.TimeoutInfinite) - .addOperation(stellar_sdk_1.Operation.invokeHostFunction({ ...op, auth: op.auth ?? [] })) - .build(); - await this.assembled.simulate(); - } - const signature = await wallet.signTransaction(this.assembled.raw.toXDR(), { - networkPassphrase: this.options.networkPassphrase, - }); - this.signed = stellar_sdk_1.TransactionBuilder.fromXDR(signature, this.options.networkPassphrase); - return this.server.sendTransaction(this.signed); - }, resp => resp.status !== "PENDING", secondsToWait); - this.sendTransactionResponse = this.sendTransactionResponseAll[this.sendTransactionResponseAll.length - 1]; - if (this.sendTransactionResponse.status !== "PENDING") { - throw new Error(`Tried to resubmit transaction for ${secondsToWait} seconds, but it's still failing. ` + - `All attempts: ${JSON.stringify(this.sendTransactionResponseAll, null, 2)}`); - } - const { hash } = this.sendTransactionResponse; - this.getTransactionResponseAll = await withExponentialBackoff(() => this.server.getTransaction(hash), resp => resp.status === stellar_sdk_1.SorobanRpc.Api.GetTransactionStatus.NOT_FOUND, secondsToWait); - this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1]; - if (this.getTransactionResponse.status === stellar_sdk_1.SorobanRpc.Api.GetTransactionStatus.NOT_FOUND) { - console.error(`Waited ${secondsToWait} seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Sent transaction: ${JSON.stringify(this.sendTransactionResponse, null, 2)}\n` + - `All attempts to get the result: ${JSON.stringify(this.getTransactionResponseAll, null, 2)}`); - } - return this; - }; - get result() { - // 1. check if transaction was submitted and awaited with `getTransaction` - if ("getTransactionResponse" in this && - this.getTransactionResponse) { - // getTransactionResponse has a `returnValue` field unless it failed - if ("returnValue" in this.getTransactionResponse) { - return this.options.parseResultXdr(this.getTransactionResponse.returnValue); - } - // if "returnValue" not present, the transaction failed; return without parsing the result - throw new Error("Transaction failed! Cannot parse result."); - } - // 2. otherwise, maybe it was merely sent with `sendTransaction` - if (this.sendTransactionResponse) { - const errorResult = this.sendTransactionResponse.errorResult?.result(); - if (errorResult) { - throw new SendFailedError(`Transaction simulation looked correct, but attempting to send the transaction failed. Check \`simulation\` and \`sendTransactionResponseAll\` to troubleshoot. Decoded \`sendTransactionResponse.errorResultXdr\`: ${errorResult}`); - } - throw new SendResultOnlyError(`Transaction was sent to the network, but not yet awaited. No result to show. Await transaction completion with \`getTransaction(sendTransactionResponse.hash)\``); - } - // 3. finally, if neither of those are present, throw an error - throw new Error(`Sending transaction failed: ${JSON.stringify(this.assembled)}`); - } -} -/** - * Keep calling a `fn` for `secondsToWait` seconds, if `keepWaitingIf` is true. - * Returns an array of all attempts to call the function. - */ -async function withExponentialBackoff(fn, keepWaitingIf, secondsToWait, exponentialFactor = 1.5, verbose = false) { - const attempts = []; - let count = 0; - attempts.push(await fn()); - if (!keepWaitingIf(attempts[attempts.length - 1])) - return attempts; - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf(); - let waitTime = 1000; - let totalWaitTime = waitTime; - while (Date.now() < waitUntil && keepWaitingIf(attempts[attempts.length - 1])) { - count++; - // Wait a beat - if (verbose) { - console.info(`Waiting ${waitTime}ms before trying again (bringing the total wait time to ${totalWaitTime}ms so far, of total ${secondsToWait * 1000}ms)`); - } - await new Promise(res => setTimeout(res, waitTime)); - // Exponential backoff - waitTime = waitTime * exponentialFactor; - if (new Date(Date.now() + waitTime).valueOf() > waitUntil) { - waitTime = waitUntil - Date.now(); - if (verbose) { - console.info(`was gonna wait too long; new waitTime: ${waitTime}ms`); - } - } - totalWaitTime = waitTime + totalWaitTime; - // Try again - attempts.push(await fn(attempts[attempts.length - 1])); - if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { - console.info(`${count}. Called ${fn}; ${attempts.length} prev attempts. Most recent: ${JSON.stringify(attempts[attempts.length - 1], null, 2)}`); - } - } - return attempts; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.d.ts deleted file mode 100644 index 66d3d595..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.d.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { ContractSpec } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -import type { u32, i32, i64, i128, Option, Error_ } from './assembled-tx.js'; -import type { ClassOptions } from './method-options.js'; -export * from './assembled-tx.js'; -export * from './method-options.js'; -export declare const networks: { - readonly futurenet: { - readonly networkPassphrase: "Test SDF Future Network ; October 2022"; - readonly contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK"; - }; -}; -/** - This is from the rust doc above the struct Test - */ -export interface Test { - /** - - */ - a: u32; - /** - - */ - b: boolean; - /** - - */ - c: string; -} -/** - - */ -export type SimpleEnum = { - tag: "First"; - values: void; -} | { - tag: "Second"; - values: void; -} | { - tag: "Third"; - values: void; -}; -/** - - */ -export declare enum RoyalCard { - Jack = 11, - Queen = 12, - King = 13 -} -/** - - */ -export type TupleStruct = readonly [Test, SimpleEnum]; -/** - - */ -export type ComplexEnum = { - tag: "Struct"; - values: readonly [Test]; -} | { - tag: "Tuple"; - values: readonly [TupleStruct]; -} | { - tag: "Enum"; - values: readonly [SimpleEnum]; -} | { - tag: "Asset"; - values: readonly [string, i128]; -} | { - tag: "Void"; - values: void; -}; -/** - - */ -export declare const Errors: { - 1: { - message: string; - }; -}; -export declare class Contract { - readonly options: ClassOptions; - spec: ContractSpec; - constructor(options: ClassOptions); - private readonly parsers; - private txFromJSON; - readonly fromJSON: { - hello: (json: string) => AssembledTransaction; - woid: (json: string) => AssembledTransaction; - val: (json: string) => AssembledTransaction; - u32FailOnEven: (json: string) => AssembledTransaction | Ok>; - u32: (json: string) => AssembledTransaction; - i32: (json: string) => AssembledTransaction; - i64: (json: string) => AssembledTransaction; - struktHel: (json: string) => AssembledTransaction; - strukt: (json: string) => AssembledTransaction; - simple: (json: string) => AssembledTransaction; - complex: (json: string) => AssembledTransaction; - addresse: (json: string) => AssembledTransaction; - bytes: (json: string) => AssembledTransaction; - bytesN: (json: string) => AssembledTransaction; - card: (json: string) => AssembledTransaction; - boolean: (json: string) => AssembledTransaction; - not: (json: string) => AssembledTransaction; - i128: (json: string) => AssembledTransaction; - u128: (json: string) => AssembledTransaction; - multiArgs: (json: string) => AssembledTransaction; - map: (json: string) => AssembledTransaction>; - vec: (json: string) => AssembledTransaction; - tuple: (json: string) => AssembledTransaction; - option: (json: string) => AssembledTransaction>; - u256: (json: string) => AssembledTransaction; - i256: (json: string) => AssembledTransaction; - string: (json: string) => AssembledTransaction; - tupleStrukt: (json: string) => AssembledTransaction; - }; - /** -* Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - hello: ({ hello }: { - hello: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - woid: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - val: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32FailOnEven: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise | Ok>>; - /** -* Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i32: ({ i32_ }: { - i32_: i32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i64: ({ i64_ }: { - i64_: i64; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct -*/ - struktHel: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - strukt: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - simple: ({ simple }: { - simple: SimpleEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - complex: ({ complex }: { - complex: ComplexEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - addresse: ({ addresse }: { - addresse: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytes: ({ bytes }: { - bytes: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytesN: ({ bytes_n }: { - bytes_n: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - card: ({ card }: { - card: RoyalCard; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - boolean: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value -*/ - not: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i128: ({ i128 }: { - i128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u128: ({ u128 }: { - u128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - multiArgs: ({ a, b }: { - a: u32; - b: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - map: ({ map }: { - map: Map; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - vec: ({ vec }: { - vec: Array; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tuple: ({ tuple }: { - tuple: readonly [string, u32]; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument -*/ - option: ({ option }: { - option: Option; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u256: ({ u256 }: { - u256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i256: ({ i256 }: { - i256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - string: ({ string }: { - string: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tupleStrukt: ({ tuple_strukt }: { - tuple_strukt: TupleStruct; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.js deleted file mode 100644 index d6e27aa8..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.js +++ /dev/null @@ -1,527 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Contract = exports.Errors = exports.RoyalCard = exports.networks = void 0; -const stellar_sdk_1 = require("@stellar/stellar-sdk"); -const buffer_1 = require("buffer"); -const assembled_tx_js_1 = require("./assembled-tx.js"); -__exportStar(require("./assembled-tx.js"), exports); -__exportStar(require("./method-options.js"), exports); -if (typeof window !== 'undefined') { - //@ts-ignore Buffer exists - window.Buffer = window.Buffer || buffer_1.Buffer; -} -exports.networks = { - futurenet: { - networkPassphrase: "Test SDF Future Network ; October 2022", - contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK", - } -}; -/** - - */ -var RoyalCard; -(function (RoyalCard) { - RoyalCard[RoyalCard["Jack"] = 11] = "Jack"; - RoyalCard[RoyalCard["Queen"] = 12] = "Queen"; - RoyalCard[RoyalCard["King"] = 13] = "King"; -})(RoyalCard || (exports.RoyalCard = RoyalCard = {})); -/** - - */ -exports.Errors = { - 1: { message: "Please provide an odd number" } -}; -class Contract { - options; - spec; - constructor(options) { - this.options = options; - this.spec = new stellar_sdk_1.ContractSpec([ - "AAAAAQAAAC9UaGlzIGlzIGZyb20gdGhlIHJ1c3QgZG9jIGFib3ZlIHRoZSBzdHJ1Y3QgVGVzdAAAAAAAAAAABFRlc3QAAAADAAAAAAAAAAFhAAAAAAAABAAAAAAAAAABYgAAAAAAAAEAAAAAAAAAAWMAAAAAAAAR", - "AAAAAgAAAAAAAAAAAAAAClNpbXBsZUVudW0AAAAAAAMAAAAAAAAAAAAAAAVGaXJzdAAAAAAAAAAAAAAAAAAABlNlY29uZAAAAAAAAAAAAAAAAAAFVGhpcmQAAAA=", - "AAAAAwAAAAAAAAAAAAAACVJveWFsQ2FyZAAAAAAAAAMAAAAAAAAABEphY2sAAAALAAAAAAAAAAVRdWVlbgAAAAAAAAwAAAAAAAAABEtpbmcAAAAN", - "AAAAAQAAAAAAAAAAAAAAC1R1cGxlU3RydWN0AAAAAAIAAAAAAAAAATAAAAAAAAfQAAAABFRlc3QAAAAAAAAAATEAAAAAAAfQAAAAClNpbXBsZUVudW0AAA==", - "AAAAAgAAAAAAAAAAAAAAC0NvbXBsZXhFbnVtAAAAAAUAAAABAAAAAAAAAAZTdHJ1Y3QAAAAAAAEAAAfQAAAABFRlc3QAAAABAAAAAAAAAAVUdXBsZQAAAAAAAAEAAAfQAAAAC1R1cGxlU3RydWN0AAAAAAEAAAAAAAAABEVudW0AAAABAAAH0AAAAApTaW1wbGVFbnVtAAAAAAABAAAAAAAAAAVBc3NldAAAAAAAAAIAAAATAAAACwAAAAAAAAAAAAAABFZvaWQ=", - "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAAQAAABxQbGVhc2UgcHJvdmlkZSBhbiBvZGQgbnVtYmVyAAAAD051bWJlck11c3RCZU9kZAAAAAAB", - "AAAAAAAAAAAAAAAFaGVsbG8AAAAAAAABAAAAAAAAAAVoZWxsbwAAAAAAABEAAAABAAAAEQ==", - "AAAAAAAAAAAAAAAEd29pZAAAAAAAAAAA", - "AAAAAAAAAAAAAAADdmFsAAAAAAAAAAABAAAAAA==", - "AAAAAAAAAAAAAAAQdTMyX2ZhaWxfb25fZXZlbgAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAA+kAAAAEAAAAAw==", - "AAAAAAAAAAAAAAAEdTMyXwAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAAEaTMyXwAAAAEAAAAAAAAABGkzMl8AAAAFAAAAAQAAAAU=", - "AAAAAAAAAAAAAAAEaTY0XwAAAAEAAAAAAAAABGk2NF8AAAAHAAAAAQAAAAc=", - "AAAAAAAAACxFeGFtcGxlIGNvbnRyYWN0IG1ldGhvZCB3aGljaCB0YWtlcyBhIHN0cnVjdAAAAApzdHJ1a3RfaGVsAAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAPqAAAAEQ==", - "AAAAAAAAAAAAAAAGc3RydWt0AAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAfQAAAABFRlc3Q=", - "AAAAAAAAAAAAAAAGc2ltcGxlAAAAAAABAAAAAAAAAAZzaW1wbGUAAAAAB9AAAAAKU2ltcGxlRW51bQAAAAAAAQAAB9AAAAAKU2ltcGxlRW51bQAA", - "AAAAAAAAAAAAAAAHY29tcGxleAAAAAABAAAAAAAAAAdjb21wbGV4AAAAB9AAAAALQ29tcGxleEVudW0AAAAAAQAAB9AAAAALQ29tcGxleEVudW0A", - "AAAAAAAAAAAAAAAIYWRkcmVzc2UAAAABAAAAAAAAAAhhZGRyZXNzZQAAABMAAAABAAAAEw==", - "AAAAAAAAAAAAAAAFYnl0ZXMAAAAAAAABAAAAAAAAAAVieXRlcwAAAAAAAA4AAAABAAAADg==", - "AAAAAAAAAAAAAAAHYnl0ZXNfbgAAAAABAAAAAAAAAAdieXRlc19uAAAAA+4AAAAJAAAAAQAAA+4AAAAJ", - "AAAAAAAAAAAAAAAEY2FyZAAAAAEAAAAAAAAABGNhcmQAAAfQAAAACVJveWFsQ2FyZAAAAAAAAAEAAAfQAAAACVJveWFsQ2FyZAAAAA==", - "AAAAAAAAAAAAAAAHYm9vbGVhbgAAAAABAAAAAAAAAAdib29sZWFuAAAAAAEAAAABAAAAAQ==", - "AAAAAAAAABdOZWdhdGVzIGEgYm9vbGVhbiB2YWx1ZQAAAAADbm90AAAAAAEAAAAAAAAAB2Jvb2xlYW4AAAAAAQAAAAEAAAAB", - "AAAAAAAAAAAAAAAEaTEyOAAAAAEAAAAAAAAABGkxMjgAAAALAAAAAQAAAAs=", - "AAAAAAAAAAAAAAAEdTEyOAAAAAEAAAAAAAAABHUxMjgAAAAKAAAAAQAAAAo=", - "AAAAAAAAAAAAAAAKbXVsdGlfYXJncwAAAAAAAgAAAAAAAAABYQAAAAAAAAQAAAAAAAAAAWIAAAAAAAABAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAADbWFwAAAAAAEAAAAAAAAAA21hcAAAAAPsAAAABAAAAAEAAAABAAAD7AAAAAQAAAAB", - "AAAAAAAAAAAAAAADdmVjAAAAAAEAAAAAAAAAA3ZlYwAAAAPqAAAABAAAAAEAAAPqAAAABA==", - "AAAAAAAAAAAAAAAFdHVwbGUAAAAAAAABAAAAAAAAAAV0dXBsZQAAAAAAA+0AAAACAAAAEQAAAAQAAAABAAAD7QAAAAIAAAARAAAABA==", - "AAAAAAAAAB9FeGFtcGxlIG9mIGFuIG9wdGlvbmFsIGFyZ3VtZW50AAAAAAZvcHRpb24AAAAAAAEAAAAAAAAABm9wdGlvbgAAAAAD6AAAAAQAAAABAAAD6AAAAAQ=", - "AAAAAAAAAAAAAAAEdTI1NgAAAAEAAAAAAAAABHUyNTYAAAAMAAAAAQAAAAw=", - "AAAAAAAAAAAAAAAEaTI1NgAAAAEAAAAAAAAABGkyNTYAAAANAAAAAQAAAA0=", - "AAAAAAAAAAAAAAAGc3RyaW5nAAAAAAABAAAAAAAAAAZzdHJpbmcAAAAAABAAAAABAAAAEA==", - "AAAAAAAAAAAAAAAMdHVwbGVfc3RydWt0AAAAAQAAAAAAAAAMdHVwbGVfc3RydWt0AAAH0AAAAAtUdXBsZVN0cnVjdAAAAAABAAAH0AAAAAtUdXBsZVN0cnVjdAA=" - ]); - } - parsers = { - hello: (result) => this.spec.funcResToNative("hello", result), - woid: () => { }, - val: (result) => this.spec.funcResToNative("val", result), - u32FailOnEven: (result) => { - if (result instanceof assembled_tx_js_1.Err) - return result; - return new assembled_tx_js_1.Ok(this.spec.funcResToNative("u32_fail_on_even", result)); - }, - u32: (result) => this.spec.funcResToNative("u32_", result), - i32: (result) => this.spec.funcResToNative("i32_", result), - i64: (result) => this.spec.funcResToNative("i64_", result), - struktHel: (result) => this.spec.funcResToNative("strukt_hel", result), - strukt: (result) => this.spec.funcResToNative("strukt", result), - simple: (result) => this.spec.funcResToNative("simple", result), - complex: (result) => this.spec.funcResToNative("complex", result), - addresse: (result) => this.spec.funcResToNative("addresse", result), - bytes: (result) => this.spec.funcResToNative("bytes", result), - bytesN: (result) => this.spec.funcResToNative("bytes_n", result), - card: (result) => this.spec.funcResToNative("card", result), - boolean: (result) => this.spec.funcResToNative("boolean", result), - not: (result) => this.spec.funcResToNative("not", result), - i128: (result) => this.spec.funcResToNative("i128", result), - u128: (result) => this.spec.funcResToNative("u128", result), - multiArgs: (result) => this.spec.funcResToNative("multi_args", result), - map: (result) => this.spec.funcResToNative("map", result), - vec: (result) => this.spec.funcResToNative("vec", result), - tuple: (result) => this.spec.funcResToNative("tuple", result), - option: (result) => this.spec.funcResToNative("option", result), - u256: (result) => this.spec.funcResToNative("u256", result), - i256: (result) => this.spec.funcResToNative("i256", result), - string: (result) => this.spec.funcResToNative("string", result), - tupleStrukt: (result) => this.spec.funcResToNative("tuple_strukt", result) - }; - txFromJSON = (json) => { - const { method, ...tx } = JSON.parse(json); - return assembled_tx_js_1.AssembledTransaction.fromJSON({ - ...this.options, - method, - parseResultXdr: this.parsers[method], - }, tx); - }; - fromJSON = { - hello: (this.txFromJSON), - woid: (this.txFromJSON), - val: (this.txFromJSON), - u32FailOnEven: (this.txFromJSON), - u32: (this.txFromJSON), - i32: (this.txFromJSON), - i64: (this.txFromJSON), - struktHel: (this.txFromJSON), - strukt: (this.txFromJSON), - simple: (this.txFromJSON), - complex: (this.txFromJSON), - addresse: (this.txFromJSON), - bytes: (this.txFromJSON), - bytesN: (this.txFromJSON), - card: (this.txFromJSON), - boolean: (this.txFromJSON), - not: (this.txFromJSON), - i128: (this.txFromJSON), - u128: (this.txFromJSON), - multiArgs: (this.txFromJSON), - map: (this.txFromJSON), - vec: (this.txFromJSON), - tuple: (this.txFromJSON), - option: (this.txFromJSON), - u256: (this.txFromJSON), - i256: (this.txFromJSON), - string: (this.txFromJSON), - tupleStrukt: (this.txFromJSON) - }; - /** -* Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - hello = async ({ hello }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'hello', - args: this.spec.funcArgsToScVals("hello", { hello }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['hello'], - }); - }; - /** -* Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - woid = async (options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'woid', - args: this.spec.funcArgsToScVals("woid", {}), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['woid'], - }); - }; - /** -* Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - val = async (options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'val', - args: this.spec.funcArgsToScVals("val", {}), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['val'], - }); - }; - /** -* Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32FailOnEven = async ({ u32_ }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'u32_fail_on_even', - args: this.spec.funcArgsToScVals("u32_fail_on_even", { u32_ }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['u32FailOnEven'], - }); - }; - /** -* Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32 = async ({ u32_ }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'u32_', - args: this.spec.funcArgsToScVals("u32_", { u32_ }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['u32'], - }); - }; - /** -* Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i32 = async ({ i32_ }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'i32_', - args: this.spec.funcArgsToScVals("i32_", { i32_ }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['i32'], - }); - }; - /** -* Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i64 = async ({ i64_ }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'i64_', - args: this.spec.funcArgsToScVals("i64_", { i64_ }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['i64'], - }); - }; - /** -* Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct -*/ - struktHel = async ({ strukt }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'strukt_hel', - args: this.spec.funcArgsToScVals("strukt_hel", { strukt }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['struktHel'], - }); - }; - /** -* Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - strukt = async ({ strukt }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'strukt', - args: this.spec.funcArgsToScVals("strukt", { strukt }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['strukt'], - }); - }; - /** -* Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - simple = async ({ simple }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'simple', - args: this.spec.funcArgsToScVals("simple", { simple }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['simple'], - }); - }; - /** -* Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - complex = async ({ complex }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'complex', - args: this.spec.funcArgsToScVals("complex", { complex }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['complex'], - }); - }; - /** -* Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - addresse = async ({ addresse }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'addresse', - args: this.spec.funcArgsToScVals("addresse", { addresse: new stellar_sdk_1.Address(addresse) }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['addresse'], - }); - }; - /** -* Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytes = async ({ bytes }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'bytes', - args: this.spec.funcArgsToScVals("bytes", { bytes }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['bytes'], - }); - }; - /** -* Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytesN = async ({ bytes_n }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'bytes_n', - args: this.spec.funcArgsToScVals("bytes_n", { bytes_n }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['bytesN'], - }); - }; - /** -* Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - card = async ({ card }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'card', - args: this.spec.funcArgsToScVals("card", { card }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['card'], - }); - }; - /** -* Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - boolean = async ({ boolean }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'boolean', - args: this.spec.funcArgsToScVals("boolean", { boolean }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['boolean'], - }); - }; - /** -* Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value -*/ - not = async ({ boolean }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'not', - args: this.spec.funcArgsToScVals("not", { boolean }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['not'], - }); - }; - /** -* Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i128 = async ({ i128 }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'i128', - args: this.spec.funcArgsToScVals("i128", { i128 }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['i128'], - }); - }; - /** -* Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u128 = async ({ u128 }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'u128', - args: this.spec.funcArgsToScVals("u128", { u128 }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['u128'], - }); - }; - /** -* Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - multiArgs = async ({ a, b }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'multi_args', - args: this.spec.funcArgsToScVals("multi_args", { a, b }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['multiArgs'], - }); - }; - /** -* Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - map = async ({ map }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'map', - args: this.spec.funcArgsToScVals("map", { map }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['map'], - }); - }; - /** -* Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - vec = async ({ vec }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'vec', - args: this.spec.funcArgsToScVals("vec", { vec }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['vec'], - }); - }; - /** -* Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tuple = async ({ tuple }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'tuple', - args: this.spec.funcArgsToScVals("tuple", { tuple }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['tuple'], - }); - }; - /** -* Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument -*/ - option = async ({ option }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'option', - args: this.spec.funcArgsToScVals("option", { option }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['option'], - }); - }; - /** -* Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u256 = async ({ u256 }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'u256', - args: this.spec.funcArgsToScVals("u256", { u256 }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['u256'], - }); - }; - /** -* Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i256 = async ({ i256 }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'i256', - args: this.spec.funcArgsToScVals("i256", { i256 }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['i256'], - }); - }; - /** -* Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - string = async ({ string }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'string', - args: this.spec.funcArgsToScVals("string", { string }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['string'], - }); - }; - /** -* Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tupleStrukt = async ({ tuple_strukt }, options = {}) => { - return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ - method: 'tuple_strukt', - args: this.spec.funcArgsToScVals("tuple_strukt", { tuple_strukt }), - ...options, - ...this.options, - errorTypes: exports.Errors, - parseResultXdr: this.parsers['tupleStrukt'], - }); - }; -} -exports.Contract = Contract; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.d.ts deleted file mode 100644 index fc6b21d5..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -declare let responseTypes: 'simulated' | 'full' | undefined; -export type ResponseTypes = typeof responseTypes; -export type XDR_BASE64 = string; -export interface Wallet { - isConnected: () => Promise; - isAllowed: () => Promise; - getUserInfo: () => Promise<{ - publicKey?: string; - }>; - signTransaction: (tx: XDR_BASE64, opts?: { - network?: string; - networkPassphrase?: string; - accountToSign?: string; - }) => Promise; - signAuthEntry: (entryXdr: XDR_BASE64, opts?: { - accountToSign?: string; - }) => Promise; -} -export type ClassOptions = { - contractId: string; - networkPassphrase: string; - rpcUrl: string; - errorTypes?: Record; - /** - * A Wallet interface, such as Freighter, that has the methods `isConnected`, `isAllowed`, `getUserInfo`, and `signTransaction`. If not provided, will attempt to import and use Freighter. Example: - * - * @example - * ```ts - * import freighter from "@stellar/freighter-api"; - * import { Contract } from "test_custom_types"; - * const contract = new Contract({ - * …, - * wallet: freighter, - * }) - * ``` - */ - wallet?: Wallet; -}; -export type MethodOptions = { - /** - * The fee to pay for the transaction. Default: soroban-sdk's BASE_FEE ('100') - */ - fee?: number; -}; -export {}; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.js deleted file mode 100644 index 6d483ac3..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -// defined this way so typeahead shows full union, not named alias -let responseTypes; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.d.ts deleted file mode 100644 index 1d5e6f5e..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.d.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Account, Address, Operation, SorobanRpc, xdr } from "@stellar/stellar-sdk"; -import type { Memo, MemoType, Transaction } from "@stellar/stellar-sdk"; -import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; -export type Tx = Transaction, Operation[]>; -export declare class ExpiredStateError extends Error { -} -export declare class NeedsMoreSignaturesError extends Error { -} -export declare class WalletDisconnectedError extends Error { -} -export declare class SendResultOnlyError extends Error { -} -export declare class SendFailedError extends Error { -} -export declare class NoUnsignedNonInvokerAuthEntriesError extends Error { -} -type SendTx = SorobanRpc.Api.SendTransactionResponse; -type GetTx = SorobanRpc.Api.GetTransactionResponse; -export type u32 = number; -export type i32 = number; -export type u64 = bigint; -export type i64 = bigint; -export type u128 = bigint; -export type i128 = bigint; -export type u256 = bigint; -export type i256 = bigint; -export type Option = T | undefined; -export type Typepoint = bigint; -export type Duration = bigint; -export { Address }; -export interface Error_ { - message: string; -} -export interface Result { - unwrap(): T; - unwrapErr(): E; - isOk(): boolean; - isErr(): boolean; -} -export declare class Ok implements Result { - readonly value: T; - constructor(value: T); - unwrapErr(): E; - unwrap(): T; - isOk(): boolean; - isErr(): boolean; -} -export declare class Err implements Result { - readonly error: E; - constructor(error: E); - unwrapErr(): E; - unwrap(): never; - isOk(): boolean; - isErr(): boolean; -} -export declare const contractErrorPattern: RegExp; -type AssembledTransactionOptions = MethodOptions & ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal | Err) => T; -}; -export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; -export declare class AssembledTransaction { - options: AssembledTransactionOptions; - raw: Tx; - private simulation?; - private simulationResult?; - private simulationTransactionData?; - private server; - toJSON(): string; - static fromJSON(options: Omit, 'args'>, { tx, simulationResult, simulationTransactionData }: { - tx: XDR_BASE64; - simulationResult: { - auth: XDR_BASE64[]; - retval: XDR_BASE64; - }; - simulationTransactionData: XDR_BASE64; - }): AssembledTransaction; - private constructor(); - static fromSimulation(options: AssembledTransactionOptions): Promise>; - simulate: () => Promise; - get simulationData(): { - result: SorobanRpc.Api.SimulateHostFunctionResult; - transactionData: xdr.SorobanTransactionData; - }; - get result(): T; - parseError(errorMessage: string): Err | undefined; - getWallet: () => Promise; - getPublicKey: () => Promise; - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount: () => Promise; - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend: ({ secondsToWait, force }?: { - /** - * Wait `secondsToWait` seconds (default: 10) for both the transaction to SEND successfully (will keep trying if the server returns `TRY_AGAIN_LATER`), as well as for the transaction to COMPLETE (will keep checking if the server returns `PENDING`). - */ - secondsToWait?: number | undefined; - /** - * If `true`, sign and send the transaction even if it is a read call. - */ - force?: boolean | undefined; - }) => Promise>; - getStorageExpiration: () => Promise; - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy: ({ includeAlreadySigned, }?: { - /** - * Whether or not to include auth entries that have already been signed. Default: false - */ - includeAlreadySigned?: boolean | undefined; - }) => Promise; - preImageFor(entry: xdr.SorobanAuthorizationEntry, signatureExpirationLedger: number): xdr.HashIdPreimage; - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries: (expiration?: number | Promise) => Promise; - get isReadCall(): boolean; - hasRealInvoker: () => Promise; -} -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -declare class SentTransaction { - options: AssembledTransactionOptions; - assembled: AssembledTransaction; - server: SorobanRpc.Server; - signed: Tx; - sendTransactionResponse?: SendTx; - sendTransactionResponseAll?: SendTx[]; - getTransactionResponse?: GetTx; - getTransactionResponseAll?: GetTx[]; - constructor(options: AssembledTransactionOptions, assembled: AssembledTransaction); - static init: (options: AssembledTransactionOptions, assembled: AssembledTransaction, secondsToWait?: number) => Promise>; - private send; - get result(): T; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.js deleted file mode 100644 index 79b7c01d..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.js +++ /dev/null @@ -1,450 +0,0 @@ -import { Account, Address, Contract, Operation, SorobanRpc, StrKey, TimeoutInfinite, TransactionBuilder, authorizeEntry, hash, xdr, BASE_FEE, } from "@stellar/stellar-sdk"; -import { Buffer } from "buffer"; -export class ExpiredStateError extends Error { -} -export class NeedsMoreSignaturesError extends Error { -} -export class WalletDisconnectedError extends Error { -} -export class SendResultOnlyError extends Error { -} -export class SendFailedError extends Error { -} -export class NoUnsignedNonInvokerAuthEntriesError extends Error { -} -export { Address }; -; -; -export class Ok { - value; - constructor(value) { - this.value = value; - } - unwrapErr() { - throw new Error('No error'); - } - unwrap() { - return this.value; - } - isOk() { - return true; - } - isErr() { - return !this.isOk(); - } -} -export class Err { - error; - constructor(error) { - this.error = error; - } - unwrapErr() { - return this.error; - } - unwrap() { - throw new Error(this.error.message); - } - isOk() { - return false; - } - isErr() { - return !this.isOk(); - } -} -export const contractErrorPattern = /Error\(Contract, #(\d+)\)/; -export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; -export class AssembledTransaction { - options; - raw; - simulation; - simulationResult; - simulationTransactionData; - server; - toJSON() { - return JSON.stringify({ - method: this.options.method, - tx: this.raw?.toXDR(), - simulationResult: { - auth: this.simulationData.result.auth.map(a => a.toXDR('base64')), - retval: this.simulationData.result.retval.toXDR('base64'), - }, - simulationTransactionData: this.simulationData.transactionData.toXDR('base64'), - }); - } - static fromJSON(options, { tx, simulationResult, simulationTransactionData }) { - const txn = new AssembledTransaction(options); - txn.raw = TransactionBuilder.fromXDR(tx, options.networkPassphrase); - txn.simulationResult = { - auth: simulationResult.auth.map(a => xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64')), - retval: xdr.ScVal.fromXDR(simulationResult.retval, 'base64'), - }; - txn.simulationTransactionData = xdr.SorobanTransactionData.fromXDR(simulationTransactionData, 'base64'); - return txn; - } - constructor(options) { - this.options = options; - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - } - static async fromSimulation(options) { - const tx = new AssembledTransaction(options); - const contract = new Contract(options.contractId); - tx.raw = new TransactionBuilder(await tx.getAccount(), { - fee: options.fee?.toString(10) ?? BASE_FEE, - networkPassphrase: options.networkPassphrase, - }) - .addOperation(contract.call(options.method, ...(options.args ?? []))) - .setTimeout(TimeoutInfinite) - .build(); - return await tx.simulate(); - } - simulate = async () => { - if (!this.raw) - throw new Error('Transaction has not yet been assembled'); - this.simulation = await this.server.simulateTransaction(this.raw); - if (SorobanRpc.Api.isSimulationSuccess(this.simulation)) { - this.raw = SorobanRpc.assembleTransaction(this.raw, this.simulation).build(); - } - return this; - }; - get simulationData() { - if (this.simulationResult && this.simulationTransactionData) { - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - }; - } - // else, we know we just did the simulation on this machine - const simulation = this.simulation; - if (SorobanRpc.Api.isSimulationError(simulation)) { - throw new Error(`Transaction simulation failed: "${simulation.error}"`); - } - if (SorobanRpc.Api.isSimulationRestore(simulation)) { - throw new ExpiredStateError(`You need to restore some contract state before you can invoke this method. ${JSON.stringify(simulation, null, 2)}`); - } - if (!simulation.result) { - throw new Error(`Expected an invocation simulation, but got no 'result' field. Simulation: ${JSON.stringify(simulation, null, 2)}`); - } - // add to object for serialization & deserialization - this.simulationResult = simulation.result; - this.simulationTransactionData = simulation.transactionData.build(); - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - }; - } - get result() { - try { - return this.options.parseResultXdr(this.simulationData.result.retval); - } - catch (e) { - let err = this.parseError(e.toString()); - if (err) - return err; - throw e; - } - } - parseError(errorMessage) { - if (!this.options.errorTypes) - return; - const match = errorMessage.match(contractErrorPattern); - if (!match) - return; - let i = parseInt(match[1], 10); - let err = this.options.errorTypes[i]; - if (err) - return new Err(err); - } - getWallet = async () => { - return this.options.wallet ?? (await import("@stellar/freighter-api")).default; - }; - getPublicKey = async () => { - const wallet = await this.getWallet(); - if (await wallet.isConnected() && await wallet.isAllowed()) { - return (await wallet.getUserInfo()).publicKey; - } - }; - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount = async () => { - const publicKey = await this.getPublicKey(); - return publicKey - ? await this.server.getAccount(publicKey) - : new Account(NULL_ACCOUNT, "0"); - }; - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend = async ({ secondsToWait = 10, force = false } = {}) => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated'); - } - if (!force && this.isReadCall) { - throw new Error('This is a read call. It requires no signature or sending. Use `force: true` to sign and send anyway.'); - } - if (!await this.hasRealInvoker()) { - throw new WalletDisconnectedError('Wallet is not connected'); - } - if (this.raw.source !== (await this.getAccount()).accountId()) { - throw new Error(`You must submit the transaction with the account that originally created it. Please switch to the wallet with "${this.raw.source}" as its public key.`); - } - if ((await this.needsNonInvokerSigningBy()).length) { - throw new NeedsMoreSignaturesError('Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.'); - } - return await SentTransaction.init(this.options, this, secondsToWait); - }; - getStorageExpiration = async () => { - const entryRes = await this.server.getLedgerEntries(new Contract(this.options.contractId).getFootprint()); - if (!entryRes.entries || - !entryRes.entries.length || - !entryRes.entries[0].liveUntilLedgerSeq) - throw new Error('failed to get ledger entry'); - return entryRes.entries[0].liveUntilLedgerSeq; - }; - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy = async ({ includeAlreadySigned = false, } = {}) => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated'); - } - // We expect that any transaction constructed by these libraries has a - // single operation, which is an InvokeHostFunction operation. The host - // function being invoked is the contract method call. - if (!("operations" in this.raw)) { - throw new Error(`Unexpected Transaction type; no operations: ${JSON.stringify(this.raw)}`); - } - const rawInvokeHostFunctionOp = this.raw - .operations[0]; - return [...new Set((rawInvokeHostFunctionOp.auth ?? []).filter(entry => entry.credentials().switch() === - xdr.SorobanCredentialsType.sorobanCredentialsAddress() && - (includeAlreadySigned || - entry.credentials().address().signature().switch().name === 'scvVoid')).map(entry => StrKey.encodeEd25519PublicKey(entry.credentials().address().address().accountId().ed25519())))]; - }; - preImageFor(entry, signatureExpirationLedger) { - const addrAuth = entry.credentials().address(); - return xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(new xdr.HashIdPreimageSorobanAuthorization({ - networkId: hash(Buffer.from(this.options.networkPassphrase)), - nonce: addrAuth.nonce(), - invocation: entry.rootInvocation(), - signatureExpirationLedger, - })); - } - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries = async ( - /** - * When to set each auth entry to expire. Could be any number of blocks in - * the future. Can be supplied as a promise or a raw number. Default: - * contract's current `persistent` storage expiration date/ledger - * number/block. - */ - expiration = this.getStorageExpiration()) => { - if (!this.raw) - throw new Error('Transaction has not yet been assembled or simulated'); - const needsNonInvokerSigningBy = await this.needsNonInvokerSigningBy(); - if (!needsNonInvokerSigningBy) - throw new NoUnsignedNonInvokerAuthEntriesError('No unsigned non-invoker auth entries; maybe you already signed?'); - const publicKey = await this.getPublicKey(); - if (!publicKey) - throw new Error('Could not get public key from wallet; maybe Freighter is not signed in?'); - if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) - throw new Error(`No auth entries for public key "${publicKey}"`); - const wallet = await this.getWallet(); - const rawInvokeHostFunctionOp = this.raw - .operations[0]; - const authEntries = rawInvokeHostFunctionOp.auth ?? []; - for (const [i, entry] of authEntries.entries()) { - if (entry.credentials().switch() !== - xdr.SorobanCredentialsType.sorobanCredentialsAddress()) { - // if the invoker/source account, then the entry doesn't need explicit - // signature, since the tx envelope is already signed by the source - // account, so only check for sorobanCredentialsAddress - continue; - } - const pk = StrKey.encodeEd25519PublicKey(entry.credentials().address().address().accountId().ed25519()); - // this auth entry needs to be signed by a different account - // (or maybe already was!) - if (pk !== publicKey) - continue; - authEntries[i] = await authorizeEntry(entry, async (preimage) => Buffer.from(await wallet.signAuthEntry(preimage.toXDR('base64')), 'base64'), await expiration, this.options.networkPassphrase); - } - }; - get isReadCall() { - const authsCount = this.simulationData.result.auth.length; - const writeLength = this.simulationData.transactionData.resources().footprint().readWrite().length; - return (authsCount === 0) && (writeLength === 0); - } - hasRealInvoker = async () => { - const account = await this.getAccount(); - return account.accountId() !== NULL_ACCOUNT; - }; -} -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -class SentTransaction { - options; - assembled; - server; - signed; - sendTransactionResponse; - sendTransactionResponseAll; - getTransactionResponse; - getTransactionResponseAll; - constructor(options, assembled) { - this.options = options; - this.assembled = assembled; - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - this.assembled = assembled; - } - static init = async (options, assembled, secondsToWait = 10) => { - const tx = new SentTransaction(options, assembled); - return await tx.send(secondsToWait); - }; - send = async (secondsToWait = 10) => { - const wallet = await this.assembled.getWallet(); - this.sendTransactionResponseAll = await withExponentialBackoff(async (previousFailure) => { - if (previousFailure) { - // Increment transaction sequence number and resimulate before trying again - // Soroban transaction can only have 1 operation - const op = this.assembled.raw.operations[0]; - this.assembled.raw = new TransactionBuilder(await this.assembled.getAccount(), { - fee: this.assembled.raw.fee, - networkPassphrase: this.options.networkPassphrase, - }) - .setTimeout(TimeoutInfinite) - .addOperation(Operation.invokeHostFunction({ ...op, auth: op.auth ?? [] })) - .build(); - await this.assembled.simulate(); - } - const signature = await wallet.signTransaction(this.assembled.raw.toXDR(), { - networkPassphrase: this.options.networkPassphrase, - }); - this.signed = TransactionBuilder.fromXDR(signature, this.options.networkPassphrase); - return this.server.sendTransaction(this.signed); - }, resp => resp.status !== "PENDING", secondsToWait); - this.sendTransactionResponse = this.sendTransactionResponseAll[this.sendTransactionResponseAll.length - 1]; - if (this.sendTransactionResponse.status !== "PENDING") { - throw new Error(`Tried to resubmit transaction for ${secondsToWait} seconds, but it's still failing. ` + - `All attempts: ${JSON.stringify(this.sendTransactionResponseAll, null, 2)}`); - } - const { hash } = this.sendTransactionResponse; - this.getTransactionResponseAll = await withExponentialBackoff(() => this.server.getTransaction(hash), resp => resp.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND, secondsToWait); - this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1]; - if (this.getTransactionResponse.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND) { - console.error(`Waited ${secondsToWait} seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Sent transaction: ${JSON.stringify(this.sendTransactionResponse, null, 2)}\n` + - `All attempts to get the result: ${JSON.stringify(this.getTransactionResponseAll, null, 2)}`); - } - return this; - }; - get result() { - // 1. check if transaction was submitted and awaited with `getTransaction` - if ("getTransactionResponse" in this && - this.getTransactionResponse) { - // getTransactionResponse has a `returnValue` field unless it failed - if ("returnValue" in this.getTransactionResponse) { - return this.options.parseResultXdr(this.getTransactionResponse.returnValue); - } - // if "returnValue" not present, the transaction failed; return without parsing the result - throw new Error("Transaction failed! Cannot parse result."); - } - // 2. otherwise, maybe it was merely sent with `sendTransaction` - if (this.sendTransactionResponse) { - const errorResult = this.sendTransactionResponse.errorResult?.result(); - if (errorResult) { - throw new SendFailedError(`Transaction simulation looked correct, but attempting to send the transaction failed. Check \`simulation\` and \`sendTransactionResponseAll\` to troubleshoot. Decoded \`sendTransactionResponse.errorResultXdr\`: ${errorResult}`); - } - throw new SendResultOnlyError(`Transaction was sent to the network, but not yet awaited. No result to show. Await transaction completion with \`getTransaction(sendTransactionResponse.hash)\``); - } - // 3. finally, if neither of those are present, throw an error - throw new Error(`Sending transaction failed: ${JSON.stringify(this.assembled)}`); - } -} -/** - * Keep calling a `fn` for `secondsToWait` seconds, if `keepWaitingIf` is true. - * Returns an array of all attempts to call the function. - */ -async function withExponentialBackoff(fn, keepWaitingIf, secondsToWait, exponentialFactor = 1.5, verbose = false) { - const attempts = []; - let count = 0; - attempts.push(await fn()); - if (!keepWaitingIf(attempts[attempts.length - 1])) - return attempts; - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf(); - let waitTime = 1000; - let totalWaitTime = waitTime; - while (Date.now() < waitUntil && keepWaitingIf(attempts[attempts.length - 1])) { - count++; - // Wait a beat - if (verbose) { - console.info(`Waiting ${waitTime}ms before trying again (bringing the total wait time to ${totalWaitTime}ms so far, of total ${secondsToWait * 1000}ms)`); - } - await new Promise(res => setTimeout(res, waitTime)); - // Exponential backoff - waitTime = waitTime * exponentialFactor; - if (new Date(Date.now() + waitTime).valueOf() > waitUntil) { - waitTime = waitUntil - Date.now(); - if (verbose) { - console.info(`was gonna wait too long; new waitTime: ${waitTime}ms`); - } - } - totalWaitTime = waitTime + totalWaitTime; - // Try again - attempts.push(await fn(attempts[attempts.length - 1])); - if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { - console.info(`${count}. Called ${fn}; ${attempts.length} prev attempts. Most recent: ${JSON.stringify(attempts[attempts.length - 1], null, 2)}`); - } - } - return attempts; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.d.ts deleted file mode 100644 index 66d3d595..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.d.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { ContractSpec } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -import type { u32, i32, i64, i128, Option, Error_ } from './assembled-tx.js'; -import type { ClassOptions } from './method-options.js'; -export * from './assembled-tx.js'; -export * from './method-options.js'; -export declare const networks: { - readonly futurenet: { - readonly networkPassphrase: "Test SDF Future Network ; October 2022"; - readonly contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK"; - }; -}; -/** - This is from the rust doc above the struct Test - */ -export interface Test { - /** - - */ - a: u32; - /** - - */ - b: boolean; - /** - - */ - c: string; -} -/** - - */ -export type SimpleEnum = { - tag: "First"; - values: void; -} | { - tag: "Second"; - values: void; -} | { - tag: "Third"; - values: void; -}; -/** - - */ -export declare enum RoyalCard { - Jack = 11, - Queen = 12, - King = 13 -} -/** - - */ -export type TupleStruct = readonly [Test, SimpleEnum]; -/** - - */ -export type ComplexEnum = { - tag: "Struct"; - values: readonly [Test]; -} | { - tag: "Tuple"; - values: readonly [TupleStruct]; -} | { - tag: "Enum"; - values: readonly [SimpleEnum]; -} | { - tag: "Asset"; - values: readonly [string, i128]; -} | { - tag: "Void"; - values: void; -}; -/** - - */ -export declare const Errors: { - 1: { - message: string; - }; -}; -export declare class Contract { - readonly options: ClassOptions; - spec: ContractSpec; - constructor(options: ClassOptions); - private readonly parsers; - private txFromJSON; - readonly fromJSON: { - hello: (json: string) => AssembledTransaction; - woid: (json: string) => AssembledTransaction; - val: (json: string) => AssembledTransaction; - u32FailOnEven: (json: string) => AssembledTransaction | Ok>; - u32: (json: string) => AssembledTransaction; - i32: (json: string) => AssembledTransaction; - i64: (json: string) => AssembledTransaction; - struktHel: (json: string) => AssembledTransaction; - strukt: (json: string) => AssembledTransaction; - simple: (json: string) => AssembledTransaction; - complex: (json: string) => AssembledTransaction; - addresse: (json: string) => AssembledTransaction; - bytes: (json: string) => AssembledTransaction; - bytesN: (json: string) => AssembledTransaction; - card: (json: string) => AssembledTransaction; - boolean: (json: string) => AssembledTransaction; - not: (json: string) => AssembledTransaction; - i128: (json: string) => AssembledTransaction; - u128: (json: string) => AssembledTransaction; - multiArgs: (json: string) => AssembledTransaction; - map: (json: string) => AssembledTransaction>; - vec: (json: string) => AssembledTransaction; - tuple: (json: string) => AssembledTransaction; - option: (json: string) => AssembledTransaction>; - u256: (json: string) => AssembledTransaction; - i256: (json: string) => AssembledTransaction; - string: (json: string) => AssembledTransaction; - tupleStrukt: (json: string) => AssembledTransaction; - }; - /** -* Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - hello: ({ hello }: { - hello: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - woid: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - val: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32FailOnEven: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise | Ok>>; - /** -* Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i32: ({ i32_ }: { - i32_: i32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i64: ({ i64_ }: { - i64_: i64; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct -*/ - struktHel: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - strukt: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - simple: ({ simple }: { - simple: SimpleEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - complex: ({ complex }: { - complex: ComplexEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - addresse: ({ addresse }: { - addresse: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytes: ({ bytes }: { - bytes: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytesN: ({ bytes_n }: { - bytes_n: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - card: ({ card }: { - card: RoyalCard; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - boolean: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value -*/ - not: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i128: ({ i128 }: { - i128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u128: ({ u128 }: { - u128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - multiArgs: ({ a, b }: { - a: u32; - b: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - map: ({ map }: { - map: Map; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - vec: ({ vec }: { - vec: Array; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tuple: ({ tuple }: { - tuple: readonly [string, u32]; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument -*/ - option: ({ option }: { - option: Option; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u256: ({ u256 }: { - u256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i256: ({ i256 }: { - i256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - string: ({ string }: { - string: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tupleStrukt: ({ tuple_strukt }: { - tuple_strukt: TupleStruct; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.js deleted file mode 100644 index b97cff55..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.js +++ /dev/null @@ -1,509 +0,0 @@ -import { ContractSpec, Address } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -export * from './assembled-tx.js'; -export * from './method-options.js'; -if (typeof window !== 'undefined') { - //@ts-ignore Buffer exists - window.Buffer = window.Buffer || Buffer; -} -export const networks = { - futurenet: { - networkPassphrase: "Test SDF Future Network ; October 2022", - contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK", - } -}; -/** - - */ -export var RoyalCard; -(function (RoyalCard) { - RoyalCard[RoyalCard["Jack"] = 11] = "Jack"; - RoyalCard[RoyalCard["Queen"] = 12] = "Queen"; - RoyalCard[RoyalCard["King"] = 13] = "King"; -})(RoyalCard || (RoyalCard = {})); -/** - - */ -export const Errors = { - 1: { message: "Please provide an odd number" } -}; -export class Contract { - options; - spec; - constructor(options) { - this.options = options; - this.spec = new ContractSpec([ - "AAAAAQAAAC9UaGlzIGlzIGZyb20gdGhlIHJ1c3QgZG9jIGFib3ZlIHRoZSBzdHJ1Y3QgVGVzdAAAAAAAAAAABFRlc3QAAAADAAAAAAAAAAFhAAAAAAAABAAAAAAAAAABYgAAAAAAAAEAAAAAAAAAAWMAAAAAAAAR", - "AAAAAgAAAAAAAAAAAAAAClNpbXBsZUVudW0AAAAAAAMAAAAAAAAAAAAAAAVGaXJzdAAAAAAAAAAAAAAAAAAABlNlY29uZAAAAAAAAAAAAAAAAAAFVGhpcmQAAAA=", - "AAAAAwAAAAAAAAAAAAAACVJveWFsQ2FyZAAAAAAAAAMAAAAAAAAABEphY2sAAAALAAAAAAAAAAVRdWVlbgAAAAAAAAwAAAAAAAAABEtpbmcAAAAN", - "AAAAAQAAAAAAAAAAAAAAC1R1cGxlU3RydWN0AAAAAAIAAAAAAAAAATAAAAAAAAfQAAAABFRlc3QAAAAAAAAAATEAAAAAAAfQAAAAClNpbXBsZUVudW0AAA==", - "AAAAAgAAAAAAAAAAAAAAC0NvbXBsZXhFbnVtAAAAAAUAAAABAAAAAAAAAAZTdHJ1Y3QAAAAAAAEAAAfQAAAABFRlc3QAAAABAAAAAAAAAAVUdXBsZQAAAAAAAAEAAAfQAAAAC1R1cGxlU3RydWN0AAAAAAEAAAAAAAAABEVudW0AAAABAAAH0AAAAApTaW1wbGVFbnVtAAAAAAABAAAAAAAAAAVBc3NldAAAAAAAAAIAAAATAAAACwAAAAAAAAAAAAAABFZvaWQ=", - "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAAQAAABxQbGVhc2UgcHJvdmlkZSBhbiBvZGQgbnVtYmVyAAAAD051bWJlck11c3RCZU9kZAAAAAAB", - "AAAAAAAAAAAAAAAFaGVsbG8AAAAAAAABAAAAAAAAAAVoZWxsbwAAAAAAABEAAAABAAAAEQ==", - "AAAAAAAAAAAAAAAEd29pZAAAAAAAAAAA", - "AAAAAAAAAAAAAAADdmFsAAAAAAAAAAABAAAAAA==", - "AAAAAAAAAAAAAAAQdTMyX2ZhaWxfb25fZXZlbgAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAA+kAAAAEAAAAAw==", - "AAAAAAAAAAAAAAAEdTMyXwAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAAEaTMyXwAAAAEAAAAAAAAABGkzMl8AAAAFAAAAAQAAAAU=", - "AAAAAAAAAAAAAAAEaTY0XwAAAAEAAAAAAAAABGk2NF8AAAAHAAAAAQAAAAc=", - "AAAAAAAAACxFeGFtcGxlIGNvbnRyYWN0IG1ldGhvZCB3aGljaCB0YWtlcyBhIHN0cnVjdAAAAApzdHJ1a3RfaGVsAAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAPqAAAAEQ==", - "AAAAAAAAAAAAAAAGc3RydWt0AAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAfQAAAABFRlc3Q=", - "AAAAAAAAAAAAAAAGc2ltcGxlAAAAAAABAAAAAAAAAAZzaW1wbGUAAAAAB9AAAAAKU2ltcGxlRW51bQAAAAAAAQAAB9AAAAAKU2ltcGxlRW51bQAA", - "AAAAAAAAAAAAAAAHY29tcGxleAAAAAABAAAAAAAAAAdjb21wbGV4AAAAB9AAAAALQ29tcGxleEVudW0AAAAAAQAAB9AAAAALQ29tcGxleEVudW0A", - "AAAAAAAAAAAAAAAIYWRkcmVzc2UAAAABAAAAAAAAAAhhZGRyZXNzZQAAABMAAAABAAAAEw==", - "AAAAAAAAAAAAAAAFYnl0ZXMAAAAAAAABAAAAAAAAAAVieXRlcwAAAAAAAA4AAAABAAAADg==", - "AAAAAAAAAAAAAAAHYnl0ZXNfbgAAAAABAAAAAAAAAAdieXRlc19uAAAAA+4AAAAJAAAAAQAAA+4AAAAJ", - "AAAAAAAAAAAAAAAEY2FyZAAAAAEAAAAAAAAABGNhcmQAAAfQAAAACVJveWFsQ2FyZAAAAAAAAAEAAAfQAAAACVJveWFsQ2FyZAAAAA==", - "AAAAAAAAAAAAAAAHYm9vbGVhbgAAAAABAAAAAAAAAAdib29sZWFuAAAAAAEAAAABAAAAAQ==", - "AAAAAAAAABdOZWdhdGVzIGEgYm9vbGVhbiB2YWx1ZQAAAAADbm90AAAAAAEAAAAAAAAAB2Jvb2xlYW4AAAAAAQAAAAEAAAAB", - "AAAAAAAAAAAAAAAEaTEyOAAAAAEAAAAAAAAABGkxMjgAAAALAAAAAQAAAAs=", - "AAAAAAAAAAAAAAAEdTEyOAAAAAEAAAAAAAAABHUxMjgAAAAKAAAAAQAAAAo=", - "AAAAAAAAAAAAAAAKbXVsdGlfYXJncwAAAAAAAgAAAAAAAAABYQAAAAAAAAQAAAAAAAAAAWIAAAAAAAABAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAADbWFwAAAAAAEAAAAAAAAAA21hcAAAAAPsAAAABAAAAAEAAAABAAAD7AAAAAQAAAAB", - "AAAAAAAAAAAAAAADdmVjAAAAAAEAAAAAAAAAA3ZlYwAAAAPqAAAABAAAAAEAAAPqAAAABA==", - "AAAAAAAAAAAAAAAFdHVwbGUAAAAAAAABAAAAAAAAAAV0dXBsZQAAAAAAA+0AAAACAAAAEQAAAAQAAAABAAAD7QAAAAIAAAARAAAABA==", - "AAAAAAAAAB9FeGFtcGxlIG9mIGFuIG9wdGlvbmFsIGFyZ3VtZW50AAAAAAZvcHRpb24AAAAAAAEAAAAAAAAABm9wdGlvbgAAAAAD6AAAAAQAAAABAAAD6AAAAAQ=", - "AAAAAAAAAAAAAAAEdTI1NgAAAAEAAAAAAAAABHUyNTYAAAAMAAAAAQAAAAw=", - "AAAAAAAAAAAAAAAEaTI1NgAAAAEAAAAAAAAABGkyNTYAAAANAAAAAQAAAA0=", - "AAAAAAAAAAAAAAAGc3RyaW5nAAAAAAABAAAAAAAAAAZzdHJpbmcAAAAAABAAAAABAAAAEA==", - "AAAAAAAAAAAAAAAMdHVwbGVfc3RydWt0AAAAAQAAAAAAAAAMdHVwbGVfc3RydWt0AAAH0AAAAAtUdXBsZVN0cnVjdAAAAAABAAAH0AAAAAtUdXBsZVN0cnVjdAA=" - ]); - } - parsers = { - hello: (result) => this.spec.funcResToNative("hello", result), - woid: () => { }, - val: (result) => this.spec.funcResToNative("val", result), - u32FailOnEven: (result) => { - if (result instanceof Err) - return result; - return new Ok(this.spec.funcResToNative("u32_fail_on_even", result)); - }, - u32: (result) => this.spec.funcResToNative("u32_", result), - i32: (result) => this.spec.funcResToNative("i32_", result), - i64: (result) => this.spec.funcResToNative("i64_", result), - struktHel: (result) => this.spec.funcResToNative("strukt_hel", result), - strukt: (result) => this.spec.funcResToNative("strukt", result), - simple: (result) => this.spec.funcResToNative("simple", result), - complex: (result) => this.spec.funcResToNative("complex", result), - addresse: (result) => this.spec.funcResToNative("addresse", result), - bytes: (result) => this.spec.funcResToNative("bytes", result), - bytesN: (result) => this.spec.funcResToNative("bytes_n", result), - card: (result) => this.spec.funcResToNative("card", result), - boolean: (result) => this.spec.funcResToNative("boolean", result), - not: (result) => this.spec.funcResToNative("not", result), - i128: (result) => this.spec.funcResToNative("i128", result), - u128: (result) => this.spec.funcResToNative("u128", result), - multiArgs: (result) => this.spec.funcResToNative("multi_args", result), - map: (result) => this.spec.funcResToNative("map", result), - vec: (result) => this.spec.funcResToNative("vec", result), - tuple: (result) => this.spec.funcResToNative("tuple", result), - option: (result) => this.spec.funcResToNative("option", result), - u256: (result) => this.spec.funcResToNative("u256", result), - i256: (result) => this.spec.funcResToNative("i256", result), - string: (result) => this.spec.funcResToNative("string", result), - tupleStrukt: (result) => this.spec.funcResToNative("tuple_strukt", result) - }; - txFromJSON = (json) => { - const { method, ...tx } = JSON.parse(json); - return AssembledTransaction.fromJSON({ - ...this.options, - method, - parseResultXdr: this.parsers[method], - }, tx); - }; - fromJSON = { - hello: (this.txFromJSON), - woid: (this.txFromJSON), - val: (this.txFromJSON), - u32FailOnEven: (this.txFromJSON), - u32: (this.txFromJSON), - i32: (this.txFromJSON), - i64: (this.txFromJSON), - struktHel: (this.txFromJSON), - strukt: (this.txFromJSON), - simple: (this.txFromJSON), - complex: (this.txFromJSON), - addresse: (this.txFromJSON), - bytes: (this.txFromJSON), - bytesN: (this.txFromJSON), - card: (this.txFromJSON), - boolean: (this.txFromJSON), - not: (this.txFromJSON), - i128: (this.txFromJSON), - u128: (this.txFromJSON), - multiArgs: (this.txFromJSON), - map: (this.txFromJSON), - vec: (this.txFromJSON), - tuple: (this.txFromJSON), - option: (this.txFromJSON), - u256: (this.txFromJSON), - i256: (this.txFromJSON), - string: (this.txFromJSON), - tupleStrukt: (this.txFromJSON) - }; - /** -* Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - hello = async ({ hello }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'hello', - args: this.spec.funcArgsToScVals("hello", { hello }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['hello'], - }); - }; - /** -* Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - woid = async (options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'woid', - args: this.spec.funcArgsToScVals("woid", {}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['woid'], - }); - }; - /** -* Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - val = async (options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'val', - args: this.spec.funcArgsToScVals("val", {}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['val'], - }); - }; - /** -* Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32FailOnEven = async ({ u32_ }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u32_fail_on_even', - args: this.spec.funcArgsToScVals("u32_fail_on_even", { u32_ }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u32FailOnEven'], - }); - }; - /** -* Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32 = async ({ u32_ }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u32_', - args: this.spec.funcArgsToScVals("u32_", { u32_ }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u32'], - }); - }; - /** -* Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i32 = async ({ i32_ }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i32_', - args: this.spec.funcArgsToScVals("i32_", { i32_ }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i32'], - }); - }; - /** -* Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i64 = async ({ i64_ }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i64_', - args: this.spec.funcArgsToScVals("i64_", { i64_ }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i64'], - }); - }; - /** -* Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct -*/ - struktHel = async ({ strukt }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'strukt_hel', - args: this.spec.funcArgsToScVals("strukt_hel", { strukt }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['struktHel'], - }); - }; - /** -* Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - strukt = async ({ strukt }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'strukt', - args: this.spec.funcArgsToScVals("strukt", { strukt }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['strukt'], - }); - }; - /** -* Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - simple = async ({ simple }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'simple', - args: this.spec.funcArgsToScVals("simple", { simple }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['simple'], - }); - }; - /** -* Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - complex = async ({ complex }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'complex', - args: this.spec.funcArgsToScVals("complex", { complex }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['complex'], - }); - }; - /** -* Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - addresse = async ({ addresse }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'addresse', - args: this.spec.funcArgsToScVals("addresse", { addresse: new Address(addresse) }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['addresse'], - }); - }; - /** -* Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytes = async ({ bytes }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'bytes', - args: this.spec.funcArgsToScVals("bytes", { bytes }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['bytes'], - }); - }; - /** -* Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytesN = async ({ bytes_n }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'bytes_n', - args: this.spec.funcArgsToScVals("bytes_n", { bytes_n }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['bytesN'], - }); - }; - /** -* Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - card = async ({ card }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'card', - args: this.spec.funcArgsToScVals("card", { card }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['card'], - }); - }; - /** -* Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - boolean = async ({ boolean }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'boolean', - args: this.spec.funcArgsToScVals("boolean", { boolean }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['boolean'], - }); - }; - /** -* Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value -*/ - not = async ({ boolean }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'not', - args: this.spec.funcArgsToScVals("not", { boolean }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['not'], - }); - }; - /** -* Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i128 = async ({ i128 }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i128', - args: this.spec.funcArgsToScVals("i128", { i128 }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i128'], - }); - }; - /** -* Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u128 = async ({ u128 }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u128', - args: this.spec.funcArgsToScVals("u128", { u128 }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u128'], - }); - }; - /** -* Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - multiArgs = async ({ a, b }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'multi_args', - args: this.spec.funcArgsToScVals("multi_args", { a, b }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['multiArgs'], - }); - }; - /** -* Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - map = async ({ map }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'map', - args: this.spec.funcArgsToScVals("map", { map }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['map'], - }); - }; - /** -* Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - vec = async ({ vec }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'vec', - args: this.spec.funcArgsToScVals("vec", { vec }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['vec'], - }); - }; - /** -* Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tuple = async ({ tuple }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'tuple', - args: this.spec.funcArgsToScVals("tuple", { tuple }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['tuple'], - }); - }; - /** -* Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument -*/ - option = async ({ option }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'option', - args: this.spec.funcArgsToScVals("option", { option }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['option'], - }); - }; - /** -* Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u256 = async ({ u256 }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u256', - args: this.spec.funcArgsToScVals("u256", { u256 }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u256'], - }); - }; - /** -* Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i256 = async ({ i256 }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i256', - args: this.spec.funcArgsToScVals("i256", { i256 }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i256'], - }); - }; - /** -* Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - string = async ({ string }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'string', - args: this.spec.funcArgsToScVals("string", { string }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['string'], - }); - }; - /** -* Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tupleStrukt = async ({ tuple_strukt }, options = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'tuple_strukt', - args: this.spec.funcArgsToScVals("tuple_strukt", { tuple_strukt }), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['tupleStrukt'], - }); - }; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.d.ts deleted file mode 100644 index fc6b21d5..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -declare let responseTypes: 'simulated' | 'full' | undefined; -export type ResponseTypes = typeof responseTypes; -export type XDR_BASE64 = string; -export interface Wallet { - isConnected: () => Promise; - isAllowed: () => Promise; - getUserInfo: () => Promise<{ - publicKey?: string; - }>; - signTransaction: (tx: XDR_BASE64, opts?: { - network?: string; - networkPassphrase?: string; - accountToSign?: string; - }) => Promise; - signAuthEntry: (entryXdr: XDR_BASE64, opts?: { - accountToSign?: string; - }) => Promise; -} -export type ClassOptions = { - contractId: string; - networkPassphrase: string; - rpcUrl: string; - errorTypes?: Record; - /** - * A Wallet interface, such as Freighter, that has the methods `isConnected`, `isAllowed`, `getUserInfo`, and `signTransaction`. If not provided, will attempt to import and use Freighter. Example: - * - * @example - * ```ts - * import freighter from "@stellar/freighter-api"; - * import { Contract } from "test_custom_types"; - * const contract = new Contract({ - * …, - * wallet: freighter, - * }) - * ``` - */ - wallet?: Wallet; -}; -export type MethodOptions = { - /** - * The fee to pay for the transaction. Default: soroban-sdk's BASE_FEE ('100') - */ - fee?: number; -}; -export {}; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.js deleted file mode 100644 index 00ad9d3c..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.js +++ /dev/null @@ -1,3 +0,0 @@ -// defined this way so typeahead shows full union, not named alias -let responseTypes; -export {}; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/package.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/package.json deleted file mode 100644 index 1632c2c4..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/package.json +++ /dev/null @@ -1 +0,0 @@ -{"type": "module"} \ No newline at end of file diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/assembled-tx.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/assembled-tx.d.ts deleted file mode 100644 index 1d5e6f5e..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/assembled-tx.d.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Account, Address, Operation, SorobanRpc, xdr } from "@stellar/stellar-sdk"; -import type { Memo, MemoType, Transaction } from "@stellar/stellar-sdk"; -import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; -export type Tx = Transaction, Operation[]>; -export declare class ExpiredStateError extends Error { -} -export declare class NeedsMoreSignaturesError extends Error { -} -export declare class WalletDisconnectedError extends Error { -} -export declare class SendResultOnlyError extends Error { -} -export declare class SendFailedError extends Error { -} -export declare class NoUnsignedNonInvokerAuthEntriesError extends Error { -} -type SendTx = SorobanRpc.Api.SendTransactionResponse; -type GetTx = SorobanRpc.Api.GetTransactionResponse; -export type u32 = number; -export type i32 = number; -export type u64 = bigint; -export type i64 = bigint; -export type u128 = bigint; -export type i128 = bigint; -export type u256 = bigint; -export type i256 = bigint; -export type Option = T | undefined; -export type Typepoint = bigint; -export type Duration = bigint; -export { Address }; -export interface Error_ { - message: string; -} -export interface Result { - unwrap(): T; - unwrapErr(): E; - isOk(): boolean; - isErr(): boolean; -} -export declare class Ok implements Result { - readonly value: T; - constructor(value: T); - unwrapErr(): E; - unwrap(): T; - isOk(): boolean; - isErr(): boolean; -} -export declare class Err implements Result { - readonly error: E; - constructor(error: E); - unwrapErr(): E; - unwrap(): never; - isOk(): boolean; - isErr(): boolean; -} -export declare const contractErrorPattern: RegExp; -type AssembledTransactionOptions = MethodOptions & ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal | Err) => T; -}; -export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; -export declare class AssembledTransaction { - options: AssembledTransactionOptions; - raw: Tx; - private simulation?; - private simulationResult?; - private simulationTransactionData?; - private server; - toJSON(): string; - static fromJSON(options: Omit, 'args'>, { tx, simulationResult, simulationTransactionData }: { - tx: XDR_BASE64; - simulationResult: { - auth: XDR_BASE64[]; - retval: XDR_BASE64; - }; - simulationTransactionData: XDR_BASE64; - }): AssembledTransaction; - private constructor(); - static fromSimulation(options: AssembledTransactionOptions): Promise>; - simulate: () => Promise; - get simulationData(): { - result: SorobanRpc.Api.SimulateHostFunctionResult; - transactionData: xdr.SorobanTransactionData; - }; - get result(): T; - parseError(errorMessage: string): Err | undefined; - getWallet: () => Promise; - getPublicKey: () => Promise; - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount: () => Promise; - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend: ({ secondsToWait, force }?: { - /** - * Wait `secondsToWait` seconds (default: 10) for both the transaction to SEND successfully (will keep trying if the server returns `TRY_AGAIN_LATER`), as well as for the transaction to COMPLETE (will keep checking if the server returns `PENDING`). - */ - secondsToWait?: number | undefined; - /** - * If `true`, sign and send the transaction even if it is a read call. - */ - force?: boolean | undefined; - }) => Promise>; - getStorageExpiration: () => Promise; - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy: ({ includeAlreadySigned, }?: { - /** - * Whether or not to include auth entries that have already been signed. Default: false - */ - includeAlreadySigned?: boolean | undefined; - }) => Promise; - preImageFor(entry: xdr.SorobanAuthorizationEntry, signatureExpirationLedger: number): xdr.HashIdPreimage; - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries: (expiration?: number | Promise) => Promise; - get isReadCall(): boolean; - hasRealInvoker: () => Promise; -} -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -declare class SentTransaction { - options: AssembledTransactionOptions; - assembled: AssembledTransaction; - server: SorobanRpc.Server; - signed: Tx; - sendTransactionResponse?: SendTx; - sendTransactionResponseAll?: SendTx[]; - getTransactionResponse?: GetTx; - getTransactionResponseAll?: GetTx[]; - constructor(options: AssembledTransactionOptions, assembled: AssembledTransaction); - static init: (options: AssembledTransactionOptions, assembled: AssembledTransaction, secondsToWait?: number) => Promise>; - private send; - get result(): T; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/index.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/index.d.ts deleted file mode 100644 index 66d3d595..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/index.d.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { ContractSpec } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -import type { u32, i32, i64, i128, Option, Error_ } from './assembled-tx.js'; -import type { ClassOptions } from './method-options.js'; -export * from './assembled-tx.js'; -export * from './method-options.js'; -export declare const networks: { - readonly futurenet: { - readonly networkPassphrase: "Test SDF Future Network ; October 2022"; - readonly contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK"; - }; -}; -/** - This is from the rust doc above the struct Test - */ -export interface Test { - /** - - */ - a: u32; - /** - - */ - b: boolean; - /** - - */ - c: string; -} -/** - - */ -export type SimpleEnum = { - tag: "First"; - values: void; -} | { - tag: "Second"; - values: void; -} | { - tag: "Third"; - values: void; -}; -/** - - */ -export declare enum RoyalCard { - Jack = 11, - Queen = 12, - King = 13 -} -/** - - */ -export type TupleStruct = readonly [Test, SimpleEnum]; -/** - - */ -export type ComplexEnum = { - tag: "Struct"; - values: readonly [Test]; -} | { - tag: "Tuple"; - values: readonly [TupleStruct]; -} | { - tag: "Enum"; - values: readonly [SimpleEnum]; -} | { - tag: "Asset"; - values: readonly [string, i128]; -} | { - tag: "Void"; - values: void; -}; -/** - - */ -export declare const Errors: { - 1: { - message: string; - }; -}; -export declare class Contract { - readonly options: ClassOptions; - spec: ContractSpec; - constructor(options: ClassOptions); - private readonly parsers; - private txFromJSON; - readonly fromJSON: { - hello: (json: string) => AssembledTransaction; - woid: (json: string) => AssembledTransaction; - val: (json: string) => AssembledTransaction; - u32FailOnEven: (json: string) => AssembledTransaction | Ok>; - u32: (json: string) => AssembledTransaction; - i32: (json: string) => AssembledTransaction; - i64: (json: string) => AssembledTransaction; - struktHel: (json: string) => AssembledTransaction; - strukt: (json: string) => AssembledTransaction; - simple: (json: string) => AssembledTransaction; - complex: (json: string) => AssembledTransaction; - addresse: (json: string) => AssembledTransaction; - bytes: (json: string) => AssembledTransaction; - bytesN: (json: string) => AssembledTransaction; - card: (json: string) => AssembledTransaction; - boolean: (json: string) => AssembledTransaction; - not: (json: string) => AssembledTransaction; - i128: (json: string) => AssembledTransaction; - u128: (json: string) => AssembledTransaction; - multiArgs: (json: string) => AssembledTransaction; - map: (json: string) => AssembledTransaction>; - vec: (json: string) => AssembledTransaction; - tuple: (json: string) => AssembledTransaction; - option: (json: string) => AssembledTransaction>; - u256: (json: string) => AssembledTransaction; - i256: (json: string) => AssembledTransaction; - string: (json: string) => AssembledTransaction; - tupleStrukt: (json: string) => AssembledTransaction; - }; - /** -* Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - hello: ({ hello }: { - hello: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - woid: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - val: (options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32FailOnEven: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise | Ok>>; - /** -* Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u32: ({ u32_ }: { - u32_: u32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i32: ({ i32_ }: { - i32_: i32; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i64: ({ i64_ }: { - i64_: i64; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct -*/ - struktHel: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - strukt: ({ strukt }: { - strukt: Test; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - simple: ({ simple }: { - simple: SimpleEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - complex: ({ complex }: { - complex: ComplexEnum; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - addresse: ({ addresse }: { - addresse: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytes: ({ bytes }: { - bytes: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - bytesN: ({ bytes_n }: { - bytes_n: Buffer; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - card: ({ card }: { - card: RoyalCard; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - boolean: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value -*/ - not: ({ boolean }: { - boolean: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i128: ({ i128 }: { - i128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u128: ({ u128 }: { - u128: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - multiArgs: ({ a, b }: { - a: u32; - b: boolean; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - map: ({ map }: { - map: Map; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - vec: ({ vec }: { - vec: Array; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tuple: ({ tuple }: { - tuple: readonly [string, u32]; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument -*/ - option: ({ option }: { - option: Option; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>>; - /** -* Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - u256: ({ u256 }: { - u256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - i256: ({ i256 }: { - i256: bigint; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - string: ({ string }: { - string: string; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; - /** -* Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. -*/ - tupleStrukt: ({ tuple_strukt }: { - tuple_strukt: TupleStruct; - }, options?: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number; - }) => Promise>; -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/method-options.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/method-options.d.ts deleted file mode 100644 index fc6b21d5..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/method-options.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -declare let responseTypes: 'simulated' | 'full' | undefined; -export type ResponseTypes = typeof responseTypes; -export type XDR_BASE64 = string; -export interface Wallet { - isConnected: () => Promise; - isAllowed: () => Promise; - getUserInfo: () => Promise<{ - publicKey?: string; - }>; - signTransaction: (tx: XDR_BASE64, opts?: { - network?: string; - networkPassphrase?: string; - accountToSign?: string; - }) => Promise; - signAuthEntry: (entryXdr: XDR_BASE64, opts?: { - accountToSign?: string; - }) => Promise; -} -export type ClassOptions = { - contractId: string; - networkPassphrase: string; - rpcUrl: string; - errorTypes?: Record; - /** - * A Wallet interface, such as Freighter, that has the methods `isConnected`, `isAllowed`, `getUserInfo`, and `signTransaction`. If not provided, will attempt to import and use Freighter. Example: - * - * @example - * ```ts - * import freighter from "@stellar/freighter-api"; - * import { Contract } from "test_custom_types"; - * const contract = new Contract({ - * …, - * wallet: freighter, - * }) - * ``` - */ - wallet?: Wallet; -}; -export type MethodOptions = { - /** - * The fee to pay for the transaction. Default: soroban-sdk's BASE_FEE ('100') - */ - fee?: number; -}; -export {}; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json deleted file mode 100644 index 44332a41..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - "name": "test_custom_types", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "test_custom_types", - "version": "0.0.0", - "dependencies": { - "@stellar/freighter-api": "1.7.1", - "@stellar/stellar-sdk": "11.2.0", - "buffer": "6.0.3" - }, - "devDependencies": { - "typescript": "5.3.3" - } - }, - "node_modules/@stellar/freighter-api": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@stellar/freighter-api/-/freighter-api-1.7.1.tgz", - "integrity": "sha512-XvPO+XgEbkeP0VhP0U1edOkds+rGS28+y8GRGbCVXeZ9ZslbWqRFQoETAdX8IXGuykk2ib/aPokiLc5ZaWYP7w==" - }, - "node_modules/@stellar/js-xdr": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.0.1.tgz", - "integrity": "sha512-dp5Eh7Nr1YjiIeqpdkj2cQYxfoPudDAH3ck8MWggp48Htw66Z/hUssNYUQG/OftLjEmHT90Z/dtey2Y77DOxIw==" - }, - "node_modules/@stellar/stellar-base": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-10.0.1.tgz", - "integrity": "sha512-BDbx7VHOEQh+4J3Q+gStNXgPaNckVFmD4aOlBBGwxlF6vPFmVnW8IoJdkX7T58zpX55eWI6DXvEhDBlrqTlhAQ==", - "dependencies": { - "@stellar/js-xdr": "^3.0.1", - "base32.js": "^0.1.0", - "bignumber.js": "^9.1.2", - "buffer": "^6.0.3", - "sha.js": "^2.3.6", - "tweetnacl": "^1.0.3" - }, - "optionalDependencies": { - "sodium-native": "^4.0.1" - } - }, - "node_modules/@stellar/stellar-sdk": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-11.2.0.tgz", - "integrity": "sha512-qInRR+mLLl9O/AI6Q+Sr19RZeYJtlNoJQJi3pch5BYoMvVhjO8IU8AhHADP//Zmc2osyogwPuqXBiFdaGlfHWA==", - "dependencies": { - "@stellar/stellar-base": "10.0.1", - "axios": "^1.6.5", - "bignumber.js": "^9.1.2", - "eventsource": "^2.0.2", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "engines": { - "node": "*" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/sodium-native": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.0.5.tgz", - "integrity": "sha512-YGimGhy7Ho6pTAAvuNdn3Tv9C2MD7HP89X1omReHat0Fd1mMnapGqwzb5YoHTAbIEh8tQmKP6+uLlwYCkf+EOA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "node-gyp-build": "^4.6.0" - } - }, - "node_modules/toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/urijs": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" - } - } -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json deleted file mode 100644 index 1f63968a..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "0.0.0", - "name": "test_custom_types", - "dependencies": { - "@stellar/freighter-api": "1.7.1", - "buffer": "6.0.3", - "@stellar/stellar-sdk": "11.2.0" - }, - "scripts": { - "build": "node ./scripts/build.mjs" - }, - "exports": { - "require": "./dist/cjs/index.js", - "import": "./dist/esm/index.js" - }, - "typings": "dist/types/index.d.ts", - "devDependencies": { - "typescript": "5.3.3" - } -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/build.mjs b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/build.mjs deleted file mode 100644 index 15a17042..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/build.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import { spawnSync } from "node:child_process" -import fs from "node:fs" -import path from "node:path" - -const buildDir = "./dist" - -const { error, stderr } = spawnSync("tsc", ["-b", "./scripts/tsconfig.cjs.json", "./scripts/tsconfig.esm.json", "./scripts/tsconfig.types.json"], { stdio: "inherit" }) - -if (error) { - console.error(stderr) - console.error(error) - throw error -} - -function createEsmModulePackageJson() { - fs.readdir(buildDir, function (err, dirs) { - if (err) { - throw err - } - dirs.forEach(function (dir) { - if (dir === "esm") { - // 1. add package.json file with "type": "module" - var packageJsonFile = path.join(buildDir, dir, "/package.json") - if (!fs.existsSync(packageJsonFile)) { - fs.writeFileSync( - packageJsonFile, - '{"type": "module"}', - 'utf8', - err => { if (err) throw err } - ) - } - } - }) - }) -} - -createEsmModulePackageJson() diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.cjs.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.cjs.json deleted file mode 100644 index 542ea86d..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.cjs.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/cjs", - "module": "commonjs" - } -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.esm.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.esm.json deleted file mode 100644 index 92b45277..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.esm.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/esm", - "module": "esnext" - } -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.types.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.types.json deleted file mode 100644 index 8a24fc13..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/scripts/tsconfig.types.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/types", - "declaration": true, - "emitDeclarationOnly": true - } -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/assembled-tx.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/assembled-tx.ts deleted file mode 100644 index f4f892d4..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/assembled-tx.ts +++ /dev/null @@ -1,664 +0,0 @@ -import { - Account, - Address, - Contract, - Operation, - SorobanRpc, - StrKey, - TimeoutInfinite, - TransactionBuilder, - authorizeEntry, - hash, - nativeToScVal, - xdr, - BASE_FEE, -} from "@stellar/stellar-sdk"; -import type { Memo, MemoType, Transaction } from "@stellar/stellar-sdk"; -import { Buffer } from "buffer"; -import type { - ClassOptions, - MethodOptions, - Wallet, - XDR_BASE64, -} from "./method-options.js"; - -export type Tx = Transaction, Operation[]> - -export class ExpiredStateError extends Error { } -export class NeedsMoreSignaturesError extends Error { } -export class WalletDisconnectedError extends Error { } -export class SendResultOnlyError extends Error { } -export class SendFailedError extends Error { } -export class NoUnsignedNonInvokerAuthEntriesError extends Error { } - -type SendTx = SorobanRpc.Api.SendTransactionResponse; -type GetTx = SorobanRpc.Api.GetTransactionResponse; - -export type u32 = number; -export type i32 = number; -export type u64 = bigint; -export type i64 = bigint; -export type u128 = bigint; -export type i128 = bigint; -export type u256 = bigint; -export type i256 = bigint; -export type Option = T | undefined; -export type Typepoint = bigint; -export type Duration = bigint; -export {Address}; - -/// Error interface containing the error message -export interface Error_ { message: string }; - -export interface Result { - unwrap(): T, - unwrapErr(): E, - isOk(): boolean, - isErr(): boolean, -}; - -export class Ok implements Result { - constructor(readonly value: T) { } - unwrapErr(): E { - throw new Error('No error'); - } - unwrap(): T { - return this.value; - } - - isOk(): boolean { - return true; - } - - isErr(): boolean { - return !this.isOk() - } -} - -export class Err implements Result { - constructor(readonly error: E) { } - unwrapErr(): E { - return this.error; - } - unwrap(): never { - throw new Error(this.error.message); - } - - isOk(): boolean { - return false; - } - - isErr(): boolean { - return !this.isOk() - } -} - -export const contractErrorPattern = /Error\(Contract, #(\d+)\)/; - -type AssembledTransactionOptions = MethodOptions & - ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal | Err) => T; - }; - -export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF" - -export class AssembledTransaction { - public raw: Tx - private simulation?: SorobanRpc.Api.SimulateTransactionResponse - private simulationResult?: SorobanRpc.Api.SimulateHostFunctionResult - private simulationTransactionData?: xdr.SorobanTransactionData - private server: SorobanRpc.Server - - toJSON() { - return JSON.stringify({ - method: this.options.method, - tx: this.raw?.toXDR(), - simulationResult: { - auth: this.simulationData.result.auth.map(a => a.toXDR('base64')), - retval: this.simulationData.result.retval.toXDR('base64'), - }, - simulationTransactionData: this.simulationData.transactionData.toXDR('base64'), - }) - } - - static fromJSON( - options: Omit, 'args'>, - { tx, simulationResult, simulationTransactionData }: - { - tx: XDR_BASE64, - simulationResult: { - auth: XDR_BASE64[], - retval: XDR_BASE64, - }, - simulationTransactionData: XDR_BASE64, - } - ): AssembledTransaction { - const txn = new AssembledTransaction(options) - txn.raw = TransactionBuilder.fromXDR(tx, options.networkPassphrase) as Tx - txn.simulationResult = { - auth: simulationResult.auth.map(a => xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64')), - retval: xdr.ScVal.fromXDR(simulationResult.retval, 'base64'), - } - txn.simulationTransactionData = xdr.SorobanTransactionData.fromXDR(simulationTransactionData, 'base64') - return txn - } - - private constructor(public options: AssembledTransactionOptions) { - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - } - - static async fromSimulation(options: AssembledTransactionOptions): Promise> { - const tx = new AssembledTransaction(options) - const contract = new Contract(options.contractId); - - tx.raw = new TransactionBuilder(await tx.getAccount(), { - fee: options.fee?.toString(10) ?? BASE_FEE, - networkPassphrase: options.networkPassphrase, - }) - .addOperation(contract.call(options.method, ...(options.args ?? []))) - .setTimeout(TimeoutInfinite) - .build(); - - return await tx.simulate() - } - - simulate = async (): Promise => { - if (!this.raw) throw new Error('Transaction has not yet been assembled') - this.simulation = await this.server.simulateTransaction(this.raw); - - if (SorobanRpc.Api.isSimulationSuccess(this.simulation)) { - this.raw = SorobanRpc.assembleTransaction( - this.raw, - this.simulation - ).build() - } - - return this - } - - get simulationData(): { - result: SorobanRpc.Api.SimulateHostFunctionResult - transactionData: xdr.SorobanTransactionData - } { - if (this.simulationResult && this.simulationTransactionData) { - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - } - } - // else, we know we just did the simulation on this machine - const simulation = this.simulation! - if (SorobanRpc.Api.isSimulationError(simulation)) { - throw new Error(`Transaction simulation failed: "${simulation.error}"`) - } - - if (SorobanRpc.Api.isSimulationRestore(simulation)) { - throw new ExpiredStateError(`You need to restore some contract state before you can invoke this method. ${JSON.stringify(simulation, null, 2)}`) - } - - if (!simulation.result) { - throw new Error(`Expected an invocation simulation, but got no 'result' field. Simulation: ${JSON.stringify(simulation, null, 2)}`) - } - - // add to object for serialization & deserialization - this.simulationResult = simulation.result - this.simulationTransactionData = simulation.transactionData.build() - - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData!, - } - } - - get result(): T { - try { - return this.options.parseResultXdr(this.simulationData.result.retval) - } catch (e) { - let err = this.parseError(e.toString()) - if (err) return err as T - throw e - } - } - - parseError(errorMessage: string): Err | undefined { - if (!this.options.errorTypes) return - const match = errorMessage.match(contractErrorPattern) - if (!match) return - let i = parseInt(match[1], 10) - let err = this.options.errorTypes[i] - if (err) return new Err(err) - } - - getWallet = async (): Promise => { - return this.options.wallet ?? (await import("@stellar/freighter-api")).default - } - - getPublicKey = async (): Promise => { - const wallet = await this.getWallet() - if (await wallet.isConnected() && await wallet.isAllowed()) { - return (await wallet.getUserInfo()).publicKey - } - } - - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount = async (): Promise => { - const publicKey = await this.getPublicKey() - return publicKey - ? await this.server.getAccount(publicKey) - : new Account(NULL_ACCOUNT, "0") - } - - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend = async ({ secondsToWait = 10, force = false }: { - /** - * Wait `secondsToWait` seconds (default: 10) for both the transaction to SEND successfully (will keep trying if the server returns `TRY_AGAIN_LATER`), as well as for the transaction to COMPLETE (will keep checking if the server returns `PENDING`). - */ - secondsToWait?: number - /** - * If `true`, sign and send the transaction even if it is a read call. - */ - force?: boolean - } = {}): Promise> => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated') - } - - if (!force && this.isReadCall) { - throw new Error('This is a read call. It requires no signature or sending. Use `force: true` to sign and send anyway.') - } - - if (!await this.hasRealInvoker()) { - throw new WalletDisconnectedError('Wallet is not connected') - } - - if (this.raw.source !== (await this.getAccount()).accountId()) { - throw new Error(`You must submit the transaction with the account that originally created it. Please switch to the wallet with "${this.raw.source}" as its public key.`) - } - - if ((await this.needsNonInvokerSigningBy()).length) { - throw new NeedsMoreSignaturesError( - 'Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.' - ) - } - - return await SentTransaction.init(this.options, this, secondsToWait); - } - - getStorageExpiration = async () => { - const entryRes = await this.server.getLedgerEntries( - new Contract(this.options.contractId).getFootprint() - ) - if ( - !entryRes.entries || - !entryRes.entries.length || - !entryRes.entries[0].liveUntilLedgerSeq - ) throw new Error('failed to get ledger entry') - return entryRes.entries[0].liveUntilLedgerSeq - } - - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy = async ({ - includeAlreadySigned = false, - }: { - /** - * Whether or not to include auth entries that have already been signed. Default: false - */ - includeAlreadySigned?: boolean - } = {}): Promise => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated') - } - - // We expect that any transaction constructed by these libraries has a - // single operation, which is an InvokeHostFunction operation. The host - // function being invoked is the contract method call. - if (!("operations" in this.raw)) { - throw new Error( - `Unexpected Transaction type; no operations: ${JSON.stringify(this.raw) - }` - ) - } - const rawInvokeHostFunctionOp = this.raw - .operations[0] as Operation.InvokeHostFunction - - return [...new Set((rawInvokeHostFunctionOp.auth ?? []).filter(entry => - entry.credentials().switch() === - xdr.SorobanCredentialsType.sorobanCredentialsAddress() && - ( - includeAlreadySigned || - entry.credentials().address().signature().switch().name === 'scvVoid' - ) - ).map(entry => StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519() - )))] - } - - preImageFor( - entry: xdr.SorobanAuthorizationEntry, - signatureExpirationLedger: number - ): xdr.HashIdPreimage { - const addrAuth = entry.credentials().address() - return xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( - new xdr.HashIdPreimageSorobanAuthorization({ - networkId: hash(Buffer.from(this.options.networkPassphrase)), - nonce: addrAuth.nonce(), - invocation: entry.rootInvocation(), - signatureExpirationLedger, - }), - ) - } - - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries = async ( - /** - * When to set each auth entry to expire. Could be any number of blocks in - * the future. Can be supplied as a promise or a raw number. Default: - * contract's current `persistent` storage expiration date/ledger - * number/block. - */ - expiration: number | Promise = this.getStorageExpiration() - ): Promise => { - if (!this.raw) throw new Error('Transaction has not yet been assembled or simulated') - const needsNonInvokerSigningBy = await this.needsNonInvokerSigningBy() - - if (!needsNonInvokerSigningBy) throw new NoUnsignedNonInvokerAuthEntriesError('No unsigned non-invoker auth entries; maybe you already signed?') - const publicKey = await this.getPublicKey() - if (!publicKey) throw new Error('Could not get public key from wallet; maybe Freighter is not signed in?') - if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) throw new Error(`No auth entries for public key "${publicKey}"`) - const wallet = await this.getWallet() - - const rawInvokeHostFunctionOp = this.raw - .operations[0] as Operation.InvokeHostFunction - - const authEntries = rawInvokeHostFunctionOp.auth ?? [] - - for (const [i, entry] of authEntries.entries()) { - if ( - entry.credentials().switch() !== - xdr.SorobanCredentialsType.sorobanCredentialsAddress() - ) { - // if the invoker/source account, then the entry doesn't need explicit - // signature, since the tx envelope is already signed by the source - // account, so only check for sorobanCredentialsAddress - continue - } - const pk = StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519() - ) - - // this auth entry needs to be signed by a different account - // (or maybe already was!) - if (pk !== publicKey) continue - - authEntries[i] = await authorizeEntry( - entry, - async preimage => Buffer.from( - await wallet.signAuthEntry(preimage.toXDR('base64')), - 'base64' - ), - await expiration, - this.options.networkPassphrase - ) - } - } - - get isReadCall(): boolean { - const authsCount = this.simulationData.result.auth.length; - const writeLength = this.simulationData.transactionData.resources().footprint().readWrite().length - return (authsCount === 0) && (writeLength === 0); - } - - hasRealInvoker = async (): Promise => { - const account = await this.getAccount() - return account.accountId() !== NULL_ACCOUNT - } -} - -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -class SentTransaction { - public server: SorobanRpc.Server - public signed: Tx - public sendTransactionResponse?: SendTx - public sendTransactionResponseAll?: SendTx[] - public getTransactionResponse?: GetTx - public getTransactionResponseAll?: GetTx[] - - constructor(public options: AssembledTransactionOptions, public assembled: AssembledTransaction) { - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - this.assembled = assembled - } - - static init = async ( - options: AssembledTransactionOptions, - assembled: AssembledTransaction, - secondsToWait: number = 10 - ): Promise> => { - const tx = new SentTransaction(options, assembled) - return await tx.send(secondsToWait) - } - - private send = async (secondsToWait: number = 10): Promise => { - const wallet = await this.assembled.getWallet() - - this.sendTransactionResponseAll = await withExponentialBackoff( - async (previousFailure) => { - if (previousFailure) { - // Increment transaction sequence number and resimulate before trying again - - // Soroban transaction can only have 1 operation - const op = this.assembled.raw.operations[0] as Operation.InvokeHostFunction; - - this.assembled.raw = new TransactionBuilder(await this.assembled.getAccount(), { - fee: this.assembled.raw.fee, - networkPassphrase: this.options.networkPassphrase, - }) - .setTimeout(TimeoutInfinite) - .addOperation( - Operation.invokeHostFunction({ ...op, auth: op.auth ?? [] }), - ) - .build() - - await this.assembled.simulate() - } - - const signature = await wallet.signTransaction(this.assembled.raw.toXDR(), { - networkPassphrase: this.options.networkPassphrase, - }); - - this.signed = TransactionBuilder.fromXDR( - signature, - this.options.networkPassphrase - ) as Tx - - return this.server.sendTransaction(this.signed) - }, - resp => resp.status !== "PENDING", - secondsToWait - ) - - this.sendTransactionResponse = this.sendTransactionResponseAll[this.sendTransactionResponseAll.length - 1] - - if (this.sendTransactionResponse.status !== "PENDING") { - throw new Error( - `Tried to resubmit transaction for ${secondsToWait - } seconds, but it's still failing. ` + - `All attempts: ${JSON.stringify( - this.sendTransactionResponseAll, - null, - 2 - )}` - ); - } - - const { hash } = this.sendTransactionResponse - - this.getTransactionResponseAll = await withExponentialBackoff( - () => this.server.getTransaction(hash), - resp => resp.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND, - secondsToWait - ) - - this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1] - if (this.getTransactionResponse.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND) { - console.error( - `Waited ${secondsToWait - } seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Sent transaction: ${JSON.stringify( - this.sendTransactionResponse, - null, - 2 - )}\n` + - `All attempts to get the result: ${JSON.stringify( - this.getTransactionResponseAll, - null, - 2 - )}` - ); - } - - return this; - } - - get result(): T { - // 1. check if transaction was submitted and awaited with `getTransaction` - if ( - "getTransactionResponse" in this && - this.getTransactionResponse - ) { - // getTransactionResponse has a `returnValue` field unless it failed - if ("returnValue" in this.getTransactionResponse) { - return this.options.parseResultXdr(this.getTransactionResponse.returnValue!) - } - - // if "returnValue" not present, the transaction failed; return without parsing the result - throw new Error("Transaction failed! Cannot parse result.") - } - - // 2. otherwise, maybe it was merely sent with `sendTransaction` - if (this.sendTransactionResponse) { - const errorResult = this.sendTransactionResponse.errorResult?.result() - if (errorResult) { - throw new SendFailedError( - `Transaction simulation looked correct, but attempting to send the transaction failed. Check \`simulation\` and \`sendTransactionResponseAll\` to troubleshoot. Decoded \`sendTransactionResponse.errorResultXdr\`: ${errorResult}` - ) - } - throw new SendResultOnlyError( - `Transaction was sent to the network, but not yet awaited. No result to show. Await transaction completion with \`getTransaction(sendTransactionResponse.hash)\`` - ) - } - - // 3. finally, if neither of those are present, throw an error - throw new Error(`Sending transaction failed: ${JSON.stringify(this.assembled)}`) - } -} - -/** - * Keep calling a `fn` for `secondsToWait` seconds, if `keepWaitingIf` is true. - * Returns an array of all attempts to call the function. - */ -async function withExponentialBackoff( - fn: (previousFailure?: T) => Promise, - keepWaitingIf: (result: T) => boolean, - secondsToWait: number, - exponentialFactor = 1.5, - verbose = false, -): Promise { - const attempts: T[] = [] - - let count = 0 - attempts.push(await fn()) - if (!keepWaitingIf(attempts[attempts.length - 1])) return attempts - - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf() - let waitTime = 1000 - let totalWaitTime = waitTime - - while (Date.now() < waitUntil && keepWaitingIf(attempts[attempts.length - 1])) { - count++ - // Wait a beat - if (verbose) { - console.info(`Waiting ${waitTime}ms before trying again (bringing the total wait time to ${totalWaitTime}ms so far, of total ${secondsToWait * 1000}ms)`) - } - await new Promise(res => setTimeout(res, waitTime)) - // Exponential backoff - waitTime = waitTime * exponentialFactor; - if (new Date(Date.now() + waitTime).valueOf() > waitUntil) { - waitTime = waitUntil - Date.now() - if (verbose) { - console.info(`was gonna wait too long; new waitTime: ${waitTime}ms`) - } - } - totalWaitTime = waitTime + totalWaitTime - // Try again - attempts.push(await fn(attempts[attempts.length - 1])) - if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { - console.info( - `${count}. Called ${fn}; ${attempts.length - } prev attempts. Most recent: ${JSON.stringify(attempts[attempts.length - 1], null, 2) - }` - ) - } - } - - return attempts -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/index.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/index.ts deleted file mode 100644 index e3119225..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/index.ts +++ /dev/null @@ -1,758 +0,0 @@ -import { ContractSpec, Address } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -import type { - u32, - i32, - u64, - i64, - u128, - i128, - u256, - i256, - Option, - Typepoint, - Duration, - Error_, - Result, -} from './assembled-tx.js'; -import type { ClassOptions, XDR_BASE64 } from './method-options.js'; - -export * from './assembled-tx.js'; -export * from './method-options.js'; - -if (typeof window !== 'undefined') { - //@ts-ignore Buffer exists - window.Buffer = window.Buffer || Buffer; -} - - -export const networks = { - futurenet: { - networkPassphrase: "Test SDF Future Network ; October 2022", - contractId: "CBYMYMSDF6FBDNCFJCRC7KMO4REYFPOH2U4N7FXI3GJO6YXNCQ43CDSK", - } -} as const - -/** - This is from the rust doc above the struct Test - */ -export interface Test { - /** - - */ -a: u32; - /** - - */ -b: boolean; - /** - - */ -c: string; -} - -/** - - */ -export type SimpleEnum = {tag: "First", values: void} | {tag: "Second", values: void} | {tag: "Third", values: void}; - -/** - - */ -export enum RoyalCard { - Jack = 11, - Queen = 12, - King = 13, -} - -/** - - */ -export type TupleStruct = readonly [Test, SimpleEnum]; -/** - - */ -export type ComplexEnum = {tag: "Struct", values: readonly [Test]} | {tag: "Tuple", values: readonly [TupleStruct]} | {tag: "Enum", values: readonly [SimpleEnum]} | {tag: "Asset", values: readonly [string, i128]} | {tag: "Void", values: void}; - -/** - - */ -export const Errors = { -1: {message:"Please provide an odd number"} -} - -export class Contract { - spec: ContractSpec; - constructor(public readonly options: ClassOptions) { - this.spec = new ContractSpec([ - "AAAAAQAAAC9UaGlzIGlzIGZyb20gdGhlIHJ1c3QgZG9jIGFib3ZlIHRoZSBzdHJ1Y3QgVGVzdAAAAAAAAAAABFRlc3QAAAADAAAAAAAAAAFhAAAAAAAABAAAAAAAAAABYgAAAAAAAAEAAAAAAAAAAWMAAAAAAAAR", - "AAAAAgAAAAAAAAAAAAAAClNpbXBsZUVudW0AAAAAAAMAAAAAAAAAAAAAAAVGaXJzdAAAAAAAAAAAAAAAAAAABlNlY29uZAAAAAAAAAAAAAAAAAAFVGhpcmQAAAA=", - "AAAAAwAAAAAAAAAAAAAACVJveWFsQ2FyZAAAAAAAAAMAAAAAAAAABEphY2sAAAALAAAAAAAAAAVRdWVlbgAAAAAAAAwAAAAAAAAABEtpbmcAAAAN", - "AAAAAQAAAAAAAAAAAAAAC1R1cGxlU3RydWN0AAAAAAIAAAAAAAAAATAAAAAAAAfQAAAABFRlc3QAAAAAAAAAATEAAAAAAAfQAAAAClNpbXBsZUVudW0AAA==", - "AAAAAgAAAAAAAAAAAAAAC0NvbXBsZXhFbnVtAAAAAAUAAAABAAAAAAAAAAZTdHJ1Y3QAAAAAAAEAAAfQAAAABFRlc3QAAAABAAAAAAAAAAVUdXBsZQAAAAAAAAEAAAfQAAAAC1R1cGxlU3RydWN0AAAAAAEAAAAAAAAABEVudW0AAAABAAAH0AAAAApTaW1wbGVFbnVtAAAAAAABAAAAAAAAAAVBc3NldAAAAAAAAAIAAAATAAAACwAAAAAAAAAAAAAABFZvaWQ=", - "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAAQAAABxQbGVhc2UgcHJvdmlkZSBhbiBvZGQgbnVtYmVyAAAAD051bWJlck11c3RCZU9kZAAAAAAB", - "AAAAAAAAAAAAAAAFaGVsbG8AAAAAAAABAAAAAAAAAAVoZWxsbwAAAAAAABEAAAABAAAAEQ==", - "AAAAAAAAAAAAAAAEd29pZAAAAAAAAAAA", - "AAAAAAAAAAAAAAADdmFsAAAAAAAAAAABAAAAAA==", - "AAAAAAAAAAAAAAAQdTMyX2ZhaWxfb25fZXZlbgAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAA+kAAAAEAAAAAw==", - "AAAAAAAAAAAAAAAEdTMyXwAAAAEAAAAAAAAABHUzMl8AAAAEAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAAEaTMyXwAAAAEAAAAAAAAABGkzMl8AAAAFAAAAAQAAAAU=", - "AAAAAAAAAAAAAAAEaTY0XwAAAAEAAAAAAAAABGk2NF8AAAAHAAAAAQAAAAc=", - "AAAAAAAAACxFeGFtcGxlIGNvbnRyYWN0IG1ldGhvZCB3aGljaCB0YWtlcyBhIHN0cnVjdAAAAApzdHJ1a3RfaGVsAAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAPqAAAAEQ==", - "AAAAAAAAAAAAAAAGc3RydWt0AAAAAAABAAAAAAAAAAZzdHJ1a3QAAAAAB9AAAAAEVGVzdAAAAAEAAAfQAAAABFRlc3Q=", - "AAAAAAAAAAAAAAAGc2ltcGxlAAAAAAABAAAAAAAAAAZzaW1wbGUAAAAAB9AAAAAKU2ltcGxlRW51bQAAAAAAAQAAB9AAAAAKU2ltcGxlRW51bQAA", - "AAAAAAAAAAAAAAAHY29tcGxleAAAAAABAAAAAAAAAAdjb21wbGV4AAAAB9AAAAALQ29tcGxleEVudW0AAAAAAQAAB9AAAAALQ29tcGxleEVudW0A", - "AAAAAAAAAAAAAAAIYWRkcmVzc2UAAAABAAAAAAAAAAhhZGRyZXNzZQAAABMAAAABAAAAEw==", - "AAAAAAAAAAAAAAAFYnl0ZXMAAAAAAAABAAAAAAAAAAVieXRlcwAAAAAAAA4AAAABAAAADg==", - "AAAAAAAAAAAAAAAHYnl0ZXNfbgAAAAABAAAAAAAAAAdieXRlc19uAAAAA+4AAAAJAAAAAQAAA+4AAAAJ", - "AAAAAAAAAAAAAAAEY2FyZAAAAAEAAAAAAAAABGNhcmQAAAfQAAAACVJveWFsQ2FyZAAAAAAAAAEAAAfQAAAACVJveWFsQ2FyZAAAAA==", - "AAAAAAAAAAAAAAAHYm9vbGVhbgAAAAABAAAAAAAAAAdib29sZWFuAAAAAAEAAAABAAAAAQ==", - "AAAAAAAAABdOZWdhdGVzIGEgYm9vbGVhbiB2YWx1ZQAAAAADbm90AAAAAAEAAAAAAAAAB2Jvb2xlYW4AAAAAAQAAAAEAAAAB", - "AAAAAAAAAAAAAAAEaTEyOAAAAAEAAAAAAAAABGkxMjgAAAALAAAAAQAAAAs=", - "AAAAAAAAAAAAAAAEdTEyOAAAAAEAAAAAAAAABHUxMjgAAAAKAAAAAQAAAAo=", - "AAAAAAAAAAAAAAAKbXVsdGlfYXJncwAAAAAAAgAAAAAAAAABYQAAAAAAAAQAAAAAAAAAAWIAAAAAAAABAAAAAQAAAAQ=", - "AAAAAAAAAAAAAAADbWFwAAAAAAEAAAAAAAAAA21hcAAAAAPsAAAABAAAAAEAAAABAAAD7AAAAAQAAAAB", - "AAAAAAAAAAAAAAADdmVjAAAAAAEAAAAAAAAAA3ZlYwAAAAPqAAAABAAAAAEAAAPqAAAABA==", - "AAAAAAAAAAAAAAAFdHVwbGUAAAAAAAABAAAAAAAAAAV0dXBsZQAAAAAAA+0AAAACAAAAEQAAAAQAAAABAAAD7QAAAAIAAAARAAAABA==", - "AAAAAAAAAB9FeGFtcGxlIG9mIGFuIG9wdGlvbmFsIGFyZ3VtZW50AAAAAAZvcHRpb24AAAAAAAEAAAAAAAAABm9wdGlvbgAAAAAD6AAAAAQAAAABAAAD6AAAAAQ=", - "AAAAAAAAAAAAAAAEdTI1NgAAAAEAAAAAAAAABHUyNTYAAAAMAAAAAQAAAAw=", - "AAAAAAAAAAAAAAAEaTI1NgAAAAEAAAAAAAAABGkyNTYAAAANAAAAAQAAAA0=", - "AAAAAAAAAAAAAAAGc3RyaW5nAAAAAAABAAAAAAAAAAZzdHJpbmcAAAAAABAAAAABAAAAEA==", - "AAAAAAAAAAAAAAAMdHVwbGVfc3RydWt0AAAAAQAAAAAAAAAMdHVwbGVfc3RydWt0AAAH0AAAAAtUdXBsZVN0cnVjdAAAAAABAAAH0AAAAAtUdXBsZVN0cnVjdAA=" - ]); - } - private readonly parsers = { - hello: (result: XDR_BASE64): string => this.spec.funcResToNative("hello", result), - woid: () => {}, - val: (result: XDR_BASE64): any => this.spec.funcResToNative("val", result), - u32FailOnEven: (result: XDR_BASE64 | Err): Ok | Err => { - if (result instanceof Err) return result - return new Ok(this.spec.funcResToNative("u32_fail_on_even", result)) - }, - u32: (result: XDR_BASE64): u32 => this.spec.funcResToNative("u32_", result), - i32: (result: XDR_BASE64): i32 => this.spec.funcResToNative("i32_", result), - i64: (result: XDR_BASE64): i64 => this.spec.funcResToNative("i64_", result), - struktHel: (result: XDR_BASE64): Array => this.spec.funcResToNative("strukt_hel", result), - strukt: (result: XDR_BASE64): Test => this.spec.funcResToNative("strukt", result), - simple: (result: XDR_BASE64): SimpleEnum => this.spec.funcResToNative("simple", result), - complex: (result: XDR_BASE64): ComplexEnum => this.spec.funcResToNative("complex", result), - addresse: (result: XDR_BASE64): string => this.spec.funcResToNative("addresse", result), - bytes: (result: XDR_BASE64): Buffer => this.spec.funcResToNative("bytes", result), - bytesN: (result: XDR_BASE64): Buffer => this.spec.funcResToNative("bytes_n", result), - card: (result: XDR_BASE64): RoyalCard => this.spec.funcResToNative("card", result), - boolean: (result: XDR_BASE64): boolean => this.spec.funcResToNative("boolean", result), - not: (result: XDR_BASE64): boolean => this.spec.funcResToNative("not", result), - i128: (result: XDR_BASE64): i128 => this.spec.funcResToNative("i128", result), - u128: (result: XDR_BASE64): u128 => this.spec.funcResToNative("u128", result), - multiArgs: (result: XDR_BASE64): u32 => this.spec.funcResToNative("multi_args", result), - map: (result: XDR_BASE64): Map => this.spec.funcResToNative("map", result), - vec: (result: XDR_BASE64): Array => this.spec.funcResToNative("vec", result), - tuple: (result: XDR_BASE64): readonly [string, u32] => this.spec.funcResToNative("tuple", result), - option: (result: XDR_BASE64): Option => this.spec.funcResToNative("option", result), - u256: (result: XDR_BASE64): u256 => this.spec.funcResToNative("u256", result), - i256: (result: XDR_BASE64): i256 => this.spec.funcResToNative("i256", result), - string: (result: XDR_BASE64): string => this.spec.funcResToNative("string", result), - tupleStrukt: (result: XDR_BASE64): TupleStruct => this.spec.funcResToNative("tuple_strukt", result) - }; - private txFromJSON = (json: string): AssembledTransaction => { - const { method, ...tx } = JSON.parse(json) - return AssembledTransaction.fromJSON( - { - ...this.options, - method, - parseResultXdr: this.parsers[method], - }, - tx, - ); - } - public readonly fromJSON = { - hello: this.txFromJSON>, - woid: this.txFromJSON>, - val: this.txFromJSON>, - u32FailOnEven: this.txFromJSON>, - u32: this.txFromJSON>, - i32: this.txFromJSON>, - i64: this.txFromJSON>, - struktHel: this.txFromJSON>, - strukt: this.txFromJSON>, - simple: this.txFromJSON>, - complex: this.txFromJSON>, - addresse: this.txFromJSON>, - bytes: this.txFromJSON>, - bytesN: this.txFromJSON>, - card: this.txFromJSON>, - boolean: this.txFromJSON>, - not: this.txFromJSON>, - i128: this.txFromJSON>, - u128: this.txFromJSON>, - multiArgs: this.txFromJSON>, - map: this.txFromJSON>, - vec: this.txFromJSON>, - tuple: this.txFromJSON>, - option: this.txFromJSON>, - u256: this.txFromJSON>, - i256: this.txFromJSON>, - string: this.txFromJSON>, - tupleStrukt: this.txFromJSON> - } - /** - * Construct and simulate a hello transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - hello = async ({hello}: {hello: string}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'hello', - args: this.spec.funcArgsToScVals("hello", {hello}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['hello'], - }); - } - - - /** - * Construct and simulate a woid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - woid = async (options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'woid', - args: this.spec.funcArgsToScVals("woid", {}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['woid'], - }); - } - - - /** - * Construct and simulate a val transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - val = async (options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'val', - args: this.spec.funcArgsToScVals("val", {}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['val'], - }); - } - - - /** - * Construct and simulate a u32_fail_on_even transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - u32FailOnEven = async ({u32_}: {u32_: u32}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u32_fail_on_even', - args: this.spec.funcArgsToScVals("u32_fail_on_even", {u32_}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u32FailOnEven'], - }); - } - - - /** - * Construct and simulate a u32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - u32 = async ({u32_}: {u32_: u32}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u32_', - args: this.spec.funcArgsToScVals("u32_", {u32_}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u32'], - }); - } - - - /** - * Construct and simulate a i32_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - i32 = async ({i32_}: {i32_: i32}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i32_', - args: this.spec.funcArgsToScVals("i32_", {i32_}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i32'], - }); - } - - - /** - * Construct and simulate a i64_ transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - i64 = async ({i64_}: {i64_: i64}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i64_', - args: this.spec.funcArgsToScVals("i64_", {i64_}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i64'], - }); - } - - - /** - * Construct and simulate a strukt_hel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example contract method which takes a struct - */ - struktHel = async ({strukt}: {strukt: Test}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'strukt_hel', - args: this.spec.funcArgsToScVals("strukt_hel", {strukt}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['struktHel'], - }); - } - - - /** - * Construct and simulate a strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - strukt = async ({strukt}: {strukt: Test}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'strukt', - args: this.spec.funcArgsToScVals("strukt", {strukt}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['strukt'], - }); - } - - - /** - * Construct and simulate a simple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - simple = async ({simple}: {simple: SimpleEnum}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'simple', - args: this.spec.funcArgsToScVals("simple", {simple}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['simple'], - }); - } - - - /** - * Construct and simulate a complex transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - complex = async ({complex}: {complex: ComplexEnum}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'complex', - args: this.spec.funcArgsToScVals("complex", {complex}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['complex'], - }); - } - - - /** - * Construct and simulate a addresse transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - addresse = async ({addresse}: {addresse: string}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'addresse', - args: this.spec.funcArgsToScVals("addresse", {addresse: new Address(addresse)}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['addresse'], - }); - } - - - /** - * Construct and simulate a bytes transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - bytes = async ({bytes}: {bytes: Buffer}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'bytes', - args: this.spec.funcArgsToScVals("bytes", {bytes}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['bytes'], - }); - } - - - /** - * Construct and simulate a bytes_n transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - bytesN = async ({bytes_n}: {bytes_n: Buffer}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'bytes_n', - args: this.spec.funcArgsToScVals("bytes_n", {bytes_n}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['bytesN'], - }); - } - - - /** - * Construct and simulate a card transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - card = async ({card}: {card: RoyalCard}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'card', - args: this.spec.funcArgsToScVals("card", {card}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['card'], - }); - } - - - /** - * Construct and simulate a boolean transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - boolean = async ({boolean}: {boolean: boolean}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'boolean', - args: this.spec.funcArgsToScVals("boolean", {boolean}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['boolean'], - }); - } - - - /** - * Construct and simulate a not transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Negates a boolean value - */ - not = async ({boolean}: {boolean: boolean}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'not', - args: this.spec.funcArgsToScVals("not", {boolean}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['not'], - }); - } - - - /** - * Construct and simulate a i128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - i128 = async ({i128}: {i128: i128}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i128', - args: this.spec.funcArgsToScVals("i128", {i128}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i128'], - }); - } - - - /** - * Construct and simulate a u128 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - u128 = async ({u128}: {u128: u128}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u128', - args: this.spec.funcArgsToScVals("u128", {u128}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u128'], - }); - } - - - /** - * Construct and simulate a multi_args transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - multiArgs = async ({a, b}: {a: u32, b: boolean}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'multi_args', - args: this.spec.funcArgsToScVals("multi_args", {a, b}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['multiArgs'], - }); - } - - - /** - * Construct and simulate a map transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - map = async ({map}: {map: Map}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'map', - args: this.spec.funcArgsToScVals("map", {map}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['map'], - }); - } - - - /** - * Construct and simulate a vec transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - vec = async ({vec}: {vec: Array}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'vec', - args: this.spec.funcArgsToScVals("vec", {vec}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['vec'], - }); - } - - - /** - * Construct and simulate a tuple transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - tuple = async ({tuple}: {tuple: readonly [string, u32]}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'tuple', - args: this.spec.funcArgsToScVals("tuple", {tuple}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['tuple'], - }); - } - - - /** - * Construct and simulate a option transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.Example of an optional argument - */ - option = async ({option}: {option: Option}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'option', - args: this.spec.funcArgsToScVals("option", {option}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['option'], - }); - } - - - /** - * Construct and simulate a u256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - u256 = async ({u256}: {u256: u256}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'u256', - args: this.spec.funcArgsToScVals("u256", {u256}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['u256'], - }); - } - - - /** - * Construct and simulate a i256 transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - i256 = async ({i256}: {i256: i256}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'i256', - args: this.spec.funcArgsToScVals("i256", {i256}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['i256'], - }); - } - - - /** - * Construct and simulate a string transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - string = async ({string}: {string: string}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'string', - args: this.spec.funcArgsToScVals("string", {string}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['string'], - }); - } - - - /** - * Construct and simulate a tuple_strukt transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - */ - tupleStrukt = async ({tuple_strukt}: {tuple_strukt: TupleStruct}, options: { - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - } = {}) => { - return await AssembledTransaction.fromSimulation({ - method: 'tuple_strukt', - args: this.spec.funcArgsToScVals("tuple_strukt", {tuple_strukt}), - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['tupleStrukt'], - }); - } - -} \ No newline at end of file diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/method-options.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/method-options.ts deleted file mode 100644 index 737ae0a0..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/method-options.ts +++ /dev/null @@ -1,50 +0,0 @@ -// defined this way so typeahead shows full union, not named alias -let responseTypes: 'simulated' | 'full' | undefined -export type ResponseTypes = typeof responseTypes - -export type XDR_BASE64 = string - -export interface Wallet { - isConnected: () => Promise, - isAllowed: () => Promise, - getUserInfo: () => Promise<{ publicKey?: string }>, - signTransaction: (tx: XDR_BASE64, opts?: { - network?: string, - networkPassphrase?: string, - accountToSign?: string, - }) => Promise, - signAuthEntry: ( - entryXdr: XDR_BASE64, - opts?: { - accountToSign?: string; - } - ) => Promise -} - -export type ClassOptions = { - contractId: string - networkPassphrase: string - rpcUrl: string - errorTypes?: Record - /** - * A Wallet interface, such as Freighter, that has the methods `isConnected`, `isAllowed`, `getUserInfo`, and `signTransaction`. If not provided, will attempt to import and use Freighter. Example: - * - * @example - * ```ts - * import freighter from "@stellar/freighter-api"; - * import { Contract } from "test_custom_types"; - * const contract = new Contract({ - * …, - * wallet: freighter, - * }) - * ``` - */ - wallet?: Wallet -} - -export type MethodOptions = { - /** - * The fee to pay for the transaction. Default: soroban-sdk's BASE_FEE ('100') - */ - fee?: number -} diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/tsconfig.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/tsconfig.json deleted file mode 100644 index efd4c619..00000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/tsconfig.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "ESNext", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ - "src/*" - ] -} diff --git a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs b/cmd/crates/soroban-spec-typescript/src/boilerplate.rs deleted file mode 100644 index 7ffe6418..00000000 --- a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs +++ /dev/null @@ -1,218 +0,0 @@ -#![allow(non_snake_case)] -use heck::{ToLowerCamelCase, ToShoutySnakeCase}; -use include_dir::{include_dir, Dir}; -use std::{ - fs, - io::Write, - path::{Path, PathBuf}, -}; -use stellar_xdr::curr::ScSpecEntry; - -use super::generate; - -static PROJECT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src/project_template"); - -const NETWORK_PASSPHRASE_TESTNET: &str = "Test SDF Network ; September 2015"; -const NETWORK_PASSPHRASE_FUTURENET: &str = "Test SDF Future Network ; October 2022"; -const NETWORK_PASSPHRASE_STANDALONE: &str = "Standalone Network ; February 2017"; - -pub struct Project(PathBuf); - -impl TryInto for PathBuf { - type Error = std::io::Error; - - fn try_into(self) -> Result { - PROJECT_DIR.extract(&self)?; - Ok(Project(self)) - } -} - -impl AsRef for Project { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } -} - -impl Project { - /// Initialize a new JS client project, updating placeholder strings in the template and - /// appending functions for each method in the contract to the index.ts file. - /// - /// # Arguments - /// - /// * `contract_name` - The colloquial name of this contract that will be used in the README and package.json - /// * `contract_id` - The ID/address of the contract on the network. Will be overridable with environment variables. - /// * `rpc_url` - The RPC URL of the network where this contract is deployed. Will be overridable with environment variables. - /// * `network_passphrase` - The passphrase of the network where this contract is deployed. Will be overridable with environment variables. - /// * `spec` - The contract specification. - pub fn init( - &self, - contract_name: &str, - contract_id: &str, - rpc_url: &str, - network_passphrase: &str, - spec: &[ScSpecEntry], - ) -> std::io::Result<()> { - self.replace_placeholder_patterns(contract_name, contract_id, rpc_url, network_passphrase)?; - self.append_index_ts(spec, contract_id, network_passphrase) - } - - fn replace_placeholder_patterns( - &self, - contract_name: &str, - contract_id: &str, - rpc_url: &str, - network_passphrase: &str, - ) -> std::io::Result<()> { - let replacement_strings = &[ - ("INSERT_CONTRACT_NAME_HERE", contract_name), - ( - "INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE", - &contract_name.to_shouty_snake_case(), - ), - ( - "INSERT_CAMEL_CASE_CONTRACT_NAME_HERE", - &contract_name.to_lower_camel_case(), - ), - ("INSERT_CONTRACT_ID_HERE", contract_id), - ("INSERT_NETWORK_PASSPHRASE_HERE", network_passphrase), - ("INSERT_RPC_URL_HERE", rpc_url), - ]; - let root: &Path = self.as_ref(); - [ - "package.json", - "README.md", - "src/assembled-tx.ts", - "src/index.ts", - "src/method-options.ts", - ] - .into_iter() - .try_for_each(|file_name| { - let file = &root.join(file_name); - let mut contents = fs::read_to_string(file).unwrap(); - for (pattern, replacement) in replacement_strings { - contents = contents.replace(pattern, replacement); - } - fs::write(file, contents) - }) - } - - fn append_index_ts( - &self, - spec: &[ScSpecEntry], - contract_id: &str, - network_passphrase: &str, - ) -> std::io::Result<()> { - let networks = Project::format_networks_object(contract_id, network_passphrase); - let types_and_fns = generate(spec); - fs::OpenOptions::new() - .append(true) - .open(self.0.join("src/index.ts"))? - .write_all(format!("\n\n{networks}\n\n{types_and_fns}").as_bytes()) - } - - fn format_networks_object(contract_id: &str, network_passphrase: &str) -> String { - let network = match network_passphrase { - NETWORK_PASSPHRASE_TESTNET => "testnet", - NETWORK_PASSPHRASE_FUTURENET => "futurenet", - NETWORK_PASSPHRASE_STANDALONE => "standalone", - _ => "unknown", - }; - format!( - r#"export const networks = {{ - {network}: {{ - networkPassphrase: "{network_passphrase}", - contractId: "{contract_id}", - }} -}} as const"# - ) - } -} - -#[cfg(test)] -mod test { - use temp_dir::TempDir; - use walkdir::WalkDir; - - use super::*; - - const EXAMPLE_WASM: &[u8] = include_bytes!( - "../../../../target/wasm32-unknown-unknown/test-wasms/test_custom_types.wasm" - ); - - fn init(root: impl AsRef) -> std::io::Result { - let spec = soroban_spec::read::from_wasm(EXAMPLE_WASM).unwrap(); - let p: Project = root.as_ref().to_path_buf().try_into()?; - p.init( - "test_custom_types", - "CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE", - "https://rpc-futurenet.stellar.org:443", - "Test SDF Future Network ; October 2022", - &spec, - ) - .unwrap(); - Ok(p) - } - - // TODO : fix the test below : - // the test below should verify only a certain subset of the files were copied - // rather then the entire directory. - #[ignore] - #[test] - fn test_project_dir_location() { - // TODO: Ensure windows support - if cfg!(windows) { - return; - } - let temp_dir = TempDir::new().unwrap(); - let _: Project = init(temp_dir.path()).unwrap(); - let fixture = PathBuf::from("./fixtures/test_custom_types"); - assert_dirs_equal(temp_dir.path(), &fixture); - } - - #[ignore] - #[test] - fn build_package() { - let root = PathBuf::from("./fixtures/ts"); - std::fs::remove_dir_all(&root).unwrap_or_default(); - std::fs::create_dir_all(&root).unwrap(); - let _: Project = init(&root).unwrap(); - println!("Updated Snapshot!"); - } - - fn assert_dirs_equal>(dir1: P, dir2: P) { - let walker1 = WalkDir::new(&dir1); - let walker2 = WalkDir::new(&dir2); - - let mut paths1: Vec<_> = walker1.into_iter().collect::>().unwrap(); - let mut paths2: Vec<_> = walker2.into_iter().collect::>().unwrap(); - - paths1 - .sort_unstable_by_key(|entry| entry.path().strip_prefix(&dir1).unwrap().to_path_buf()); - paths2 - .sort_unstable_by_key(|entry| entry.path().strip_prefix(&dir2).unwrap().to_path_buf()); - - assert_eq!( - paths1.len(), - paths2.len(), - "{paths1:?}.len() != {paths2:?}.len()" - ); - - for (entry1, entry2) in paths1.iter().zip(paths2.iter()) { - let path1 = entry1.path(); - let path2 = entry2.path(); - - if path1.is_file() && path2.is_file() { - let content1 = fs::read_to_string(path1).unwrap(); - let content2 = fs::read_to_string(path2).unwrap(); - pretty_assertions::assert_eq!(content1, content2, "{:?} != {:?}", path1, path2); - } else if path1.is_dir() && path2.is_dir() { - continue; - } else { - panic!( - "{:?} is not a file", - if path1.is_file() { path2 } else { path1 } - ); - } - } - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/lib.rs b/cmd/crates/soroban-spec-typescript/src/lib.rs deleted file mode 100644 index 790f43b0..00000000 --- a/cmd/crates/soroban-spec-typescript/src/lib.rs +++ /dev/null @@ -1,377 +0,0 @@ -#![allow( - clippy::missing_errors_doc, - clippy::must_use_candidate, - clippy::missing_panics_doc -)] - -use std::{fs, io}; - -use crate::types::Type; -use heck::ToLowerCamelCase; -use itertools::Itertools; -use sha2::{Digest, Sha256}; -use stellar_xdr::curr::{Limits, ScSpecEntry, WriteXdr}; - -use types::Entry; - -use soroban_spec::read::{from_wasm, FromWasmError}; - -pub mod boilerplate; -mod types; -pub mod wrapper; - -#[derive(thiserror::Error, Debug)] -pub enum GenerateFromFileError { - #[error("reading file: {0}")] - Io(io::Error), - #[error("sha256 does not match, expected: {expected}")] - VerifySha256 { expected: String }, - #[error("parsing contract spec: {0}")] - Parse(stellar_xdr::curr::Error), - #[error("getting contract spec: {0}")] - GetSpec(FromWasmError), -} - -pub fn generate_from_file( - file: &str, - verify_sha256: Option<&str>, -) -> Result { - // Read file. - let wasm = fs::read(file).map_err(GenerateFromFileError::Io)?; - - // Produce hash for file. - let sha256 = Sha256::digest(&wasm); - let sha256 = format!("{sha256:x}"); - - if let Some(verify_sha256) = verify_sha256 { - if verify_sha256 != sha256 { - return Err(GenerateFromFileError::VerifySha256 { expected: sha256 }); - } - } - - // Generate code. - let json = generate_from_wasm(&wasm).map_err(GenerateFromFileError::GetSpec)?; - Ok(json) -} - -pub fn generate_from_wasm(wasm: &[u8]) -> Result { - let spec = from_wasm(wasm)?; - let json = generate(&spec); - Ok(json) -} - -fn generate_class(fns: &[Entry], spec: &[ScSpecEntry]) -> String { - let methods = fns.iter().map(entry_to_method).join("\n\n "); - let parsers = fns - .iter() - .filter_map(entry_to_parser) - .map(|(method, parser)| format!("{method}: {parser}")) - .join(",\n "); - let from_jsons = fns - .iter() - .filter_map(entry_to_parser) - .map(|(method, _)| { - format!("{method}: this.txFromJSON>") - }) - .join(",\n "); - let spec = spec - .iter() - .map(|s| format!("\"{}\"", s.to_xdr_base64(Limits::none()).unwrap())) - .join(",\n "); - format!( - r#"export class Contract {{ - spec: ContractSpec; - constructor(public readonly options: ClassOptions) {{ - this.spec = new ContractSpec([ - {spec} - ]); - }} - private readonly parsers = {{ - {parsers} - }}; - private txFromJSON = (json: string): AssembledTransaction => {{ - const {{ method, ...tx }} = JSON.parse(json) - return AssembledTransaction.fromJSON( - {{ - ...this.options, - method, - parseResultXdr: this.parsers[method], - }}, - tx, - ); - }} - public readonly fromJSON = {{ - {from_jsons} - }} - {methods} -}}"#, - ) -} - -pub fn generate(spec: &[ScSpecEntry]) -> String { - let mut collected: Vec<_> = spec.iter().map(Entry::from).collect(); - if !spec.iter().any(is_error_enum) { - collected.push(Entry::ErrorEnum { - doc: String::new(), - name: "Error".to_string(), - cases: vec![], - }); - } - let (fns, other): (Vec<_>, Vec<_>) = collected - .into_iter() - .partition(|entry| matches!(entry, Entry::Function { .. })); - let top = other.iter().map(entry_to_method).join("\n"); - let bottom = generate_class(&fns, spec); - format!("{top}\n\n{bottom}") -} - -fn doc_to_ts_doc(doc: &str, method: Option<&str>) -> String { - let header = if let Some(method) = method { - format!( - r#"/** - * Construct and simulate a {method} transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object."# - ) - } else { - "/**\n ".to_string() - }; - let footer = "\n */\n"; - let body = if doc.is_empty() { - String::new() - } else { - doc.split('\n').join("\n * ") - }; - format!(r#"{header}{body}{footer}"#) -} - -fn is_error_enum(entry: &ScSpecEntry) -> bool { - matches!(entry, ScSpecEntry::UdtErrorEnumV0(_)) -} - -const METHOD_OPTIONS: &str = r"{ - /** - * The fee to pay for the transaction. Default: 100. - */ - fee?: number, - }"; - -fn jsify_name(name: &String) -> String { - name.to_lower_camel_case() -} - -pub fn entry_to_parser(entry: &Entry) -> Option<(String, String)> { - if let Entry::Function { name, outputs, .. } = entry { - let mut is_result = false; - let mut return_type: String; - if outputs.is_empty() { - return_type = "void".to_owned(); - } else if outputs.len() == 1 { - return_type = type_to_ts(&outputs[0]); - is_result = return_type.starts_with("Result<"); - } else { - return_type = format!("readonly [{}]", outputs.iter().map(type_to_ts).join(", ")); - }; - - if is_result { - return_type = return_type - .strip_prefix("Result<") - .unwrap() - .strip_suffix('>') - .unwrap() - .to_owned(); - return_type = format!("Ok<{return_type}> | Err"); - } - - let output = outputs - .first() - .map(|_| format!("this.spec.funcResToNative(\"{name}\", result)")) - .unwrap_or_default(); - let parse_result_xdr = if return_type == "void" { - r"() => {}".to_owned() - } else if is_result { - format!( - r"(result: XDR_BASE64 | Err): {return_type} => {{ - if (result instanceof Err) return result - return new Ok({output}) - }}" - ) - } else { - format!(r"(result: XDR_BASE64): {return_type} => {output}") - }; - let js_name = jsify_name(name); - Some((js_name, parse_result_xdr)) - } else { - None - } -} - -#[allow(clippy::too_many_lines)] -pub fn entry_to_method(entry: &Entry) -> String { - match entry { - Entry::Function { - doc, name, inputs, .. - } => { - let input_vals = inputs.iter().map(func_input_to_arg_name).join(", "); - let input = (!inputs.is_empty()) - .then(|| { - format!( - "{{{input_vals}}}: {{{}}}, ", - inputs.iter().map(func_input_to_ts).join(", ") - ) - }) - .unwrap_or_default(); - let ts_doc = doc_to_ts_doc(doc, Some(name)); - let (js_name, _) = entry_to_parser(entry).unwrap(); - let parsed_scvals = inputs.iter().map(parse_arg_to_scval).join(", "); - let args = - format!("args: this.spec.funcArgsToScVals(\"{name}\", {{{parsed_scvals}}}),"); - let body = format!( - r#"return await AssembledTransaction.fromSimulation({{ - method: '{name}', - {args} - ...options, - ...this.options, - errorTypes: Errors, - parseResultXdr: this.parsers['{js_name}'], - }});"# - ); - format!( - r#" {ts_doc} {js_name} = async ({input}options: {METHOD_OPTIONS} = {{}}) => {{ - {body} - }} -"# - ) - } - Entry::Struct { doc, name, fields } => { - let docs = doc_to_ts_doc(doc, None); - let fields = fields.iter().map(field_to_ts).join("\n "); - format!( - r#"{docs}export interface {name} {{ - {fields} -}} -"# - ) - } - - Entry::TupleStruct { doc, name, fields } => { - let docs = doc_to_ts_doc(doc, None); - let fields = fields.iter().map(type_to_ts).join(", "); - format!("{docs}export type {name} = readonly [{fields}];") - } - - Entry::Union { name, doc, cases } => { - let doc = doc_to_ts_doc(doc, None); - let cases = cases.iter().map(case_to_ts).join(" | "); - - format!( - r#"{doc}export type {name} = {cases}; -"# - ) - } - Entry::Enum { doc, name, cases } => { - let doc = doc_to_ts_doc(doc, None); - let cases = cases.iter().map(enum_case_to_ts).join("\n "); - let name = (name == "Error") - .then(|| format!("{name}s")) - .unwrap_or(name.to_string()); - format!( - r#"{doc}export enum {name} {{ - {cases} -}} -"#, - ) - } - Entry::ErrorEnum { doc, cases, .. } => { - let doc = doc_to_ts_doc(doc, None); - let cases = cases - .iter() - .map(|c| format!("{}: {{message:\"{}\"}}", c.value, c.doc)) - .join(",\n "); - format!( - r#"{doc}export const Errors = {{ -{cases} -}}"# - ) - } - } -} - -fn enum_case_to_ts(case: &types::EnumCase) -> String { - let types::EnumCase { name, value, .. } = case; - format!("{name} = {value},") -} - -fn case_to_ts(case: &types::UnionCase) -> String { - let types::UnionCase { name, values, .. } = case; - format!( - "{{tag: \"{name}\", values: {}}}", - type_to_ts(&Type::Tuple { - elements: values.clone(), - }) - ) -} - -fn field_to_ts(field: &types::StructField) -> String { - let types::StructField { doc, name, value } = field; - let doc = doc_to_ts_doc(doc, None); - let type_ = type_to_ts(value); - format!("{doc}{name}: {type_};") -} - -pub fn func_input_to_ts(input: &types::FunctionInput) -> String { - let types::FunctionInput { name, value, .. } = input; - let type_ = type_to_ts(value); - format!("{name}: {type_}") -} - -pub fn func_input_to_arg_name(input: &types::FunctionInput) -> String { - let types::FunctionInput { name, .. } = input; - name.to_string() -} - -pub fn parse_arg_to_scval(input: &types::FunctionInput) -> String { - let types::FunctionInput { name, value, .. } = input; - match value { - types::Type::Address => format!("{name}: new Address({name})"), - _ => name.to_string(), - } -} - -pub fn type_to_ts(value: &types::Type) -> String { - match value { - types::Type::U64 => "u64".to_owned(), - types::Type::I64 => "i64".to_owned(), - types::Type::U128 => "u128".to_owned(), - types::Type::I128 => "i128".to_owned(), - types::Type::U32 => "u32".to_owned(), - types::Type::I32 => "i32".to_owned(), - types::Type::Bool => "boolean".to_owned(), - types::Type::Symbol | types::Type::String => "string".to_owned(), - types::Type::Map { key, value } => { - format!("Map<{}, {}>", type_to_ts(key), type_to_ts(value)) - } - types::Type::Option { value } => format!("Option<{}>", type_to_ts(value)), - types::Type::Result { value, .. } => { - format!("Result<{}>", type_to_ts(value)) - } - types::Type::Vec { element } => format!("Array<{}>", type_to_ts(element)), - types::Type::Tuple { elements } => { - if elements.is_empty() { - "void".to_owned() - } else { - format!("readonly [{}]", elements.iter().map(type_to_ts).join(", ")) - } - } - types::Type::Custom { name } => name.clone(), - // TODO: Figure out what js type to map this to. There is already an `Error_` one that - // ahalabs have added in the bindings, so.. maybe rename that? - types::Type::Val => "any".to_owned(), - types::Type::Error { .. } => "Error_".to_owned(), - types::Type::Address => "string".to_string(), - types::Type::Bytes | types::Type::BytesN { .. } => "Buffer".to_string(), - types::Type::Void => "void".to_owned(), - types::Type::U256 => "u256".to_string(), - types::Type::I256 => "i256".to_string(), - types::Type::Timepoint => "Timepoint".to_string(), - types::Type::Duration => "Duration".to_string(), - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/.gitignore b/cmd/crates/soroban-spec-typescript/src/project_template/.gitignore deleted file mode 100644 index 72aae85f..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -out/ diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/README.md b/cmd/crates/soroban-spec-typescript/src/project_template/README.md deleted file mode 100644 index 3995e52b..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# INSERT_CONTRACT_NAME_HERE JS - -JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `INSERT_CONTRACT_NAME_HERE` via Soroban RPC. - -This library was automatically generated by Soroban CLI using a command similar to: - -```bash -soroban contract bindings ts \ - --rpc-url INSERT_RPC_URL_HERE \ - --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \ - --contract-id INSERT_CONTRACT_ID_HERE \ - --output-dir ./path/to/INSERT_CONTRACT_NAME_HERE -``` - -The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily. - -# To publish or not to publish - -This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command. - -But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path: - -```json -"dependencies": { - "INSERT_CONTRACT_NAME_HERE": "./path/to/this/folder" -} -``` - -However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract. - -```json -"scripts": { - "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name INSERT_CONTRACT_NAME_HERE" -} -``` - -Obviously you need to adjust the above command based on the actual command you used to generate the library. - -# Use it - -Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods: - -```js -import { Contract, networks } from "INSERT_CONTRACT_NAME_HERE" - -const contract = new Contract({ - ...networks.futurenet, // for example; check which networks this library exports - rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers -}) - -contract.| -``` - -As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code. diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/package.json b/cmd/crates/soroban-spec-typescript/src/project_template/package.json deleted file mode 100644 index e32d0f5e..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "0.0.0", - "name": "INSERT_CONTRACT_NAME_HERE", - "dependencies": { - "@stellar/freighter-api": "1.7.1", - "buffer": "6.0.3", - "@stellar/stellar-sdk": "11.2.0" - }, - "scripts": { - "build": "node ./scripts/build.mjs" - }, - "exports": { - "require": "./dist/cjs/index.js", - "import": "./dist/esm/index.js" - }, - "typings": "dist/types/index.d.ts", - "devDependencies": { - "typescript": "5.3.3" - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/build.mjs b/cmd/crates/soroban-spec-typescript/src/project_template/scripts/build.mjs deleted file mode 100644 index 15a17042..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/build.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import { spawnSync } from "node:child_process" -import fs from "node:fs" -import path from "node:path" - -const buildDir = "./dist" - -const { error, stderr } = spawnSync("tsc", ["-b", "./scripts/tsconfig.cjs.json", "./scripts/tsconfig.esm.json", "./scripts/tsconfig.types.json"], { stdio: "inherit" }) - -if (error) { - console.error(stderr) - console.error(error) - throw error -} - -function createEsmModulePackageJson() { - fs.readdir(buildDir, function (err, dirs) { - if (err) { - throw err - } - dirs.forEach(function (dir) { - if (dir === "esm") { - // 1. add package.json file with "type": "module" - var packageJsonFile = path.join(buildDir, dir, "/package.json") - if (!fs.existsSync(packageJsonFile)) { - fs.writeFileSync( - packageJsonFile, - '{"type": "module"}', - 'utf8', - err => { if (err) throw err } - ) - } - } - }) - }) -} - -createEsmModulePackageJson() diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.cjs.json b/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.cjs.json deleted file mode 100644 index 542ea86d..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.cjs.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/cjs", - "module": "commonjs" - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.esm.json b/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.esm.json deleted file mode 100644 index 92b45277..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.esm.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/esm", - "module": "esnext" - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.types.json b/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.types.json deleted file mode 100644 index 8a24fc13..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/scripts/tsconfig.types.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../dist/types", - "declaration": true, - "emitDeclarationOnly": true - } -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts b/cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts deleted file mode 100644 index f4f892d4..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts +++ /dev/null @@ -1,664 +0,0 @@ -import { - Account, - Address, - Contract, - Operation, - SorobanRpc, - StrKey, - TimeoutInfinite, - TransactionBuilder, - authorizeEntry, - hash, - nativeToScVal, - xdr, - BASE_FEE, -} from "@stellar/stellar-sdk"; -import type { Memo, MemoType, Transaction } from "@stellar/stellar-sdk"; -import { Buffer } from "buffer"; -import type { - ClassOptions, - MethodOptions, - Wallet, - XDR_BASE64, -} from "./method-options.js"; - -export type Tx = Transaction, Operation[]> - -export class ExpiredStateError extends Error { } -export class NeedsMoreSignaturesError extends Error { } -export class WalletDisconnectedError extends Error { } -export class SendResultOnlyError extends Error { } -export class SendFailedError extends Error { } -export class NoUnsignedNonInvokerAuthEntriesError extends Error { } - -type SendTx = SorobanRpc.Api.SendTransactionResponse; -type GetTx = SorobanRpc.Api.GetTransactionResponse; - -export type u32 = number; -export type i32 = number; -export type u64 = bigint; -export type i64 = bigint; -export type u128 = bigint; -export type i128 = bigint; -export type u256 = bigint; -export type i256 = bigint; -export type Option = T | undefined; -export type Typepoint = bigint; -export type Duration = bigint; -export {Address}; - -/// Error interface containing the error message -export interface Error_ { message: string }; - -export interface Result { - unwrap(): T, - unwrapErr(): E, - isOk(): boolean, - isErr(): boolean, -}; - -export class Ok implements Result { - constructor(readonly value: T) { } - unwrapErr(): E { - throw new Error('No error'); - } - unwrap(): T { - return this.value; - } - - isOk(): boolean { - return true; - } - - isErr(): boolean { - return !this.isOk() - } -} - -export class Err implements Result { - constructor(readonly error: E) { } - unwrapErr(): E { - return this.error; - } - unwrap(): never { - throw new Error(this.error.message); - } - - isOk(): boolean { - return false; - } - - isErr(): boolean { - return !this.isOk() - } -} - -export const contractErrorPattern = /Error\(Contract, #(\d+)\)/; - -type AssembledTransactionOptions = MethodOptions & - ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal | Err) => T; - }; - -export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF" - -export class AssembledTransaction { - public raw: Tx - private simulation?: SorobanRpc.Api.SimulateTransactionResponse - private simulationResult?: SorobanRpc.Api.SimulateHostFunctionResult - private simulationTransactionData?: xdr.SorobanTransactionData - private server: SorobanRpc.Server - - toJSON() { - return JSON.stringify({ - method: this.options.method, - tx: this.raw?.toXDR(), - simulationResult: { - auth: this.simulationData.result.auth.map(a => a.toXDR('base64')), - retval: this.simulationData.result.retval.toXDR('base64'), - }, - simulationTransactionData: this.simulationData.transactionData.toXDR('base64'), - }) - } - - static fromJSON( - options: Omit, 'args'>, - { tx, simulationResult, simulationTransactionData }: - { - tx: XDR_BASE64, - simulationResult: { - auth: XDR_BASE64[], - retval: XDR_BASE64, - }, - simulationTransactionData: XDR_BASE64, - } - ): AssembledTransaction { - const txn = new AssembledTransaction(options) - txn.raw = TransactionBuilder.fromXDR(tx, options.networkPassphrase) as Tx - txn.simulationResult = { - auth: simulationResult.auth.map(a => xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64')), - retval: xdr.ScVal.fromXDR(simulationResult.retval, 'base64'), - } - txn.simulationTransactionData = xdr.SorobanTransactionData.fromXDR(simulationTransactionData, 'base64') - return txn - } - - private constructor(public options: AssembledTransactionOptions) { - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - } - - static async fromSimulation(options: AssembledTransactionOptions): Promise> { - const tx = new AssembledTransaction(options) - const contract = new Contract(options.contractId); - - tx.raw = new TransactionBuilder(await tx.getAccount(), { - fee: options.fee?.toString(10) ?? BASE_FEE, - networkPassphrase: options.networkPassphrase, - }) - .addOperation(contract.call(options.method, ...(options.args ?? []))) - .setTimeout(TimeoutInfinite) - .build(); - - return await tx.simulate() - } - - simulate = async (): Promise => { - if (!this.raw) throw new Error('Transaction has not yet been assembled') - this.simulation = await this.server.simulateTransaction(this.raw); - - if (SorobanRpc.Api.isSimulationSuccess(this.simulation)) { - this.raw = SorobanRpc.assembleTransaction( - this.raw, - this.simulation - ).build() - } - - return this - } - - get simulationData(): { - result: SorobanRpc.Api.SimulateHostFunctionResult - transactionData: xdr.SorobanTransactionData - } { - if (this.simulationResult && this.simulationTransactionData) { - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData, - } - } - // else, we know we just did the simulation on this machine - const simulation = this.simulation! - if (SorobanRpc.Api.isSimulationError(simulation)) { - throw new Error(`Transaction simulation failed: "${simulation.error}"`) - } - - if (SorobanRpc.Api.isSimulationRestore(simulation)) { - throw new ExpiredStateError(`You need to restore some contract state before you can invoke this method. ${JSON.stringify(simulation, null, 2)}`) - } - - if (!simulation.result) { - throw new Error(`Expected an invocation simulation, but got no 'result' field. Simulation: ${JSON.stringify(simulation, null, 2)}`) - } - - // add to object for serialization & deserialization - this.simulationResult = simulation.result - this.simulationTransactionData = simulation.transactionData.build() - - return { - result: this.simulationResult, - transactionData: this.simulationTransactionData!, - } - } - - get result(): T { - try { - return this.options.parseResultXdr(this.simulationData.result.retval) - } catch (e) { - let err = this.parseError(e.toString()) - if (err) return err as T - throw e - } - } - - parseError(errorMessage: string): Err | undefined { - if (!this.options.errorTypes) return - const match = errorMessage.match(contractErrorPattern) - if (!match) return - let i = parseInt(match[1], 10) - let err = this.options.errorTypes[i] - if (err) return new Err(err) - } - - getWallet = async (): Promise => { - return this.options.wallet ?? (await import("@stellar/freighter-api")).default - } - - getPublicKey = async (): Promise => { - const wallet = await this.getWallet() - if (await wallet.isConnected() && await wallet.isAllowed()) { - return (await wallet.getUserInfo()).publicKey - } - } - - /** - * Get account details from the Soroban network for the publicKey currently - * selected in user's wallet. If not connected to Freighter, use placeholder - * null account. - */ - getAccount = async (): Promise => { - const publicKey = await this.getPublicKey() - return publicKey - ? await this.server.getAccount(publicKey) - : new Account(NULL_ACCOUNT, "0") - } - - /** - * Sign the transaction with the `wallet` (default Freighter), then send to - * the network and return a `SentTransaction` that keeps track of all the - * attempts to send and fetch the transaction from the network. - */ - signAndSend = async ({ secondsToWait = 10, force = false }: { - /** - * Wait `secondsToWait` seconds (default: 10) for both the transaction to SEND successfully (will keep trying if the server returns `TRY_AGAIN_LATER`), as well as for the transaction to COMPLETE (will keep checking if the server returns `PENDING`). - */ - secondsToWait?: number - /** - * If `true`, sign and send the transaction even if it is a read call. - */ - force?: boolean - } = {}): Promise> => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated') - } - - if (!force && this.isReadCall) { - throw new Error('This is a read call. It requires no signature or sending. Use `force: true` to sign and send anyway.') - } - - if (!await this.hasRealInvoker()) { - throw new WalletDisconnectedError('Wallet is not connected') - } - - if (this.raw.source !== (await this.getAccount()).accountId()) { - throw new Error(`You must submit the transaction with the account that originally created it. Please switch to the wallet with "${this.raw.source}" as its public key.`) - } - - if ((await this.needsNonInvokerSigningBy()).length) { - throw new NeedsMoreSignaturesError( - 'Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.' - ) - } - - return await SentTransaction.init(this.options, this, secondsToWait); - } - - getStorageExpiration = async () => { - const entryRes = await this.server.getLedgerEntries( - new Contract(this.options.contractId).getFootprint() - ) - if ( - !entryRes.entries || - !entryRes.entries.length || - !entryRes.entries[0].liveUntilLedgerSeq - ) throw new Error('failed to get ledger entry') - return entryRes.entries[0].liveUntilLedgerSeq - } - - /** - * Get a list of accounts, other than the invoker of the simulation, that - * need to sign auth entries in this transaction. - * - * Soroban allows multiple people to sign a transaction. Someone needs to - * sign the final transaction envelope; this person/account is called the - * _invoker_, or _source_. Other accounts might need to sign individual auth - * entries in the transaction, if they're not also the invoker. - * - * This function returns a list of accounts that need to sign auth entries, - * assuming that the same invoker/source account will sign the final - * transaction envelope as signed the initial simulation. - * - * One at a time, for each public key in this array, you will need to - * serialize this transaction with `toJSON`, send to the owner of that key, - * deserialize the transaction with `txFromJson`, and call - * {@link signAuthEntries}. Then re-serialize and send to the next account - * in this list. - */ - needsNonInvokerSigningBy = async ({ - includeAlreadySigned = false, - }: { - /** - * Whether or not to include auth entries that have already been signed. Default: false - */ - includeAlreadySigned?: boolean - } = {}): Promise => { - if (!this.raw) { - throw new Error('Transaction has not yet been simulated') - } - - // We expect that any transaction constructed by these libraries has a - // single operation, which is an InvokeHostFunction operation. The host - // function being invoked is the contract method call. - if (!("operations" in this.raw)) { - throw new Error( - `Unexpected Transaction type; no operations: ${JSON.stringify(this.raw) - }` - ) - } - const rawInvokeHostFunctionOp = this.raw - .operations[0] as Operation.InvokeHostFunction - - return [...new Set((rawInvokeHostFunctionOp.auth ?? []).filter(entry => - entry.credentials().switch() === - xdr.SorobanCredentialsType.sorobanCredentialsAddress() && - ( - includeAlreadySigned || - entry.credentials().address().signature().switch().name === 'scvVoid' - ) - ).map(entry => StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519() - )))] - } - - preImageFor( - entry: xdr.SorobanAuthorizationEntry, - signatureExpirationLedger: number - ): xdr.HashIdPreimage { - const addrAuth = entry.credentials().address() - return xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( - new xdr.HashIdPreimageSorobanAuthorization({ - networkId: hash(Buffer.from(this.options.networkPassphrase)), - nonce: addrAuth.nonce(), - invocation: entry.rootInvocation(), - signatureExpirationLedger, - }), - ) - } - - /** - * If {@link needsNonInvokerSigningBy} returns a non-empty list, you can serialize - * the transaction with `toJSON`, send it to the owner of one of the public keys - * in the map, deserialize with `txFromJSON`, and call this method on their - * machine. Internally, this will use `signAuthEntry` function from connected - * `wallet` for each. - * - * Then, re-serialize the transaction and either send to the next - * `needsNonInvokerSigningBy` owner, or send it back to the original account - * who simulated the transaction so they can {@link sign} the transaction - * envelope and {@link send} it to the network. - * - * Sending to all `needsNonInvokerSigningBy` owners in parallel is not currently - * supported! - */ - signAuthEntries = async ( - /** - * When to set each auth entry to expire. Could be any number of blocks in - * the future. Can be supplied as a promise or a raw number. Default: - * contract's current `persistent` storage expiration date/ledger - * number/block. - */ - expiration: number | Promise = this.getStorageExpiration() - ): Promise => { - if (!this.raw) throw new Error('Transaction has not yet been assembled or simulated') - const needsNonInvokerSigningBy = await this.needsNonInvokerSigningBy() - - if (!needsNonInvokerSigningBy) throw new NoUnsignedNonInvokerAuthEntriesError('No unsigned non-invoker auth entries; maybe you already signed?') - const publicKey = await this.getPublicKey() - if (!publicKey) throw new Error('Could not get public key from wallet; maybe Freighter is not signed in?') - if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) throw new Error(`No auth entries for public key "${publicKey}"`) - const wallet = await this.getWallet() - - const rawInvokeHostFunctionOp = this.raw - .operations[0] as Operation.InvokeHostFunction - - const authEntries = rawInvokeHostFunctionOp.auth ?? [] - - for (const [i, entry] of authEntries.entries()) { - if ( - entry.credentials().switch() !== - xdr.SorobanCredentialsType.sorobanCredentialsAddress() - ) { - // if the invoker/source account, then the entry doesn't need explicit - // signature, since the tx envelope is already signed by the source - // account, so only check for sorobanCredentialsAddress - continue - } - const pk = StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519() - ) - - // this auth entry needs to be signed by a different account - // (or maybe already was!) - if (pk !== publicKey) continue - - authEntries[i] = await authorizeEntry( - entry, - async preimage => Buffer.from( - await wallet.signAuthEntry(preimage.toXDR('base64')), - 'base64' - ), - await expiration, - this.options.networkPassphrase - ) - } - } - - get isReadCall(): boolean { - const authsCount = this.simulationData.result.auth.length; - const writeLength = this.simulationData.transactionData.resources().footprint().readWrite().length - return (authsCount === 0) && (writeLength === 0); - } - - hasRealInvoker = async (): Promise => { - const account = await this.getAccount() - return account.accountId() !== NULL_ACCOUNT - } -} - -/** - * A transaction that has been sent to the Soroban network. This happens in two steps: - * - * 1. `sendTransaction`: initial submission of the transaction to the network. - * This step can run into problems, and will be retried with exponential - * backoff if it does. See all attempts in `sendTransactionResponseAll` and the - * most recent attempt in `sendTransactionResponse`. - * 2. `getTransaction`: once the transaction has been submitted to the network - * successfully, you need to wait for it to finalize to get the results of the - * transaction. This step can also run into problems, and will be retried with - * exponential backoff if it does. See all attempts in - * `getTransactionResponseAll` and the most recent attempt in - * `getTransactionResponse`. - */ -class SentTransaction { - public server: SorobanRpc.Server - public signed: Tx - public sendTransactionResponse?: SendTx - public sendTransactionResponseAll?: SendTx[] - public getTransactionResponse?: GetTx - public getTransactionResponseAll?: GetTx[] - - constructor(public options: AssembledTransactionOptions, public assembled: AssembledTransaction) { - this.server = new SorobanRpc.Server(this.options.rpcUrl, { - allowHttp: this.options.rpcUrl.startsWith("http://"), - }); - this.assembled = assembled - } - - static init = async ( - options: AssembledTransactionOptions, - assembled: AssembledTransaction, - secondsToWait: number = 10 - ): Promise> => { - const tx = new SentTransaction(options, assembled) - return await tx.send(secondsToWait) - } - - private send = async (secondsToWait: number = 10): Promise => { - const wallet = await this.assembled.getWallet() - - this.sendTransactionResponseAll = await withExponentialBackoff( - async (previousFailure) => { - if (previousFailure) { - // Increment transaction sequence number and resimulate before trying again - - // Soroban transaction can only have 1 operation - const op = this.assembled.raw.operations[0] as Operation.InvokeHostFunction; - - this.assembled.raw = new TransactionBuilder(await this.assembled.getAccount(), { - fee: this.assembled.raw.fee, - networkPassphrase: this.options.networkPassphrase, - }) - .setTimeout(TimeoutInfinite) - .addOperation( - Operation.invokeHostFunction({ ...op, auth: op.auth ?? [] }), - ) - .build() - - await this.assembled.simulate() - } - - const signature = await wallet.signTransaction(this.assembled.raw.toXDR(), { - networkPassphrase: this.options.networkPassphrase, - }); - - this.signed = TransactionBuilder.fromXDR( - signature, - this.options.networkPassphrase - ) as Tx - - return this.server.sendTransaction(this.signed) - }, - resp => resp.status !== "PENDING", - secondsToWait - ) - - this.sendTransactionResponse = this.sendTransactionResponseAll[this.sendTransactionResponseAll.length - 1] - - if (this.sendTransactionResponse.status !== "PENDING") { - throw new Error( - `Tried to resubmit transaction for ${secondsToWait - } seconds, but it's still failing. ` + - `All attempts: ${JSON.stringify( - this.sendTransactionResponseAll, - null, - 2 - )}` - ); - } - - const { hash } = this.sendTransactionResponse - - this.getTransactionResponseAll = await withExponentialBackoff( - () => this.server.getTransaction(hash), - resp => resp.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND, - secondsToWait - ) - - this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1] - if (this.getTransactionResponse.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND) { - console.error( - `Waited ${secondsToWait - } seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Sent transaction: ${JSON.stringify( - this.sendTransactionResponse, - null, - 2 - )}\n` + - `All attempts to get the result: ${JSON.stringify( - this.getTransactionResponseAll, - null, - 2 - )}` - ); - } - - return this; - } - - get result(): T { - // 1. check if transaction was submitted and awaited with `getTransaction` - if ( - "getTransactionResponse" in this && - this.getTransactionResponse - ) { - // getTransactionResponse has a `returnValue` field unless it failed - if ("returnValue" in this.getTransactionResponse) { - return this.options.parseResultXdr(this.getTransactionResponse.returnValue!) - } - - // if "returnValue" not present, the transaction failed; return without parsing the result - throw new Error("Transaction failed! Cannot parse result.") - } - - // 2. otherwise, maybe it was merely sent with `sendTransaction` - if (this.sendTransactionResponse) { - const errorResult = this.sendTransactionResponse.errorResult?.result() - if (errorResult) { - throw new SendFailedError( - `Transaction simulation looked correct, but attempting to send the transaction failed. Check \`simulation\` and \`sendTransactionResponseAll\` to troubleshoot. Decoded \`sendTransactionResponse.errorResultXdr\`: ${errorResult}` - ) - } - throw new SendResultOnlyError( - `Transaction was sent to the network, but not yet awaited. No result to show. Await transaction completion with \`getTransaction(sendTransactionResponse.hash)\`` - ) - } - - // 3. finally, if neither of those are present, throw an error - throw new Error(`Sending transaction failed: ${JSON.stringify(this.assembled)}`) - } -} - -/** - * Keep calling a `fn` for `secondsToWait` seconds, if `keepWaitingIf` is true. - * Returns an array of all attempts to call the function. - */ -async function withExponentialBackoff( - fn: (previousFailure?: T) => Promise, - keepWaitingIf: (result: T) => boolean, - secondsToWait: number, - exponentialFactor = 1.5, - verbose = false, -): Promise { - const attempts: T[] = [] - - let count = 0 - attempts.push(await fn()) - if (!keepWaitingIf(attempts[attempts.length - 1])) return attempts - - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf() - let waitTime = 1000 - let totalWaitTime = waitTime - - while (Date.now() < waitUntil && keepWaitingIf(attempts[attempts.length - 1])) { - count++ - // Wait a beat - if (verbose) { - console.info(`Waiting ${waitTime}ms before trying again (bringing the total wait time to ${totalWaitTime}ms so far, of total ${secondsToWait * 1000}ms)`) - } - await new Promise(res => setTimeout(res, waitTime)) - // Exponential backoff - waitTime = waitTime * exponentialFactor; - if (new Date(Date.now() + waitTime).valueOf() > waitUntil) { - waitTime = waitUntil - Date.now() - if (verbose) { - console.info(`was gonna wait too long; new waitTime: ${waitTime}ms`) - } - } - totalWaitTime = waitTime + totalWaitTime - // Try again - attempts.push(await fn(attempts[attempts.length - 1])) - if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { - console.info( - `${count}. Called ${fn}; ${attempts.length - } prev attempts. Most recent: ${JSON.stringify(attempts[attempts.length - 1], null, 2) - }` - ) - } - } - - return attempts -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/src/index.ts b/cmd/crates/soroban-spec-typescript/src/project_template/src/index.ts deleted file mode 100644 index f7ad0b66..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ContractSpec, Address } from '@stellar/stellar-sdk'; -import { Buffer } from "buffer"; -import { AssembledTransaction, Ok, Err } from './assembled-tx.js'; -import type { - u32, - i32, - u64, - i64, - u128, - i128, - u256, - i256, - Option, - Typepoint, - Duration, - Error_, - Result, -} from './assembled-tx.js'; -import type { ClassOptions, XDR_BASE64 } from './method-options.js'; - -export * from './assembled-tx.js'; -export * from './method-options.js'; - -if (typeof window !== 'undefined') { - //@ts-ignore Buffer exists - window.Buffer = window.Buffer || Buffer; -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/src/method-options.ts b/cmd/crates/soroban-spec-typescript/src/project_template/src/method-options.ts deleted file mode 100644 index d1ff142f..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/method-options.ts +++ /dev/null @@ -1,50 +0,0 @@ -// defined this way so typeahead shows full union, not named alias -let responseTypes: 'simulated' | 'full' | undefined -export type ResponseTypes = typeof responseTypes - -export type XDR_BASE64 = string - -export interface Wallet { - isConnected: () => Promise, - isAllowed: () => Promise, - getUserInfo: () => Promise<{ publicKey?: string }>, - signTransaction: (tx: XDR_BASE64, opts?: { - network?: string, - networkPassphrase?: string, - accountToSign?: string, - }) => Promise, - signAuthEntry: ( - entryXdr: XDR_BASE64, - opts?: { - accountToSign?: string; - } - ) => Promise -} - -export type ClassOptions = { - contractId: string - networkPassphrase: string - rpcUrl: string - errorTypes?: Record - /** - * A Wallet interface, such as Freighter, that has the methods `isConnected`, `isAllowed`, `getUserInfo`, and `signTransaction`. If not provided, will attempt to import and use Freighter. Example: - * - * @example - * ```ts - * import freighter from "@stellar/freighter-api"; - * import { Contract } from "INSERT_CONTRACT_NAME_HERE"; - * const contract = new Contract({ - * …, - * wallet: freighter, - * }) - * ``` - */ - wallet?: Wallet -} - -export type MethodOptions = { - /** - * The fee to pay for the transaction. Default: soroban-sdk's BASE_FEE ('100') - */ - fee?: number -} diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/tsconfig.json b/cmd/crates/soroban-spec-typescript/src/project_template/tsconfig.json deleted file mode 100644 index efd4c619..00000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/tsconfig.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "ESNext", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ - "src/*" - ] -} diff --git a/cmd/crates/soroban-spec-typescript/src/types.rs b/cmd/crates/soroban-spec-typescript/src/types.rs deleted file mode 100644 index 02511843..00000000 --- a/cmd/crates/soroban-spec-typescript/src/types.rs +++ /dev/null @@ -1,259 +0,0 @@ -use serde::Serialize; -use stellar_xdr::curr::{ - ScSpecEntry, ScSpecFunctionInputV0, ScSpecTypeDef, ScSpecUdtEnumCaseV0, - ScSpecUdtErrorEnumCaseV0, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, ScSpecUdtUnionCaseV0, -}; - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct StructField { - pub doc: String, - pub name: String, - pub value: Type, -} - -impl From<&ScSpecUdtStructFieldV0> for StructField { - fn from(f: &ScSpecUdtStructFieldV0) -> Self { - StructField { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - value: (&f.type_).into(), - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct FunctionInput { - pub doc: String, - pub name: String, - pub value: Type, -} - -impl From<&ScSpecFunctionInputV0> for FunctionInput { - fn from(f: &ScSpecFunctionInputV0) -> Self { - FunctionInput { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - value: (&f.type_).into(), - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UnionCase { - pub doc: String, - pub name: String, - pub values: Vec, -} - -impl From<&ScSpecUdtUnionCaseV0> for UnionCase { - fn from(c: &ScSpecUdtUnionCaseV0) -> Self { - let (doc, name, values) = match c { - ScSpecUdtUnionCaseV0::VoidV0(v) => ( - v.doc.to_utf8_string_lossy(), - v.name.to_utf8_string_lossy(), - vec![], - ), - ScSpecUdtUnionCaseV0::TupleV0(t) => ( - t.doc.to_utf8_string_lossy(), - t.name.to_utf8_string_lossy(), - t.type_.iter().map(Type::from).collect(), - ), - }; - UnionCase { doc, name, values } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct EnumCase { - pub doc: String, - pub name: String, - pub value: u32, -} - -impl From<&ScSpecUdtEnumCaseV0> for EnumCase { - fn from(c: &ScSpecUdtEnumCaseV0) -> Self { - EnumCase { - doc: c.doc.to_utf8_string_lossy(), - name: c.name.to_utf8_string_lossy(), - value: c.value, - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ErrorEnumCase { - pub doc: String, - pub name: String, - pub value: u32, -} - -impl From<&ScSpecUdtErrorEnumCaseV0> for ErrorEnumCase { - fn from(c: &ScSpecUdtErrorEnumCaseV0) -> Self { - ErrorEnumCase { - doc: c.doc.to_utf8_string_lossy(), - name: c.name.to_utf8_string_lossy(), - value: c.value, - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(tag = "type")] -#[serde(rename_all = "camelCase")] -pub enum Type { - Void, - Val, - U64, - I64, - U32, - I32, - U128, - I128, - U256, - I256, - Bool, - Symbol, - Bytes, - String, - Address, - Timepoint, - Duration, - Map { key: Box, value: Box }, - Option { value: Box }, - Result { value: Box, error: Box }, - Vec { element: Box }, - BytesN { n: u32 }, - Tuple { elements: Vec }, - Error { message: Option }, - Custom { name: String }, -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(tag = "type")] -#[serde(rename_all = "camelCase")] -pub enum Entry { - Function { - doc: String, - name: String, - inputs: Vec, - outputs: Vec, - }, - Struct { - doc: String, - name: String, - fields: Vec, - }, - TupleStruct { - doc: String, - name: String, - fields: Vec, - }, - Union { - doc: String, - name: String, - cases: Vec, - }, - Enum { - doc: String, - name: String, - cases: Vec, - }, - ErrorEnum { - doc: String, - name: String, - cases: Vec, - }, -} - -impl From<&ScSpecTypeDef> for Type { - fn from(spec: &ScSpecTypeDef) -> Self { - match spec { - ScSpecTypeDef::Map(map) => Type::Map { - key: Box::new(Type::from(map.key_type.as_ref())), - value: Box::new(Type::from(map.value_type.as_ref())), - }, - ScSpecTypeDef::Option(opt) => Type::Option { - value: Box::new(Type::from(opt.value_type.as_ref())), - }, - ScSpecTypeDef::Result(res) => Type::Result { - value: Box::new(Type::from(res.ok_type.as_ref())), - error: Box::new(Type::from(res.error_type.as_ref())), - }, - ScSpecTypeDef::Tuple(tuple) => Type::Tuple { - elements: tuple.value_types.iter().map(Type::from).collect(), - }, - ScSpecTypeDef::Vec(vec) => Type::Vec { - element: Box::new(Type::from(vec.element_type.as_ref())), - }, - ScSpecTypeDef::Udt(udt) => Type::Custom { - name: udt.name.to_utf8_string_lossy(), - }, - ScSpecTypeDef::BytesN(b) => Type::BytesN { n: b.n }, - ScSpecTypeDef::Val => Type::Val, - ScSpecTypeDef::U64 => Type::U64, - ScSpecTypeDef::I64 => Type::I64, - ScSpecTypeDef::U32 => Type::U32, - ScSpecTypeDef::I32 => Type::I32, - ScSpecTypeDef::U128 => Type::U128, - ScSpecTypeDef::I128 => Type::I128, - ScSpecTypeDef::U256 => Type::U256, - ScSpecTypeDef::I256 => Type::I256, - ScSpecTypeDef::Bool => Type::Bool, - ScSpecTypeDef::Symbol => Type::Symbol, - ScSpecTypeDef::Error => Type::Error { message: None }, - ScSpecTypeDef::Bytes => Type::Bytes, - ScSpecTypeDef::String => Type::String, - ScSpecTypeDef::Address => Type::Address, - ScSpecTypeDef::Void => Type::Void, - ScSpecTypeDef::Timepoint => Type::Timepoint, - ScSpecTypeDef::Duration => Type::Duration, - } - } -} - -impl From<&ScSpecEntry> for Entry { - fn from(spec: &ScSpecEntry) -> Self { - match spec { - ScSpecEntry::FunctionV0(f) => Entry::Function { - doc: f.doc.to_utf8_string_lossy(), - name: f.name.to_utf8_string_lossy(), - inputs: f.inputs.iter().map(Into::into).collect(), - outputs: f.outputs.iter().map(Into::into).collect(), - }, - ScSpecEntry::UdtStructV0(s) if is_tuple_strukt(s) => Entry::TupleStruct { - doc: s.doc.to_utf8_string_lossy(), - name: s.name.to_utf8_string_lossy(), - fields: s.fields.iter().map(|f| &f.type_).map(Into::into).collect(), - }, - ScSpecEntry::UdtStructV0(s) => Entry::Struct { - doc: s.doc.to_utf8_string_lossy(), - name: s.name.to_utf8_string_lossy(), - fields: s.fields.iter().map(Into::into).collect(), - }, - ScSpecEntry::UdtUnionV0(u) => Entry::Union { - doc: u.doc.to_utf8_string_lossy(), - name: u.name.to_utf8_string_lossy(), - cases: u.cases.iter().map(Into::into).collect(), - }, - ScSpecEntry::UdtEnumV0(e) => Entry::Enum { - doc: e.doc.to_utf8_string_lossy(), - name: e.name.to_utf8_string_lossy(), - cases: e.cases.iter().map(Into::into).collect(), - }, - ScSpecEntry::UdtErrorEnumV0(e) => Entry::ErrorEnum { - doc: e.doc.to_utf8_string_lossy(), - name: e.name.to_utf8_string_lossy(), - cases: e.cases.iter().map(Into::into).collect(), - }, - } - } -} - -fn is_tuple_strukt(s: &ScSpecUdtStructV0) -> bool { - !s.fields.is_empty() && s.fields[0].name.to_utf8_string_lossy() == "0" -} diff --git a/cmd/crates/soroban-spec-typescript/src/wrapper.rs b/cmd/crates/soroban-spec-typescript/src/wrapper.rs deleted file mode 100644 index d2a8d60a..00000000 --- a/cmd/crates/soroban-spec-typescript/src/wrapper.rs +++ /dev/null @@ -1,55 +0,0 @@ -use itertools::Itertools; - -use crate::types; - -pub fn type_to_js_xdr(value: &types::Type) -> String { - match value { - types::Type::Val => todo!(), - types::Type::U64 => "xdr.ScVal.scvU64(xdr.Uint64.fromString(i.toString()))".to_string(), - types::Type::I64 => "xdr.ScVal.scvI64(xdr.Int64.fromString(i.toString()))".to_string(), - types::Type::U32 => "xdr.ScVal.scvU32(i)".to_string(), - types::Type::I32 => "xdr.ScVal.scvI32(i)".to_string(), - types::Type::Bool => "xdr.ScVal.scvBool(i)".to_string(), - types::Type::Symbol => "xdr.ScVal.scvSymbol(i)".to_string(), - types::Type::Map { key, value } => format!( - "xdr.ScVal.scvMap(Array.from(i.entries()).map(([key, value]) => {{ - return new xdr.ScMapEntry({{ - key: ((i)=>{})(key), - val: ((i)=>{})(value)}}) - }}))", - type_to_js_xdr(key), - type_to_js_xdr(value) - ), - types::Type::Option { value } => format!( - "(!i) ? {} : {}", - type_to_js_xdr(&types::Type::Void), - type_to_js_xdr(value) - ), - types::Type::Result { value, .. } => type_to_js_xdr(value), - types::Type::Vec { element } => { - format!("xdr.ScVal.scvVec(i.map((i)=>{}))", type_to_js_xdr(element)) - } - types::Type::Tuple { elements } => { - let cases = elements - .iter() - .enumerate() - .map(|(i, e)| format!("((i) => {})(i[{i}])", type_to_js_xdr(e))) - .join(",\n "); - format!("xdr.ScVal.scvVec([{cases}])") - } - - types::Type::Custom { name } => format!("{name}ToXdr(i)"), - types::Type::BytesN { .. } | types::Type::Bytes => "xdr.ScVal.scvBytes(i)".to_owned(), - types::Type::Address => "addressToScVal(i)".to_owned(), - types::Type::Void => "xdr.ScVal.scvVoid()".to_owned(), - types::Type::U128 => "u128ToScVal(i)".to_owned(), - types::Type::I128 => "i128ToScVal(i)".to_owned(), - - types::Type::U256 | types::Type::I256 | types::Type::Timepoint | types::Type::Duration => { - "i".to_owned() - } - // This is case shouldn't happen since we only go xdr -> js for errors - types::Type::Error { .. } => "N/A".to_owned(), - types::Type::String => "xdr.ScVal.scvString(i)".to_owned(), - } -} diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.env b/cmd/crates/soroban-spec-typescript/ts-tests/.env deleted file mode 100644 index 39b24548..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.env +++ /dev/null @@ -1,2 +0,0 @@ -SOROBAN_NETWORK_PASSPHRASE="Standalone Network ; February 2017" -SOROBAN_RPC_URL="http://localhost:8000/soroban/rpc" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.eslintrc.cjs b/cmd/crates/soroban-spec-typescript/ts-tests/.eslintrc.cjs deleted file mode 100644 index 90af706b..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.eslintrc.cjs +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-env node */ -module.exports = { - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], - parser: '@typescript-eslint/parser', - parserOptions: { project: './tsconfig.json' }, - plugins: ['@typescript-eslint'], - root: true, - rules: { - "@typescript-eslint/no-floating-promises": ["error"] - }, -}; diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore b/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore deleted file mode 100644 index 878823de..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -build -node_modules -yarn.lock -contract-*.txt diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh deleted file mode 100755 index 621be2bc..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# read .env file, but prefer explicitly set environment variables -IFS=$'\n' -for l in $(cat .env); do - IFS='=' read -ra VARVAL <<< "$l" - # If variable with such name already exists, preserves its value - eval "export ${VARVAL[0]}=\${${VARVAL[0]}:-${VARVAL[1]}}" -done -unset IFS - -echo Network -echo " RPC: $SOROBAN_RPC_URL" -echo " Passphrase: \"$SOROBAN_NETWORK_PASSPHRASE\"" - -NETWORK_STATUS=$(curl -s -X POST "http://localhost:8000/soroban/rpc" -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 8675309, "method": "getHealth" }' | sed 's/.*"status":"\(.*\)".*/\1/') || { echo "Make sure you're running local RPC network on localhost:8000" && exit 1; } -echo " Status: $NETWORK_STATUS" - -if [[ "$NETWORK_STATUS" != "healthy" ]]; then - echo "Network is not healthy (not running?), exiting" - exit 1 -fi - -# Print command before executing, from https://stackoverflow.com/a/23342259/249801 -# Discussion: https://github.com/stellar/soroban-tools/pull/1034#pullrequestreview-1690667116 -exe() { echo"${@/eval/}" ; "$@" ; } - -function fund_all() { - exe eval "./soroban keys generate root" - exe eval "./soroban keys fund root" - exe eval "./soroban keys generate alice" - exe eval "./soroban keys fund alice" - exe eval "./soroban keys generate bob" - exe eval "./soroban keys fund bob" -} -function upload() { - exe eval "(./soroban contract $1 --source root --wasm $2 --ignore-checks) > $3" -} -function deploy() { - exe eval "(./soroban contract deploy --source root --wasm-hash $(cat $1) --ignore-checks) > $2" -} -function deploy_all() { - upload deploy ../../../../target/wasm32-unknown-unknown/test-wasms/test_custom_types.wasm contract-id-custom-types.txt - upload deploy ../../../../target/wasm32-unknown-unknown/test-wasms/test_hello_world.wasm contract-id-hello-world.txt - upload deploy ../../../../target/wasm32-unknown-unknown/test-wasms/test_swap.wasm contract-id-swap.txt - upload install ../../../../target/wasm32-unknown-unknown/test-wasms/test_token.wasm contract-token-hash.txt - deploy contract-token-hash.txt contract-id-token-a.txt - deploy contract-token-hash.txt contract-id-token-b.txt -} -function initialize() { - exe eval "./soroban contract invoke --source root --id $(cat $1) -- initialize --admin $(./soroban keys address root) --decimal 0 --name 'Token $2' --symbol '$2'" -} -function initialize_all() { - initialize contract-id-token-a.txt A - initialize contract-id-token-b.txt B -} -function bind() { - exe eval "./soroban contract bindings typescript --contract-id $(cat $1) --output-dir ./node_modules/$2 --overwrite" -} -function bind_all() { - bind contract-id-custom-types.txt test-custom-types - bind contract-id-hello-world.txt test-hello-world - bind contract-id-swap.txt test-swap - bind contract-id-token-a.txt token -} - -function mint() { - exe eval "./soroban contract invoke --source root --id $(cat $1) -- mint --amount 2000000 --to $(./soroban keys address $2)" -} -function mint_all() { - mint contract-id-token-a.txt alice - mint contract-id-token-b.txt bob -} - -fund_all -deploy_all -initialize_all -mint_all -bind_all diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json deleted file mode 100644 index 36f5cdd6..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json +++ /dev/null @@ -1,3405 +0,0 @@ -{ - "name": "ts-tests", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "hasInstallScript": true, - "devDependencies": { - "@ava/typescript": "^4.1.0", - "@stellar/stellar-sdk": "11.2.0", - "@types/node": "^20.4.9", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "ava": "^5.3.1", - "dotenv": "^16.3.1", - "eslint": "^8.53.0", - "typescript": "^5.3.3" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ava/typescript": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-4.1.0.tgz", - "integrity": "sha512-1iWZQ/nr9iflhLK9VN8H+1oDZqe93qxNnyYUz+jTzkYPAHc5fdZXBrqmNIgIfFhWYXK5OaQ5YtC7OmLeTNhVEg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0", - "execa": "^7.1.1" - }, - "engines": { - "node": "^14.19 || ^16.15 || ^18 || ^20" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@stellar/js-xdr": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.0.1.tgz", - "integrity": "sha512-dp5Eh7Nr1YjiIeqpdkj2cQYxfoPudDAH3ck8MWggp48Htw66Z/hUssNYUQG/OftLjEmHT90Z/dtey2Y77DOxIw==", - "dev": true - }, - "node_modules/@stellar/stellar-base": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-10.0.1.tgz", - "integrity": "sha512-BDbx7VHOEQh+4J3Q+gStNXgPaNckVFmD4aOlBBGwxlF6vPFmVnW8IoJdkX7T58zpX55eWI6DXvEhDBlrqTlhAQ==", - "dev": true, - "dependencies": { - "@stellar/js-xdr": "^3.0.1", - "base32.js": "^0.1.0", - "bignumber.js": "^9.1.2", - "buffer": "^6.0.3", - "sha.js": "^2.3.6", - "tweetnacl": "^1.0.3" - }, - "optionalDependencies": { - "sodium-native": "^4.0.1" - } - }, - "node_modules/@stellar/stellar-sdk": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-11.2.0.tgz", - "integrity": "sha512-qInRR+mLLl9O/AI6Q+Sr19RZeYJtlNoJQJi3pch5BYoMvVhjO8IU8AhHADP//Zmc2osyogwPuqXBiFdaGlfHWA==", - "dev": true, - "dependencies": { - "@stellar/stellar-base": "10.0.1", - "axios": "^1.6.5", - "bignumber.js": "^9.1.2", - "eventsource": "^2.0.2", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.14.0.tgz", - "integrity": "sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.14.0", - "@typescript-eslint/type-utils": "6.14.0", - "@typescript-eslint/utils": "6.14.0", - "@typescript-eslint/visitor-keys": "6.14.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.14.0.tgz", - "integrity": "sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.14.0", - "@typescript-eslint/types": "6.14.0", - "@typescript-eslint/typescript-estree": "6.14.0", - "@typescript-eslint/visitor-keys": "6.14.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.14.0.tgz", - "integrity": "sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.14.0", - "@typescript-eslint/visitor-keys": "6.14.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.14.0.tgz", - "integrity": "sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.14.0", - "@typescript-eslint/utils": "6.14.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.14.0.tgz", - "integrity": "sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.14.0.tgz", - "integrity": "sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.14.0", - "@typescript-eslint/visitor-keys": "6.14.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.14.0.tgz", - "integrity": "sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.14.0", - "@typescript-eslint/types": "6.14.0", - "@typescript-eslint/typescript-estree": "6.14.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.14.0.tgz", - "integrity": "sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.14.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", - "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "dev": true, - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/arrgv": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", - "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/arrify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", - "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/ava": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ava/-/ava-5.3.1.tgz", - "integrity": "sha512-Scv9a4gMOXB6+ni4toLuhAm9KYWEjsgBglJl+kMGI5+IVDt120CCDZyB5HNU9DjmLI2t4I0GbnxGLmmRfGTJGg==", - "dev": true, - "dependencies": { - "acorn": "^8.8.2", - "acorn-walk": "^8.2.0", - "ansi-styles": "^6.2.1", - "arrgv": "^1.0.2", - "arrify": "^3.0.0", - "callsites": "^4.0.0", - "cbor": "^8.1.0", - "chalk": "^5.2.0", - "chokidar": "^3.5.3", - "chunkd": "^2.0.1", - "ci-info": "^3.8.0", - "ci-parallel-vars": "^1.0.1", - "clean-yaml-object": "^0.1.0", - "cli-truncate": "^3.1.0", - "code-excerpt": "^4.0.0", - "common-path-prefix": "^3.0.0", - "concordance": "^5.0.4", - "currently-unhandled": "^0.4.1", - "debug": "^4.3.4", - "emittery": "^1.0.1", - "figures": "^5.0.0", - "globby": "^13.1.4", - "ignore-by-default": "^2.1.0", - "indent-string": "^5.0.0", - "is-error": "^2.2.2", - "is-plain-object": "^5.0.0", - "is-promise": "^4.0.0", - "matcher": "^5.0.0", - "mem": "^9.0.2", - "ms": "^2.1.3", - "p-event": "^5.0.1", - "p-map": "^5.5.0", - "picomatch": "^2.3.1", - "pkg-conf": "^4.0.0", - "plur": "^5.1.0", - "pretty-ms": "^8.0.0", - "resolve-cwd": "^3.0.0", - "stack-utils": "^2.0.6", - "strip-ansi": "^7.0.1", - "supertap": "^3.0.1", - "temp-dir": "^3.0.0", - "write-file-atomic": "^5.0.1", - "yargs": "^17.7.2" - }, - "bin": { - "ava": "entrypoints/cli.mjs" - }, - "engines": { - "node": ">=14.19 <15 || >=16.15 <17 || >=18" - }, - "peerDependencies": { - "@ava/typescript": "*" - }, - "peerDependenciesMeta": { - "@ava/typescript": { - "optional": true - } - } - }, - "node_modules/ava/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ava/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/blueimp-md5": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", - "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/callsites": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.1.0.tgz", - "integrity": "sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "dev": true, - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chunkd": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", - "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", - "dev": true - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/ci-parallel-vars": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", - "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", - "dev": true - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-yaml-object": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", - "integrity": "sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/code-excerpt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", - "dev": true, - "dependencies": { - "convert-to-spaces": "^2.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concordance": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", - "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", - "dev": true, - "dependencies": { - "date-time": "^3.1.0", - "esutils": "^2.0.3", - "fast-diff": "^1.2.0", - "js-string-escape": "^1.0.1", - "lodash": "^4.17.15", - "md5-hex": "^3.0.1", - "semver": "^7.3.2", - "well-known-symbols": "^2.0.0" - }, - "engines": { - "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" - } - }, - "node_modules/convert-to-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "dev": true, - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/date-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", - "dev": true, - "dependencies": { - "time-zone": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emittery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.1.tgz", - "integrity": "sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", - "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", - "dev": true, - "engines": { - "node": ">=10 <11 || >=12 <13 || >=14" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/irregular-plurals": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", - "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-error": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", - "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", - "dev": true - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-string-escape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", - "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/load-json-file": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", - "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/matcher": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", - "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/md5-hex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", - "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", - "dev": true, - "dependencies": { - "blueimp-md5": "^2.10.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mem": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mem/-/mem-9.0.2.tgz", - "integrity": "sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sindresorhus/mem?sponsor=1" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-gyp-build": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", - "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-event": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", - "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", - "dev": true, - "dependencies": { - "p-timeout": "^5.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "dev": true, - "dependencies": { - "aggregate-error": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module/node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-ms": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", - "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-conf": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", - "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", - "dev": true, - "dependencies": { - "find-up": "^6.0.0", - "load-json-file": "^7.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/pkg-conf/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/plur": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", - "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", - "dev": true, - "dependencies": { - "irregular-plurals": "^3.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-ms": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", - "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", - "dev": true, - "dependencies": { - "parse-ms": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/sodium-native": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.0.4.tgz", - "integrity": "sha512-faqOKw4WQKK7r/ybn6Lqo1F9+L5T6NlBJJYvpxbZPetpWylUVqz449mvlwIBKBqxEHbWakWuOlUt8J3Qpc4sWw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "node-gyp-build": "^4.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supertap": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", - "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", - "dev": true, - "dependencies": { - "indent-string": "^5.0.0", - "js-yaml": "^3.14.1", - "serialize-error": "^7.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/supertap/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/supertap/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "dev": true - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urijs": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", - "dev": true - }, - "node_modules/well-known-symbols": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", - "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package.json b/cmd/crates/soroban-spec-typescript/ts-tests/package.json deleted file mode 100644 index c06446de..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "private": true, - "type": "module", - "scripts": { - "lint": "eslint src/*", - "postinstall": "./initialize.sh", - "test": "npm run lint && ava" - }, - "devDependencies": { - "@ava/typescript": "^4.1.0", - "@types/node": "^20.4.9", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "ava": "^5.3.1", - "dotenv": "^16.3.1", - "eslint": "^8.53.0", - "@stellar/stellar-sdk": "11.2.0", - "typescript": "^5.3.3" - }, - "ava": { - "typescript": { - "rewritePaths": { - "src/": "build/" - }, - "compile": "tsc" - }, - "require": [ - "dotenv/config" - ] - } -} diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/soroban b/cmd/crates/soroban-spec-typescript/ts-tests/soroban deleted file mode 100755 index d98f247c..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/soroban +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cargo run --quiet -p soroban-cli -- "$@" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts deleted file mode 100644 index 3b07dc3d..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts +++ /dev/null @@ -1,187 +0,0 @@ -import test from 'ava' -import { root, rpcUrl, wallet } from './util.js' -import { Contract, Ok, Err, networks } from 'test-custom-types' - -const publicKey = root.keypair.publicKey(); - -const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }); - -test('hello', async t => { - const { result } = await contract.hello({ hello: 'tests' }) - t.is(result, 'tests') -}) - -test('woid', async t => { - t.is((await contract.woid()).result, undefined) -}) - -test('u32_fail_on_even', async t => { - t.deepEqual( - (await contract.u32FailOnEven({ u32_: 1 })).result, - new Ok(1) - ) - t.deepEqual( - (await contract.u32FailOnEven({ u32_: 0 })).result, - new Err({ message: "Please provide an odd number" }) - ) -}) - -test('u32', async t => { - t.is((await contract.u32({ u32_: 1 })).result, 1) -}) - -test('i32', async t => { - t.is((await contract.i32({ i32_: 1 })).result, 1) -}) - -test('i64', async t => { - t.is((await contract.i64({ i64_: 1n })).result, 1n) -}) - -test("strukt_hel", async (t) => { - const test = { a: 0, b: true, c: "world" } - t.deepEqual((await contract.struktHel({ strukt: test })).result, ["Hello", "world"]) -}) - -test("strukt", async (t) => { - const test = { a: 0, b: true, c: "hello" } - t.deepEqual((await contract.strukt({ strukt: test })).result, test) -}) - -test('simple first', async t => { - const arg = { tag: 'First', values: undefined } as const - const ret = { tag: 'First' } - t.deepEqual((await contract.simple({ simple: arg })).result, ret) -}) - -test('simple second', async t => { - const arg = { tag: 'Second', values: undefined } as const - const ret = { tag: 'Second' } - t.deepEqual((await contract.simple({ simple: arg })).result, ret) -}) - -test('simple third', async t => { - const arg = { tag: 'Third', values: undefined } as const - const ret = { tag: 'Third' } - t.deepEqual((await contract.simple({ simple: arg })).result, ret) -}) - -test('complex with struct', async t => { - const arg = { tag: 'Struct', values: [{ a: 0, b: true, c: 'hello' }] } as const - const ret = { tag: 'Struct', values: [{ a: 0, b: true, c: 'hello' }] } - t.deepEqual((await contract.complex({ complex: arg })).result, ret) -}) - -test('complex with tuple', async t => { - const arg = { tag: 'Tuple', values: [[{ a: 0, b: true, c: 'hello' }, { tag: 'First', values: undefined }]] } as const - const ret = { tag: 'Tuple', values: [[{ a: 0, b: true, c: 'hello' }, { tag: 'First' }]] } - t.deepEqual((await contract.complex({ complex: arg })).result, ret) -}) - -test('complex with enum', async t => { - const arg = { tag: 'Enum', values: [{ tag: 'First', values: undefined }] } as const - const ret = { tag: 'Enum', values: [{ tag: 'First' }] } - t.deepEqual((await contract.complex({ complex: arg })).result, ret) -}) - -test('complex with asset', async t => { - const arg = { tag: 'Asset', values: [publicKey, 1n] } as const - const ret = { tag: 'Asset', values: [publicKey, 1n] } - t.deepEqual((await contract.complex({ complex: arg })).result, ret) -}) - -test('complex with void', async t => { - const arg = { tag: 'Void', values: undefined } as const - const ret = { tag: 'Void' } - t.deepEqual((await contract.complex({ complex: arg })).result, ret) -}) - -test('addresse', async t => { - t.deepEqual((await contract.addresse({ addresse: publicKey })).result, publicKey) -}) - -test('bytes', async t => { - const bytes = Buffer.from('hello') - t.deepEqual((await contract.bytes({ bytes })).result, bytes) -}) - -test('bytes_n', async t => { - const bytes_n = Buffer.from('123456789') // what's the correct way to construct bytes_n? - t.deepEqual((await contract.bytesN({ bytes_n })).result, bytes_n) -}) - -test('card', async t => { - const card = 11 - t.is((await contract.card({ card })).result, card) -}) - -test('boolean', async t => { - t.is((await contract.boolean({ boolean: true })).result, true) -}) - -test('not', async t => { - t.is((await contract.not({ boolean: true })).result, false) -}) - -test('i128', async t => { - t.is((await contract.i128({ i128: -1n })).result, -1n) -}) - -test('u128', async t => { - t.is((await contract.u128({ u128: 1n })).result, 1n) -}) - -test('multi_args', async t => { - t.is((await contract.multiArgs({ a: 1, b: true })).result, 1) - t.is((await contract.multiArgs({ a: 1, b: false })).result, 0) -}) - -test('map', async t => { - const map = new Map() - map.set(1, true) - map.set(2, false) - // map.set(3, 'hahaha') // should throw an error - const ret = Array.from(map.entries()) - t.deepEqual((await contract.map({ map })).result, ret) -}) - -test('vec', async t => { - const vec = [1, 2, 3] - t.deepEqual((await contract.vec({ vec })).result, vec) -}) - -test('tuple', async t => { - const tuple = ['hello', 1] as const - t.deepEqual((await contract.tuple({ tuple })).result, tuple) -}) - -test('option', async t => { - // this makes sense - t.deepEqual((await contract.option({ option: 1 })).result, 1) - - // this passes but shouldn't - t.deepEqual((await contract.option({ option: undefined })).result, undefined) - - // this is the behavior we probably want, but fails - // t.deepEqual(await contract.option(), undefined) // typing and implementation require the object - // t.deepEqual((await contract.option({})).result, undefined) // typing requires argument; implementation would be fine with this - // t.deepEqual((await contract.option({ option: undefined })).result, undefined) -}) - -test('u256', async t => { - t.is((await contract.u256({ u256: 1n })).result, 1n) -}) - -test('i256', async t => { - t.is((await contract.i256({ i256: -1n })).result, -1n) -}) - -test('string', async t => { - t.is((await contract.string({ string: 'hello' })).result, 'hello') -}) - -test('tuple_strukt', async t => { - const arg = [{ a: 0, b: true, c: 'hello' }, { tag: 'First', values: undefined }] as const - const res = [{ a: 0, b: true, c: 'hello' }, { tag: 'First' }] - t.deepEqual((await contract.tupleStrukt({ tuple_strukt: arg })).result, res) -}) diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-transaction.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-transaction.ts deleted file mode 100644 index f6152d2c..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-transaction.ts +++ /dev/null @@ -1,16 +0,0 @@ -import test from "ava" -import { wallet, rpcUrl } from "./util.js" -import { Contract, networks } from "test-hello-world" - -const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }) - -test("has correctly-typed result", async (t) => { - const initial = await contract.hello({ world: "tests" }) - t.is(initial.result[0], "Hello") - t.is(initial.result[1], "tests") - - const serialized = initial.toJSON() - const deserialized = contract.fromJSON.hello(serialized) - t.is(deserialized.result[0], "Hello") // throws TS error if `result` is of type `unknown` - t.is(deserialized.result[1], "tests") // throws TS error if `result` is of type `unknown` -}); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-hello-world.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-hello-world.ts deleted file mode 100644 index e658c455..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-hello-world.ts +++ /dev/null @@ -1,26 +0,0 @@ -import test from "ava"; -import { root, wallet, rpcUrl } from "./util.js"; -import { Contract, networks } from "test-hello-world"; - -const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }); - -test("hello", async (t) => { - t.deepEqual((await contract.hello({ world: "tests" })).result, ["Hello", "tests"]); -}); - -test("auth", async (t) => { - t.deepEqual( - (await contract.auth({ - addr: root.keypair.publicKey(), - world: 'lol' - })).result, - root.keypair.publicKey() - ) -}); - -test("inc", async (t) => { - const { result: startingBalance } = await contract.getCount() - const inc = await contract.inc() - t.is((await inc.signAndSend()).result, startingBalance + 1) - t.is((await contract.getCount()).result, startingBalance + 1) -}); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-methods-as-args.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-methods-as-args.ts deleted file mode 100644 index afa3b651..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-methods-as-args.ts +++ /dev/null @@ -1,12 +0,0 @@ -import test from "ava"; -import { wallet, rpcUrl } from "./util.js"; -import { Contract, networks } from "test-hello-world"; - -const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }); - -// this test checks that apps can pass methods as arguments to other methods and have them still work -const hello = contract.hello - -test("hello", async (t) => { - t.deepEqual((await hello({ world: "tests" })).result, ["Hello", "tests"]); -}); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-swap.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-swap.ts deleted file mode 100644 index a473cb4d..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-swap.ts +++ /dev/null @@ -1,134 +0,0 @@ -import test from "ava" -import { SorobanRpc, xdr } from '@stellar/stellar-sdk' -import { wallet, rpcUrl, alice, bob, networkPassphrase, root, Wallet } from "./util.js" -import { Contract as Token } from "token" -import { Contract as Swap, networks, NeedsMoreSignaturesError } from "test-swap" -import fs from "node:fs" - -const tokenAId = fs.readFileSync(new URL("../contract-id-token-a.txt", import.meta.url), "utf8").trim() -const tokenBId = fs.readFileSync(new URL("../contract-id-token-b.txt", import.meta.url), "utf8").trim() - -// `root` is the invoker of all contracts -const tokenA = new Token({ - contractId: tokenAId, - networkPassphrase, - rpcUrl, - wallet, -}) -const tokenB = new Token({ - contractId: tokenBId, - networkPassphrase, - rpcUrl, - wallet, -}) -function swapContractAs(invoker: typeof root | typeof alice | typeof bob) { - return new Swap({ - ...networks.standalone, - rpcUrl, - wallet: new Wallet(invoker.keypair.publicKey()), - }) -} - -const amountAToSwap = 2n -const amountBToSwap = 1n -const alicePk = alice.keypair.publicKey() -const bobPk = bob.keypair.publicKey() - -test('calling `signAndSend()` too soon throws descriptive error', async t => { - const swapContract = swapContractAs(root) - const tx = await swapContract.swap({ - a: alicePk, - b: bobPk, - token_a: tokenAId, - token_b: tokenBId, - amount_a: amountAToSwap, - min_a_for_b: amountAToSwap, - amount_b: amountBToSwap, - min_b_for_a: amountBToSwap, - }) - const error = await t.throwsAsync(tx.signAndSend()) - t.true(error instanceof NeedsMoreSignaturesError, `error is not of type 'NeedsMoreSignaturesError'; instead it is of type '${error?.constructor.name}'`) - if (error) t.regex(error.message, /needsNonInvokerSigningBy/) -}) - -test('alice swaps bob 10 A for 1 B', async t => { - const swapContractAsRoot = swapContractAs(root) - const [ - { result: aliceStartingABalance }, - { result: aliceStartingBBalance }, - { result: bobStartingABalance }, - { result: bobStartingBBalance }, - ] = await Promise.all([ - tokenA.balance({ id: alicePk }), - tokenB.balance({ id: alicePk }), - tokenA.balance({ id: bobPk }), - tokenB.balance({ id: bobPk }), - ]) - t.true(aliceStartingABalance >= amountAToSwap, `alice does not have enough Token A! aliceStartingABalance: ${aliceStartingABalance}`) - t.true(bobStartingBBalance >= amountBToSwap, `bob does not have enough Token B! bobStartingBBalance: ${bobStartingBBalance}`) - - const tx = await swapContractAsRoot.swap({ - a: alicePk, - b: bobPk, - token_a: tokenAId, - token_b: tokenBId, - amount_a: amountAToSwap, - min_a_for_b: amountAToSwap, - amount_b: amountBToSwap, - min_b_for_a: amountBToSwap, - }) - - const needsNonInvokerSigningBy = await tx.needsNonInvokerSigningBy() - t.is(needsNonInvokerSigningBy.length, 2) - t.is(needsNonInvokerSigningBy.indexOf(alicePk), 0, 'needsNonInvokerSigningBy does not have alice\'s public key!') - t.is(needsNonInvokerSigningBy.indexOf(bobPk), 1, 'needsNonInvokerSigningBy does not have bob\'s public key!') - - - // root serializes & sends to alice - const jsonFromRoot = tx.toJSON() - const txAlice = swapContractAs(alice).fromJSON.swap(jsonFromRoot) - await txAlice.signAuthEntries() - - // alice serializes & sends to bob - const jsonFromAlice = txAlice.toJSON() - const txBob = swapContractAs(bob).fromJSON.swap(jsonFromAlice) - await txBob.signAuthEntries() - - // bob serializes & sends back to root - const jsonFromBob = txBob.toJSON() - const txRoot = swapContractAsRoot.fromJSON.swap(jsonFromBob) - const result = await txRoot.signAndSend() - - t.truthy(result.sendTransactionResponse, `tx failed: ${JSON.stringify(result, null, 2)}`) - t.is(result.sendTransactionResponse!.status, 'PENDING', `tx failed: ${JSON.stringify(result, null, 2)}`) - t.truthy(result.getTransactionResponseAll?.length, `tx failed: ${JSON.stringify(result.getTransactionResponseAll, null, 2)}`) - t.not(result.getTransactionResponse!.status, 'FAILED', `tx failed: ${JSON.stringify( - ((result.getTransactionResponse as SorobanRpc.Api.GetFailedTransactionResponse) - .resultXdr.result().value() as xdr.OperationResult[] - ).map(op => - op.value()?.value().switch() - ), null, 2)}` - ) - t.is( - result.getTransactionResponse!.status, - SorobanRpc.Api.GetTransactionStatus.SUCCESS, - `tx failed: ${JSON.stringify(result.getTransactionResponse, null, 2)}` - ) - - t.is( - (await tokenA.balance({ id: alicePk })).result, - aliceStartingABalance - amountAToSwap - ) - t.is( - (await tokenB.balance({ id: alicePk })).result, - aliceStartingBBalance + amountBToSwap - ) - t.is( - (await tokenA.balance({ id: bobPk })).result, - bobStartingABalance + amountAToSwap - ) - t.is( - (await tokenB.balance({ id: bobPk })).result, - bobStartingBBalance - amountBToSwap - ) -}) diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts deleted file mode 100644 index d5539fd1..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { spawnSync } from "node:child_process"; -import { Keypair, TransactionBuilder, hash } from "@stellar/stellar-sdk"; -import { Address } from 'test-custom-types' - -const rootKeypair = Keypair.fromSecret(spawnSync("./soroban", ["keys", "show", "root"], { shell: true, encoding: "utf8" }).stdout.trim()); -const aliceKeypair = Keypair.fromSecret(spawnSync("./soroban", ["keys", "show", "alice"], { shell: true, encoding: "utf8" }).stdout.trim()); -const bobKeypair = Keypair.fromSecret(spawnSync("./soroban", ["keys", "show", "bob"], { shell: true, encoding: "utf8" }).stdout.trim()); - -export const root = { - keypair: rootKeypair, - address: Address.fromString(rootKeypair.publicKey()), -} - -export const alice = { - keypair: aliceKeypair, - address: Address.fromString(aliceKeypair.publicKey()), -} - -export const bob = { - keypair: bobKeypair, - address: Address.fromString(bobKeypair.publicKey()), -} - -function getKeypair(pk: string): Keypair { - return Keypair.fromSecret({ - [root.keypair.publicKey()]: root.keypair.secret(), - [alice.keypair.publicKey()]: alice.keypair.secret(), - [bob.keypair.publicKey()]: bob.keypair.secret(), - }[pk]) -} - -export const rpcUrl = process.env.SOROBAN_RPC_URL ?? "http://localhost:8000/"; -export const networkPassphrase = process.env.SOROBAN_NETWORK_PASSPHRASE ?? "Standalone Network ; February 2017"; - -export class Wallet { - constructor(private publicKey: string) {} - isConnected = () => Promise.resolve(true) - isAllowed = () => Promise.resolve(true) - getUserInfo = () => Promise.resolve({ publicKey: this.publicKey }) - signTransaction = async (tx: string) => { - const t = TransactionBuilder.fromXDR(tx, networkPassphrase); - t.sign(getKeypair(this.publicKey)); - return t.toXDR(); - } - signAuthEntry = async ( - entryXdr: string, - opts?: { - accountToSign?: string, - } - ): Promise => { - return getKeypair(opts?.accountToSign ?? this.publicKey) - .sign(hash(Buffer.from(entryXdr, "base64"))) - .toString('base64') - } -} - -export const wallet = new Wallet(root.keypair.publicKey()) diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/tsconfig.json b/cmd/crates/soroban-spec-typescript/ts-tests/tsconfig.json deleted file mode 100644 index 119437bc..00000000 --- a/cmd/crates/soroban-spec-typescript/ts-tests/tsconfig.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "nodenext", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./build", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml deleted file mode 100644 index ad60c827..00000000 --- a/cmd/soroban-cli/Cargo.toml +++ /dev/null @@ -1,106 +0,0 @@ -[package] -name = "soroban-cli" -description = "Soroban CLI" -homepage = "https://github.com/stellar/soroban-cli" -repository = "https://github.com/stellar/soroban-cli" -authors = ["Stellar Development Foundation "] -license = "Apache-2.0" -readme = "README.md" -version = "20.2.0" -edition = "2021" -rust-version.workspace = true -autobins = false -default-run = "soroban" - -[[bin]] -name = "soroban" -path = "src/bin/main.rs" - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }{ archive-suffix }" -bin-dir = "{ bin }{ binary-ext }" - -[[bin]] -name = "doc-gen" -path = "src/bin/doc-gen.rs" -required-features = ["clap-markdown"] - -[lib] -name = "soroban_cli" -path = "src/lib.rs" -doctest = false - -[features] -default = [] -opt = ["dep:wasm-opt"] - -[dependencies] -stellar-xdr = { workspace = true, features = ["cli"] } -soroban-env-host = { workspace = true } -soroban-spec = { workspace = true } -soroban-spec-json = { workspace = true } -soroban-spec-rust = { workspace = true } -soroban-spec-tools = { workspace = true } -soroban-spec-typescript = { workspace = true } -soroban-ledger-snapshot = { workspace = true } -stellar-strkey = { workspace = true } -soroban-sdk = { workspace = true } -clap = { version = "4.1.8", features = [ - "derive", - "env", - "deprecated", - "string", -] } -base64 = { workspace = true } -thiserror = { workspace = true } -serde = "1.0.82" -serde_derive = "1.0.82" -serde_json = "1.0.82" -serde-aux = "4.1.2" -hex = { workspace = true } -num-bigint = "0.4" -tokio = { version = "1", features = ["full"] } -termcolor = "1.1.3" -termcolor_output = "1.0.1" -clap_complete = "4.1.4" -rand = "0.8.5" -wasmparser = { workspace = true } -sha2 = { workspace = true } -csv = "1.1.6" -ed25519-dalek = "=2.0.0" -jsonrpsee-http-client = "0.20.1" -jsonrpsee-core = "0.20.1" -hyper = "0.14.27" -hyper-tls = "0.5" -http = "0.2.9" -regex = "1.6.0" -wasm-opt = { version = "0.114.0", optional = true } -chrono = "0.4.27" -rpassword = "7.2.0" -dirs = "4.0.0" -toml = "0.5.9" -itertools = "0.10.5" -shlex = "1.1.0" -sep5 = { workspace = true } -ethnum = { workspace = true } -clap-markdown = { version = "0.1.3", optional = true } -which = { workspace = true, features = ["regex"] } -strsim = "0.10.0" -heck = "0.4.1" -tracing = { workspace = true } -tracing-appender = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter"] } -cargo_metadata = "0.15.4" -pathdiff = "0.2.1" -dotenvy = "0.15.7" -# For hyper-tls -[target.'cfg(unix)'.dependencies] -openssl = { version = "0.10.55", features = ["vendored"] } - -[build-dependencies] -crate-git-revision = "0.0.4" - -[dev-dependencies] -assert_cmd = "2.0.4" -assert_fs = "1.0.7" -predicates = "2.1.5" diff --git a/cmd/soroban-cli/README.md b/cmd/soroban-cli/README.md deleted file mode 100644 index d17261b1..00000000 --- a/cmd/soroban-cli/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# soroban-cli - -CLI for running Soroban contracts locally in a test VM. Executes WASM files built using the [rs-soroban-sdk](https://github.com/stellar/rs-soroban-sdk). - -Soroban: https://soroban.stellar.org - -## Install - -``` -cargo install --locked soroban-cli -``` - -To install with the `opt` feature, which includes a WASM optimization feature and wasm-opt built in: - -``` -cargo install --locked soroban-cli --features opt -``` - -## Usage - -Can invoke a contract method as a subcommand with different arguments. Anything after the slop (`--`) is passed to the contract's CLI. You can use `--help` to learn about which methods are available and what their arguments are including an example of the type of the input. - -## Example - -``` -soroban invoke --id --wasm -- --help -soroban invoke --id --network futurenet -- --help -``` diff --git a/cmd/soroban-cli/build.rs b/cmd/soroban-cli/build.rs deleted file mode 100644 index b6e6dd92..00000000 --- a/cmd/soroban-cli/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - crate_git_revision::init(); -} diff --git a/cmd/soroban-cli/src/bin/doc-gen.rs b/cmd/soroban-cli/src/bin/doc-gen.rs deleted file mode 100644 index 096f9681..00000000 --- a/cmd/soroban-cli/src/bin/doc-gen.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, -}; - -type DynError = Box; - -fn main() -> Result<(), DynError> { - doc_gen()?; - Ok(()) -} - -fn doc_gen() -> std::io::Result<()> { - let out_dir = docs_dir(); - - fs::create_dir_all(out_dir.clone())?; - - std::fs::write( - out_dir.join("soroban-cli-full-docs.md"), - clap_markdown::help_markdown::(), - )?; - - Ok(()) -} - -fn project_root() -> PathBuf { - Path::new(&env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(2) - .unwrap() - .to_path_buf() -} - -fn docs_dir() -> PathBuf { - project_root().join("docs") -} diff --git a/cmd/soroban-cli/src/bin/main.rs b/cmd/soroban-cli/src/bin/main.rs deleted file mode 100644 index 7a87099c..00000000 --- a/cmd/soroban-cli/src/bin/main.rs +++ /dev/null @@ -1,51 +0,0 @@ -use clap::CommandFactory; -use dotenvy::dotenv; -use tracing_subscriber::{fmt, EnvFilter}; - -use soroban_cli::{commands, Root}; - -#[tokio::main] -async fn main() { - let _ = dotenv().unwrap_or_default(); - let mut root = Root::new().unwrap_or_else(|e| match e { - commands::Error::Clap(e) => { - let mut cmd = Root::command(); - e.format(&mut cmd).exit(); - } - e => { - eprintln!("{e}"); - std::process::exit(1); - } - }); - // Now use root to setup the logger - if let Some(level) = root.global_args.log_level() { - let mut e_filter = EnvFilter::from_default_env() - .add_directive("hyper=off".parse().unwrap()) - .add_directive(format!("soroban_cli={level}").parse().unwrap()); - - for filter in &root.global_args.filter_logs { - e_filter = e_filter.add_directive( - filter - .parse() - .map_err(|e| { - eprintln!("{e}: {filter}"); - std::process::exit(1); - }) - .unwrap(), - ); - } - - let builder = fmt::Subscriber::builder() - .with_env_filter(e_filter) - .with_writer(std::io::stderr); - - let subscriber = builder.finish(); - tracing::subscriber::set_global_default(subscriber) - .expect("Failed to set the global tracing subscriber"); - } - - if let Err(e) = root.run().await { - eprintln!("error: {e}"); - std::process::exit(1); - } -} diff --git a/cmd/soroban-cli/src/commands/completion.rs b/cmd/soroban-cli/src/commands/completion.rs deleted file mode 100644 index f64386b4..00000000 --- a/cmd/soroban-cli/src/commands/completion.rs +++ /dev/null @@ -1,32 +0,0 @@ -use clap::{arg, CommandFactory, Parser}; -use clap_complete::{generate, Shell}; -use std::io; - -use crate::commands::Root; - -pub const LONG_ABOUT: &str = "\ -Print shell completion code for the specified shell - -Ensure the completion package for your shell is installed, -e.g., bash-completion for bash. - -To enable autocomplete in the current bash shell, run: - source <(soroban completion --shell bash) - -To enable autocomplete permanently, run: - echo \"source <(soroban completion --shell bash)\" >> ~/.bashrc"; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// The shell type - #[arg(long, value_enum)] - shell: Shell, -} - -impl Cmd { - pub fn run(&self) { - let cmd = &mut Root::command(); - generate(self.shell, cmd, "soroban", &mut io::stdout()); - } -} diff --git a/cmd/soroban-cli/src/commands/config/locator.rs b/cmd/soroban-cli/src/commands/config/locator.rs deleted file mode 100644 index 2688b043..00000000 --- a/cmd/soroban-cli/src/commands/config/locator.rs +++ /dev/null @@ -1,358 +0,0 @@ -use clap::arg; -use serde::de::DeserializeOwned; -use std::{ - ffi::OsStr, - fmt::Display, - fs, io, - path::{Path, PathBuf}, - str::FromStr, -}; - -use crate::{utils::find_config_dir, Pwd}; - -use super::{network::Network, secret::Secret}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Failed to find home directory")] - HomeDirNotFound, - #[error("Failed read current directory")] - CurrentDirNotFound, - #[error("Failed read current directory and no SOROBAN_CONFIG_HOME is set")] - NoConfigEnvVar, - #[error("Failed to create directory: {path:?}")] - DirCreationFailed { path: PathBuf }, - #[error( - "Failed to read secret's file: {path}.\nProbably need to use `soroban config identity add`" - )] - SecretFileRead { path: PathBuf }, - #[error( - "Failed to read network file: {path};\nProbably need to use `soroban config network add`" - )] - NetworkFileRead { path: PathBuf }, - #[error(transparent)] - Toml(#[from] toml::de::Error), - #[error("Seceret file failed to deserialize")] - Deserialization, - #[error("Failed to write identity file:{filepath}: {error}")] - IdCreationFailed { filepath: PathBuf, error: io::Error }, - #[error("Seceret file failed to deserialize")] - NetworkDeserialization, - #[error("Failed to write network file: {0}")] - NetworkCreationFailed(std::io::Error), - #[error("Error Identity directory is invalid: {name}")] - IdentityList { name: String }, - // #[error("Config file failed to deserialize")] - // CannotReadConfigFile, - #[error("Config file failed to serialize")] - ConfigSerialization, - // #[error("Config file failed write")] - // CannotWriteConfigFile, - #[error("XDG_CONFIG_HOME env variable is not a valid path. Got {0}")] - XdgConfigHome(String), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("Failed to remove {0}: {1}")] - ConfigRemoval(String, String), - #[error("Failed to find config {0} for {1}")] - ConfigMissing(String, String), - #[error(transparent)] - String(#[from] std::string::FromUtf8Error), - #[error(transparent)] - Secret(#[from] crate::commands::config::secret::Error), -} - -#[derive(Debug, clap::Args, Default, Clone)] -#[group(skip)] -pub struct Args { - /// Use global config - #[arg(long)] - pub global: bool, - - /// Location of config directory, default is "." - #[arg(long, help_heading = "TESTING_OPTIONS")] - pub config_dir: Option, -} - -pub enum Location { - Local(PathBuf), - Global(PathBuf), -} - -impl Display for Location { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} {:?}", - match self { - Location::Local(_) => "Local", - Location::Global(_) => "Global", - }, - self.as_ref().parent().unwrap().parent().unwrap() - ) - } -} - -impl AsRef for Location { - fn as_ref(&self) -> &Path { - match self { - Location::Local(p) | Location::Global(p) => p.as_path(), - } - } -} - -impl Location { - #[must_use] - pub fn wrap(&self, p: PathBuf) -> Self { - match self { - Location::Local(_) => Location::Local(p), - Location::Global(_) => Location::Global(p), - } - } -} - -impl Args { - pub fn config_dir(&self) -> Result { - if self.global { - global_config_path() - } else { - self.local_config() - } - } - - pub fn local_and_global(&self) -> Result<[Location; 2], Error> { - Ok([ - Location::Local(self.local_config()?), - Location::Global(global_config_path()?), - ]) - } - - pub fn local_config(&self) -> Result { - let pwd = self.current_dir()?; - Ok(find_config_dir(pwd.clone()).unwrap_or_else(|_| pwd.join(".soroban"))) - } - - pub fn current_dir(&self) -> Result { - self.config_dir.as_ref().map_or_else( - || std::env::current_dir().map_err(|_| Error::CurrentDirNotFound), - |pwd| Ok(pwd.clone()), - ) - } - - pub fn write_identity(&self, name: &str, secret: &Secret) -> Result<(), Error> { - KeyType::Identity.write(name, secret, &self.config_dir()?) - } - - pub fn write_network(&self, name: &str, network: &Network) -> Result<(), Error> { - KeyType::Network.write(name, network, &self.config_dir()?) - } - - pub fn list_identities(&self) -> Result, Error> { - Ok(KeyType::Identity - .list_paths(&self.local_and_global()?)? - .into_iter() - .map(|(name, _)| name) - .collect()) - } - - pub fn list_identities_long(&self) -> Result, Error> { - Ok(KeyType::Identity - .list_paths(&self.local_and_global()?) - .into_iter() - .flatten() - .map(|(name, location)| { - let path = match location { - Location::Local(path) | Location::Global(path) => path, - }; - (name, format!("{}", path.display())) - }) - .collect()) - } - - pub fn list_networks(&self) -> Result, Error> { - Ok(KeyType::Network - .list_paths(&self.local_and_global()?) - .into_iter() - .flatten() - .map(|x| x.0) - .collect()) - } - - pub fn list_networks_long(&self) -> Result, Error> { - Ok(KeyType::Network - .list_paths(&self.local_and_global()?) - .into_iter() - .flatten() - .filter_map(|(name, location)| { - Some(( - name, - KeyType::read_from_path::(location.as_ref()).ok()?, - location, - )) - }) - .collect::>()) - } - pub fn read_identity(&self, name: &str) -> Result { - KeyType::Identity.read_with_global(name, &self.local_config()?) - } - - pub fn read_network(&self, name: &str) -> Result { - let res = KeyType::Network.read_with_global(name, &self.local_config()?); - if let Err(Error::ConfigMissing(_, _)) = &res { - if name == "futurenet" { - let network = Network::futurenet(); - self.write_network(name, &network)?; - return Ok(network); - } - } - res - } - - pub fn remove_identity(&self, name: &str) -> Result<(), Error> { - KeyType::Identity.remove(name, &self.config_dir()?) - } - - pub fn remove_network(&self, name: &str) -> Result<(), Error> { - KeyType::Network.remove(name, &self.config_dir()?) - } -} - -fn ensure_directory(dir: PathBuf) -> Result { - let parent = dir.parent().ok_or(Error::HomeDirNotFound)?; - std::fs::create_dir_all(parent).map_err(|_| dir_creation_failed(parent))?; - Ok(dir) -} - -fn dir_creation_failed(p: &Path) -> Error { - Error::DirCreationFailed { - path: p.to_path_buf(), - } -} - -fn read_dir(dir: &Path) -> Result, Error> { - let contents = std::fs::read_dir(dir)?; - let mut res = vec![]; - for entry in contents.filter_map(Result::ok) { - let path = entry.path(); - if let Some("toml") = path.extension().and_then(OsStr::to_str) { - if let Some(os_str) = path.file_stem() { - res.push((os_str.to_string_lossy().trim().to_string(), path)); - } - } - } - res.sort(); - Ok(res) -} - -pub enum KeyType { - Identity, - Network, -} - -impl Display for KeyType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - KeyType::Identity => "identity", - KeyType::Network => "network", - } - ) - } -} - -impl KeyType { - pub fn read(&self, key: &str, pwd: &Path) -> Result { - let path = self.path(pwd, key); - Self::read_from_path(&path) - } - - pub fn read_from_path(path: &Path) -> Result { - let data = fs::read(path).map_err(|_| Error::NetworkFileRead { - path: path.to_path_buf(), - })?; - let res = toml::from_slice(data.as_slice()); - Ok(res?) - } - - pub fn read_with_global(&self, key: &str, pwd: &Path) -> Result { - for path in [pwd, global_config_path()?.as_path()] { - match self.read(key, path) { - Ok(t) => return Ok(t), - _ => continue, - } - } - Err(Error::ConfigMissing(self.to_string(), key.to_string())) - } - - pub fn write( - &self, - key: &str, - value: &T, - pwd: &Path, - ) -> Result<(), Error> { - let filepath = ensure_directory(self.path(pwd, key))?; - let data = toml::to_string(value).map_err(|_| Error::ConfigSerialization)?; - std::fs::write(&filepath, data).map_err(|error| Error::IdCreationFailed { filepath, error }) - } - - fn root(&self, pwd: &Path) -> PathBuf { - pwd.join(self.to_string()) - } - - fn path(&self, pwd: &Path, key: &str) -> PathBuf { - let mut path = self.root(pwd).join(key); - path.set_extension("toml"); - path - } - - pub fn list_paths(&self, paths: &[Location]) -> Result, Error> { - Ok(paths - .iter() - .flat_map(|p| self.list(p).unwrap_or_default()) - .collect()) - } - - pub fn list(&self, pwd: &Location) -> Result, Error> { - let path = self.root(pwd.as_ref()); - if path.exists() { - let mut files = read_dir(&path)?; - files.sort(); - - Ok(files - .into_iter() - .map(|(name, p)| (name, pwd.wrap(p))) - .collect()) - } else { - Ok(vec![]) - } - } - - pub fn remove(&self, key: &str, pwd: &Path) -> Result<(), Error> { - let path = self.path(pwd, key); - if path.exists() { - std::fs::remove_file(&path) - .map_err(|_| Error::ConfigRemoval(self.to_string(), key.to_string())) - } else { - Ok(()) - } - } -} - -fn global_config_path() -> Result { - Ok(if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { - PathBuf::from_str(&config_home).map_err(|_| Error::XdgConfigHome(config_home))? - } else { - dirs::home_dir() - .ok_or(Error::HomeDirNotFound)? - .join(".config") - } - .join("soroban")) -} - -impl Pwd for Args { - fn set_pwd(&mut self, pwd: &Path) { - self.config_dir = Some(pwd.to_path_buf()); - } -} diff --git a/cmd/soroban-cli/src/commands/config/mod.rs b/cmd/soroban-cli/src/commands/config/mod.rs deleted file mode 100644 index be76e77f..00000000 --- a/cmd/soroban-cli/src/commands/config/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::path::PathBuf; - -use clap::{arg, command, Parser}; -use serde::{Deserialize, Serialize}; - -use crate::Pwd; - -use self::{network::Network, secret::Secret}; - -use super::{keys, network}; - -pub mod locator; -pub mod secret; - -#[derive(Debug, Parser)] -pub enum Cmd { - /// Configure different networks. Depraecated, use `soroban network` instead. - #[command(subcommand)] - Network(network::Cmd), - /// Identity management. Deprecated, use `soroban keys` instead. - #[command(subcommand)] - Identity(keys::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Identity(#[from] keys::Error), - #[error(transparent)] - Network(#[from] network::Error), - #[error(transparent)] - Secret(#[from] secret::Error), - #[error(transparent)] - Config(#[from] locator::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Identity(identity) => identity.run().await?, - Cmd::Network(network) => network.run()?, - } - Ok(()) - } -} - -#[derive(Debug, clap::Args, Clone, Default)] -#[group(skip)] -pub struct Args { - #[command(flatten)] - pub network: network::Args, - - #[arg(long, visible_alias = "source", env = "SOROBAN_ACCOUNT")] - /// Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). Default: `identity generate --default-seed` - pub source_account: String, - - #[arg(long)] - /// If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` - pub hd_path: Option, - - #[command(flatten)] - pub locator: locator::Args, -} - -impl Args { - pub fn key_pair(&self) -> Result { - let key = self.account(&self.source_account)?; - Ok(key.key_pair(self.hd_path)?) - } - - pub fn account(&self, account_str: &str) -> Result { - if let Ok(secret) = self.locator.read_identity(account_str) { - Ok(secret) - } else { - Ok(account_str.parse::()?) - } - } - - pub fn get_network(&self) -> Result { - Ok(self.network.get(&self.locator)?) - } - - pub fn config_dir(&self) -> Result { - Ok(self.locator.config_dir()?) - } -} - -impl Pwd for Args { - fn set_pwd(&mut self, pwd: &std::path::Path) { - self.locator.set_pwd(pwd); - } -} - -#[derive(Default, Serialize, Deserialize)] -pub struct Config {} diff --git a/cmd/soroban-cli/src/commands/config/secret.rs b/cmd/soroban-cli/src/commands/config/secret.rs deleted file mode 100644 index 4684e2a8..00000000 --- a/cmd/soroban-cli/src/commands/config/secret.rs +++ /dev/null @@ -1,143 +0,0 @@ -use clap::arg; -use serde::{Deserialize, Serialize}; -use std::{io::Write, str::FromStr}; -use stellar_strkey::ed25519::{PrivateKey, PublicKey}; - -use crate::utils; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("invalid secret key")] - InvalidSecretKey, - // #[error("seed_phrase must be 12 words long, found {len}")] - // InvalidSeedPhrase { len: usize }, - #[error("seceret input error")] - PasswordRead, - #[error(transparent)] - Secret(#[from] stellar_strkey::DecodeError), - #[error(transparent)] - SeedPhrase(#[from] sep5::error::Error), - #[error(transparent)] - Ed25519(#[from] ed25519_dalek::SignatureError), - #[error("Invalid address {0}")] - InvalidAddress(String), -} - -#[derive(Debug, clap::Args, Clone)] -#[group(skip)] -pub struct Args { - /// Add using secret_key - /// Can provide with SOROBAN_SECRET_KEY - #[arg(long, conflicts_with = "seed_phrase")] - pub secret_key: bool, - /// Add using 12 word seed phrase to generate secret_key - #[arg(long, conflicts_with = "secret_key")] - pub seed_phrase: bool, -} - -impl Args { - pub fn read_secret(&self) -> Result { - if let Ok(secret_key) = std::env::var("SOROBAN_SECRET_KEY") { - Ok(Secret::SecretKey { secret_key }) - } else if self.secret_key { - println!("Type a secret key: "); - let secret_key = read_password()?; - let secret_key = PrivateKey::from_string(&secret_key) - .map_err(|_| Error::InvalidSecretKey)? - .to_string(); - Ok(Secret::SecretKey { secret_key }) - } else if self.seed_phrase { - println!("Type a 12 word seed phrase: "); - let seed_phrase = read_password()?; - let seed_phrase: Vec<&str> = seed_phrase.split_whitespace().collect(); - // if seed_phrase.len() != 12 { - // let len = seed_phrase.len(); - // return Err(Error::InvalidSeedPhrase { len }); - // } - Ok(Secret::SeedPhrase { - seed_phrase: seed_phrase - .into_iter() - .map(ToString::to_string) - .collect::>() - .join(" "), - }) - } else { - Err(Error::PasswordRead {}) - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum Secret { - SecretKey { secret_key: String }, - SeedPhrase { seed_phrase: String }, -} - -impl FromStr for Secret { - type Err = Error; - - fn from_str(s: &str) -> Result { - if PrivateKey::from_string(s).is_ok() { - Ok(Secret::SecretKey { - secret_key: s.to_string(), - }) - } else if sep5::SeedPhrase::from_str(s).is_ok() { - Ok(Secret::SeedPhrase { - seed_phrase: s.to_string(), - }) - } else { - Err(Error::InvalidAddress(s.to_string())) - } - } -} - -impl From for Secret { - fn from(value: PrivateKey) -> Self { - Secret::SecretKey { - secret_key: value.to_string(), - } - } -} - -impl Secret { - pub fn private_key(&self, index: Option) -> Result { - Ok(match self { - Secret::SecretKey { secret_key } => PrivateKey::from_string(secret_key)?, - Secret::SeedPhrase { seed_phrase } => sep5::SeedPhrase::from_str(seed_phrase)? - .from_path_index(index.unwrap_or_default(), None)? - .private(), - }) - } - - pub fn public_key(&self, index: Option) -> Result { - let key = self.key_pair(index)?; - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - key.verifying_key().as_bytes(), - )?) - } - - pub fn key_pair(&self, index: Option) -> Result { - Ok(utils::into_signing_key(&self.private_key(index)?)) - } - - pub fn from_seed(seed: Option<&str>) -> Result { - let seed_phrase = if let Some(seed) = seed.map(str::as_bytes) { - sep5::SeedPhrase::from_entropy(seed) - } else { - sep5::SeedPhrase::random(sep5::MnemonicType::Words12) - }? - .seed_phrase - .into_phrase(); - Ok(Secret::SeedPhrase { seed_phrase }) - } - - pub fn test_seed_phrase() -> Result { - Self::from_seed(Some("0000000000000000")) - } -} - -fn read_password() -> Result { - std::io::stdout().flush().map_err(|_| Error::PasswordRead)?; - rpassword::read_password().map_err(|_| Error::PasswordRead) -} diff --git a/cmd/soroban-cli/src/commands/contract/asset.rs b/cmd/soroban-cli/src/commands/contract/asset.rs deleted file mode 100644 index ad7be020..00000000 --- a/cmd/soroban-cli/src/commands/contract/asset.rs +++ /dev/null @@ -1,27 +0,0 @@ -use super::{deploy, id}; - -#[derive(Debug, clap::Subcommand)] -pub enum Cmd { - /// Get Id of builtin Soroban Asset Contract. Deprecated, use `soroban contract id asset` instead - Id(id::asset::Cmd), - /// Deploy builtin Soroban Asset Contract - Deploy(deploy::asset::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Id(#[from] id::asset::Error), - #[error(transparent)] - Deploy(#[from] deploy::asset::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Id(id) => id.run()?, - Cmd::Deploy(asset) => asset.run().await?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/bindings.rs b/cmd/soroban-cli/src/commands/contract/bindings.rs deleted file mode 100644 index 1da94697..00000000 --- a/cmd/soroban-cli/src/commands/contract/bindings.rs +++ /dev/null @@ -1,38 +0,0 @@ -pub mod json; -pub mod rust; -pub mod typescript; - -#[derive(Debug, clap::Subcommand)] -pub enum Cmd { - /// Generate Json Bindings - Json(json::Cmd), - - /// Generate Rust bindings - Rust(rust::Cmd), - - /// Generate a TypeScript / JavaScript package - Typescript(typescript::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Json(#[from] json::Error), - - #[error(transparent)] - Rust(#[from] rust::Error), - - #[error(transparent)] - Typescript(#[from] typescript::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Json(json) => json.run()?, - Cmd::Rust(rust) => rust.run()?, - Cmd::Typescript(ts) => ts.run().await?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/bindings/json.rs b/cmd/soroban-cli/src/commands/contract/bindings/json.rs deleted file mode 100644 index 060f9064..00000000 --- a/cmd/soroban-cli/src/commands/contract/bindings/json.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::fmt::Debug; - -use clap::{command, Parser}; -use soroban_spec_json; - -use crate::wasm; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - wasm: wasm::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("generate json from file: {0}")] - GenerateJsonFromFile(soroban_spec_json::GenerateFromFileError), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let wasm_path_str = self.wasm.wasm.to_string_lossy(); - let json = soroban_spec_json::generate_from_file(&wasm_path_str, None) - .map_err(Error::GenerateJsonFromFile)?; - println!("{json}"); - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/bindings/rust.rs b/cmd/soroban-cli/src/commands/contract/bindings/rust.rs deleted file mode 100644 index 176732ec..00000000 --- a/cmd/soroban-cli/src/commands/contract/bindings/rust.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fmt::Debug; - -use clap::{command, Parser}; -use soroban_spec_rust::{self, ToFormattedString}; - -use crate::wasm; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - wasm: wasm::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("generate rust from file: {0}")] - GenerateRustFromFile(soroban_spec_rust::GenerateFromFileError), - #[error("format rust error: {0}")] - FormatRust(String), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let wasm_path_str = self.wasm.wasm.to_string_lossy(); - let code = soroban_spec_rust::generate_from_file(&wasm_path_str, None) - .map_err(Error::GenerateRustFromFile)?; - match code.to_formatted_string() { - Ok(formatted) => { - println!("{formatted}"); - Ok(()) - } - Err(e) => { - println!("{code}"); - Err(Error::FormatRust(e.to_string())) - } - } - } -} diff --git a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs deleted file mode 100644 index 19c7eecd..00000000 --- a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::{ffi::OsString, fmt::Debug, path::PathBuf}; - -use clap::{command, Parser}; -use soroban_spec_typescript::{self as typescript, boilerplate::Project}; - -use crate::wasm; -use crate::{ - commands::{ - config::locator, - contract::{self, fetch}, - network::{self, Network}, - }, - utils::contract_spec::{self, ContractSpec}, -}; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// Path to optional wasm binary - #[arg(long)] - pub wasm: Option, - /// Where to place generated project - #[arg(long)] - output_dir: PathBuf, - /// Whether to overwrite output directory if it already exists - #[arg(long)] - overwrite: bool, - /// The contract ID/address on the network - #[arg(long, visible_alias = "id")] - contract_id: String, - #[command(flatten)] - locator: locator::Args, - #[command(flatten)] - network: network::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("failed generate TS from file: {0}")] - GenerateTSFromFile(typescript::GenerateFromFileError), - #[error(transparent)] - Io(#[from] std::io::Error), - - #[error("--output-dir cannot be a file: {0:?}")] - IsFile(PathBuf), - - #[error("--output-dir already exists and you did not specify --overwrite: {0:?}")] - OutputDirExists(PathBuf), - - #[error("--output-dir filepath not representable as utf-8: {0:?}")] - NotUtf8(OsString), - - #[error(transparent)] - Network(#[from] network::Error), - - #[error(transparent)] - Locator(#[from] locator::Error), - #[error(transparent)] - Fetch(#[from] fetch::Error), - #[error(transparent)] - Spec(#[from] contract_spec::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[error("Failed to get file name from path: {0:?}")] - FailedToGetFileName(PathBuf), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let spec = if let Some(wasm) = &self.wasm { - let wasm: wasm::Args = wasm.into(); - wasm.parse()?.spec - } else { - let fetch = contract::fetch::Cmd { - contract_id: self.contract_id.clone(), - out_file: None, - locator: self.locator.clone(), - network: self.network.clone(), - }; - let bytes = fetch.get_bytes().await?; - ContractSpec::new(&bytes)?.spec - }; - if self.output_dir.is_file() { - return Err(Error::IsFile(self.output_dir.clone())); - } - if self.output_dir.exists() { - if self.overwrite { - std::fs::remove_dir_all(&self.output_dir)?; - } else { - return Err(Error::OutputDirExists(self.output_dir.clone())); - } - } - std::fs::create_dir_all(&self.output_dir)?; - let p: Project = self.output_dir.clone().try_into()?; - let Network { - rpc_url, - network_passphrase, - .. - } = self - .network - .get(&self.locator) - .ok() - .unwrap_or_else(Network::futurenet); - let absolute_path = self.output_dir.canonicalize()?; - let file_name = absolute_path - .file_name() - .ok_or_else(|| Error::FailedToGetFileName(absolute_path.clone()))?; - let contract_name = &file_name - .to_str() - .ok_or_else(|| Error::NotUtf8(file_name.to_os_string()))?; - p.init( - contract_name, - &self.contract_id, - &rpc_url, - &network_passphrase, - &spec, - )?; - std::process::Command::new("npm") - .arg("install") - .current_dir(&self.output_dir) - .spawn()? - .wait()?; - std::process::Command::new("npm") - .arg("run") - .arg("build") - .current_dir(&self.output_dir) - .spawn()? - .wait()?; - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs deleted file mode 100644 index ba17bd1b..00000000 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ /dev/null @@ -1,194 +0,0 @@ -use clap::Parser; -use itertools::Itertools; -use std::{ - collections::HashSet, - env, - ffi::OsStr, - fmt::Debug, - fs, io, - path::Path, - process::{Command, ExitStatus, Stdio}, -}; - -use cargo_metadata::{Metadata, MetadataCommand, Package}; - -/// Build a contract from source -/// -/// Builds all crates that are referenced by the cargo manifest (Cargo.toml) -/// that have cdylib as their crate-type. Crates are built for the wasm32 -/// target. Unless configured otherwise, crates are built with their default -/// features and with their release profile. -/// -/// To view the commands that will be executed, without executing them, use the -/// --print-commands-only option. -#[derive(Parser, Debug, Clone)] -pub struct Cmd { - /// Path to Cargo.toml - #[arg(long, default_value = "Cargo.toml")] - pub manifest_path: std::path::PathBuf, - /// Package to build - /// - /// If omitted, all packages that build for crate-type cdylib are built. - #[arg(long)] - pub package: Option, - /// Build with the specified profile - #[arg(long, default_value = "release")] - pub profile: String, - /// Build with the list of features activated, space or comma separated - #[arg(long, help_heading = "Features")] - pub features: Option, - /// Build with the all features activated - #[arg( - long, - conflicts_with = "features", - conflicts_with = "no_default_features", - help_heading = "Features" - )] - pub all_features: bool, - /// Build with the default feature not activated - #[arg(long, help_heading = "Features")] - pub no_default_features: bool, - /// Directory to copy wasm files to - /// - /// If provided, wasm files can be found in the cargo target directory, and - /// the specified directory. - /// - /// If ommitted, wasm files are written only to the cargo target directory. - #[arg(long)] - pub out_dir: Option, - /// Print commands to build without executing them - #[arg(long, conflicts_with = "out_dir", help_heading = "Other")] - pub print_commands_only: bool, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Metadata(#[from] cargo_metadata::Error), - #[error(transparent)] - CargoCmd(io::Error), - #[error("exit status {0}")] - Exit(ExitStatus), - #[error("package {package} not found")] - PackageNotFound { package: String }, - #[error("creating out directory: {0}")] - CreatingOutDir(io::Error), - #[error("copying wasm file: {0}")] - CopyingWasmFile(io::Error), - #[error("getting the current directory: {0}")] - GettingCurrentDir(io::Error), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let working_dir = env::current_dir().map_err(Error::GettingCurrentDir)?; - - let metadata = self.metadata()?; - let packages = self.packages(&metadata); - let target_dir = &metadata.target_directory; - - if let Some(package) = &self.package { - if packages.is_empty() { - return Err(Error::PackageNotFound { - package: package.clone(), - }); - } - } - - for p in packages { - let mut cmd = Command::new("cargo"); - cmd.stdout(Stdio::piped()); - cmd.arg("rustc"); - let manifest_path = pathdiff::diff_paths(&p.manifest_path, &working_dir) - .unwrap_or(p.manifest_path.clone().into()); - cmd.arg(format!( - "--manifest-path={}", - manifest_path.to_string_lossy() - )); - cmd.arg("--crate-type=cdylib"); - cmd.arg("--target=wasm32-unknown-unknown"); - if self.profile == "release" { - cmd.arg("--release"); - } else { - cmd.arg(format!("--profile={}", self.profile)); - } - if self.all_features { - cmd.arg("--all-features"); - } - if self.no_default_features { - cmd.arg("--no-default-features"); - } - if let Some(features) = self.features() { - let requested: HashSet = features.iter().cloned().collect(); - let available = p.features.iter().map(|f| f.0).cloned().collect(); - let activate = requested.intersection(&available).join(","); - if !activate.is_empty() { - cmd.arg(format!("--features={activate}")); - } - } - let cmd_str = format!( - "cargo {}", - cmd.get_args().map(OsStr::to_string_lossy).join(" ") - ); - - if self.print_commands_only { - println!("{cmd_str}"); - } else { - eprintln!("{cmd_str}"); - let status = cmd.status().map_err(Error::CargoCmd)?; - if !status.success() { - return Err(Error::Exit(status)); - } - - if let Some(out_dir) = &self.out_dir { - fs::create_dir_all(out_dir).map_err(Error::CreatingOutDir)?; - - let file = format!("{}.wasm", p.name.replace('-', "_")); - let target_file_path = Path::new(target_dir) - .join("wasm32-unknown-unknown") - .join(&self.profile) - .join(&file); - let out_file_path = Path::new(out_dir).join(&file); - fs::copy(target_file_path, out_file_path).map_err(Error::CopyingWasmFile)?; - } - } - } - - Ok(()) - } - - fn features(&self) -> Option> { - self.features - .as_ref() - .map(|f| f.split(&[',', ' ']).map(String::from).collect()) - } - - fn packages(&self, metadata: &Metadata) -> Vec { - metadata - .packages - .iter() - .filter(|p| - // Filter by the package name if one is provided. - self.package.is_none() || Some(&p.name) == self.package.as_ref()) - .filter(|p| { - // Filter crates by those that build to cdylib (wasm), unless a - // package is provided. - self.package.is_some() - || p.targets - .iter() - .any(|t| t.crate_types.iter().any(|c| c == "cdylib")) - }) - .cloned() - .collect() - } - - fn metadata(&self) -> Result { - let mut cmd = MetadataCommand::new(); - cmd.no_deps(); - cmd.manifest_path(&self.manifest_path); - // Do not configure features on the metadata command, because we are - // only collecting non-dependency metadata, features have no impact on - // the output. - cmd.exec() - } -} diff --git a/cmd/soroban-cli/src/commands/contract/deploy.rs b/cmd/soroban-cli/src/commands/contract/deploy.rs deleted file mode 100644 index 9baf4459..00000000 --- a/cmd/soroban-cli/src/commands/contract/deploy.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub mod asset; -pub mod wasm; - -#[derive(Debug, clap::Subcommand)] -pub enum Cmd { - /// Deploy builtin Soroban Asset Contract - Asset(asset::Cmd), - /// Deploy normal Wasm Contract - Wasm(wasm::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Asset(#[from] asset::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Asset(asset) => asset.run().await?, - Cmd::Wasm(wasm) => wasm.run().await?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs deleted file mode 100644 index c10bf816..00000000 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ /dev/null @@ -1,155 +0,0 @@ -use clap::{arg, command, Parser}; -use soroban_env_host::{ - xdr::{ - Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, - Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, - LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, Preconditions, - ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, Uint256, VecM, - }, - HostError, -}; -use std::convert::Infallible; -use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError}; - -use crate::{ - commands::config, - rpc::{Client, Error as SorobanRpcError}, - utils::{contract_id_hash_from_asset, parsing::parse_asset}, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), - #[error("error parsing int: {0}")] - ParseIntError(#[from] ParseIntError), - #[error(transparent)] - Client(#[from] SorobanRpcError), - #[error("internal conversion error: {0}")] - TryFromSliceError(#[from] TryFromSliceError), - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - ParseAssetError(#[from] crate::utils::parsing::Error), -} - -impl From for Error { - fn from(_: Infallible) -> Self { - unreachable!() - } -} - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" - #[arg(long)] - pub asset: String, - - #[command(flatten)] - pub config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - // Parse asset - let asset = parse_asset(&self.asset)?; - - let res_str = self.run_against_rpc_server(asset).await?; - println!("{res_str}"); - Ok(()) - } - - async fn run_against_rpc_server(&self, asset: Asset) -> Result { - let network = self.config.get_network()?; - let client = Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - let key = self.config.key_pair()?; - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - // TODO: use symbols for the method names (both here and in serve) - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - let network_passphrase = &network.network_passphrase; - let contract_id = contract_id_hash_from_asset(&asset, network_passphrase)?; - let tx = build_wrap_token_tx( - &asset, - &contract_id, - sequence + 1, - self.fee.fee, - network_passphrase, - &key, - )?; - - client - .prepare_and_send_transaction(&tx, &key, &[], network_passphrase, None, None) - .await?; - - Ok(stellar_strkey::Contract(contract_id.0).to_string()) - } -} - -fn build_wrap_token_tx( - asset: &Asset, - contract_id: &Hash, - sequence: i64, - fee: u32, - _network_passphrase: &str, - key: &ed25519_dalek::SigningKey, -) -> Result { - let contract = ScAddress::Contract(contract_id.clone()); - let mut read_write = vec![ - ContractData(LedgerKeyContractData { - contract: contract.clone(), - key: ScVal::LedgerKeyContractInstance, - durability: ContractDataDurability::Persistent, - }), - ContractData(LedgerKeyContractData { - contract: contract.clone(), - key: ScVal::Vec(Some( - vec![ScVal::Symbol("Metadata".try_into().unwrap())].try_into()?, - )), - durability: ContractDataDurability::Persistent, - }), - ]; - if asset != &Asset::Native { - read_write.push(ContractData(LedgerKeyContractData { - contract, - key: ScVal::Vec(Some( - vec![ScVal::Symbol("Admin".try_into().unwrap())].try_into()?, - )), - durability: ContractDataDurability::Persistent, - })); - } - - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage: ContractIdPreimage::Asset(asset.clone()), - executable: ContractExecutable::StellarAsset, - }), - auth: VecM::default(), - }), - }; - - Ok(Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee, - seq_num: SequenceNumber(sequence), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![op].try_into()?, - ext: TransactionExt::V0, - }) -} diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs deleted file mode 100644 index 76c13017..00000000 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ /dev/null @@ -1,228 +0,0 @@ -use std::array::TryFromSliceError; -use std::fmt::Debug; -use std::num::ParseIntError; - -use clap::{arg, command, Parser}; -use rand::Rng; -use soroban_env_host::{ - xdr::{ - AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, - CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Memo, - MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, - SequenceNumber, Transaction, TransactionExt, Uint256, VecM, - }, - HostError, -}; - -use crate::commands::contract::{self, id::wasm::get_contract_id}; -use crate::{ - commands::{config, contract::install, HEADING_RPC}, - rpc::{self, Client}, - utils, wasm, -}; - -#[derive(Parser, Debug, Clone)] -#[command(group( - clap::ArgGroup::new("wasm_src") - .required(true) - .args(&["wasm", "wasm_hash"]), -))] -#[group(skip)] -pub struct Cmd { - /// WASM file to deploy - #[arg(long, group = "wasm_src")] - wasm: Option, - /// Hash of the already installed/deployed WASM file - #[arg(long = "wasm-hash", conflicts_with = "wasm", group = "wasm_src")] - wasm_hash: Option, - /// Custom salt 32-byte salt for the token id - #[arg( - long, - help_heading = HEADING_RPC, - )] - salt: Option, - #[command(flatten)] - config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, - #[arg(long, short = 'i', default_value = "false")] - /// Whether to ignore safety checks when deploying contracts - pub ignore_checks: bool, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Install(#[from] install::Error), - #[error(transparent)] - Host(#[from] HostError), - #[error("error parsing int: {0}")] - ParseIntError(#[from] ParseIntError), - #[error("internal conversion error: {0}")] - TryFromSliceError(#[from] TryFromSliceError), - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error("jsonrpc error: {0}")] - JsonRpc(#[from] jsonrpsee_core::Error), - #[error("cannot parse salt: {salt}")] - CannotParseSalt { salt: String }, - #[error("cannot parse contract ID {contract_id}: {error}")] - CannotParseContractId { - contract_id: String, - error: stellar_strkey::DecodeError, - }, - #[error("cannot parse WASM hash {wasm_hash}: {error}")] - CannotParseWasmHash { - wasm_hash: String, - error: stellar_strkey::DecodeError, - }, - #[error("Must provide either --wasm or --wash-hash")] - WasmNotProvided, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), - #[error(transparent)] - Infallible(#[from] std::convert::Infallible), - #[error(transparent)] - WasmId(#[from] contract::id::wasm::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let res_str = self.run_and_get_contract_id().await?; - println!("{res_str}"); - Ok(()) - } - - pub async fn run_and_get_contract_id(&self) -> Result { - let wasm_hash = if let Some(wasm) = &self.wasm { - let hash = install::Cmd { - wasm: wasm::Args { wasm: wasm.clone() }, - config: self.config.clone(), - fee: self.fee.clone(), - ignore_checks: self.ignore_checks, - } - .run_and_get_hash() - .await?; - hex::encode(hash) - } else { - self.wasm_hash - .as_ref() - .ok_or(Error::WasmNotProvided)? - .to_string() - }; - - let hash = Hash(utils::contract_id_from_str(&wasm_hash).map_err(|e| { - Error::CannotParseWasmHash { - wasm_hash: wasm_hash.clone(), - error: e, - } - })?); - - self.run_against_rpc_server(hash).await - } - - async fn run_against_rpc_server(&self, wasm_hash: Hash) -> Result { - let network = self.config.get_network()?; - let salt: [u8; 32] = match &self.salt { - Some(h) => soroban_spec_tools::utils::padded_hex_from_str(h, 32) - .map_err(|_| Error::CannotParseSalt { salt: h.clone() })? - .try_into() - .map_err(|_| Error::CannotParseSalt { salt: h.clone() })?, - None => rand::thread_rng().gen::<[u8; 32]>(), - }; - - let client = Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - let key = self.config.key_pair()?; - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - let (tx, contract_id) = build_create_contract_tx( - wasm_hash, - sequence + 1, - self.fee.fee, - &network.network_passphrase, - salt, - &key, - )?; - client - .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) - .await?; - Ok(stellar_strkey::Contract(contract_id.0).to_string()) - } -} - -fn build_create_contract_tx( - hash: Hash, - sequence: i64, - fee: u32, - network_passphrase: &str, - salt: [u8; 32], - key: &ed25519_dalek::SigningKey, -) -> Result<(Transaction, Hash), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519( - key.verifying_key().to_bytes().into(), - )); - - let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { - address: ScAddress::Account(source_account), - salt: Uint256(salt), - }); - let contract_id = get_contract_id(contract_id_preimage.clone(), network_passphrase)?; - - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage, - executable: ContractExecutable::Wasm(hash), - }), - auth: VecM::default(), - }), - }; - let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee, - seq_num: SequenceNumber(sequence), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![op].try_into()?, - ext: TransactionExt::V0, - }; - - Ok((tx, Hash(contract_id.into()))) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_create_contract() { - let hash = hex::decode("0000000000000000000000000000000000000000000000000000000000000000") - .unwrap() - .try_into() - .unwrap(); - let result = build_create_contract_tx( - Hash(hash), - 300, - 1, - "Public Global Stellar Network ; September 2015", - [0u8; 32], - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), - ); - - assert!(result.is_ok()); - } -} diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs deleted file mode 100644 index 7e9f1e98..00000000 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::{fmt::Debug, path::Path, str::FromStr}; - -use clap::{command, Parser}; -use soroban_env_host::xdr::{ - Error as XdrError, ExtendFootprintTtlOp, ExtensionPoint, LedgerEntry, LedgerEntryChange, - LedgerEntryData, LedgerFootprint, Memo, MuxedAccount, Operation, OperationBody, Preconditions, - SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, - TransactionMeta, TransactionMetaV3, TtlEntry, Uint256, -}; - -use crate::{ - commands::config, - key, - rpc::{self, Client}, - wasm, Pwd, -}; - -const MAX_LEDGERS_TO_EXTEND: u32 = 535_679; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// Number of ledgers to extend the entries - #[arg(long, required = true)] - pub ledgers_to_extend: u32, - /// Only print the new Time To Live ledger - #[arg(long)] - pub ttl_ledger_only: bool, - #[command(flatten)] - pub key: key::Args, - #[command(flatten)] - pub config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, -} - -impl FromStr for Cmd { - type Err = clap::error::Error; - - fn from_str(s: &str) -> Result { - use clap::{CommandFactory, FromArgMatches}; - Self::from_arg_matches_mut(&mut Self::command().get_matches_from(s.split_whitespace())) - } -} - -impl Pwd for Cmd { - fn set_pwd(&mut self, pwd: &Path) { - self.config.set_pwd(pwd); - } -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("parsing key {key}: {error}")] - CannotParseKey { - key: String, - error: soroban_spec_tools::Error, - }, - #[error("parsing XDR key {key}: {error}")] - CannotParseXdrKey { key: String, error: XdrError }, - - #[error(transparent)] - Config(#[from] config::Error), - #[error("either `--key` or `--key-xdr` are required")] - KeyIsRequired, - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error("Ledger entry not found")] - LedgerEntryNotFound, - #[error("missing operation result")] - MissingOperationResult, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[error(transparent)] - Key(#[from] key::Error), -} - -impl Cmd { - #[allow(clippy::too_many_lines)] - pub async fn run(&self) -> Result<(), Error> { - let ttl_ledger = self.run_against_rpc_server().await?; - if self.ttl_ledger_only { - println!("{ttl_ledger}"); - } else { - println!("New ttl ledger: {ttl_ledger}"); - } - - Ok(()) - } - - fn ledgers_to_extend(&self) -> u32 { - let res = u32::min(self.ledgers_to_extend, MAX_LEDGERS_TO_EXTEND); - if res < self.ledgers_to_extend { - tracing::warn!( - "Ledgers to extend is too large, using max value of {MAX_LEDGERS_TO_EXTEND}" - ); - } - res - } - - async fn run_against_rpc_server(&self) -> Result { - let network = self.config.get_network()?; - tracing::trace!(?network); - let keys = self.key.parse_keys()?; - let network = &self.config.get_network()?; - let client = Client::new(&network.rpc_url)?; - let key = self.config.key_pair()?; - let extend_to = self.ledgers_to_extend(); - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - - let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee: self.fee.fee, - seq_num: SequenceNumber(sequence + 1), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![Operation { - source_account: None, - body: OperationBody::ExtendFootprintTtl(ExtendFootprintTtlOp { - ext: ExtensionPoint::V0, - extend_to, - }), - }] - .try_into()?, - ext: TransactionExt::V1(SorobanTransactionData { - ext: ExtensionPoint::V0, - resources: SorobanResources { - footprint: LedgerFootprint { - read_only: keys.clone().try_into()?, - read_write: vec![].try_into()?, - }, - instructions: 0, - read_bytes: 0, - write_bytes: 0, - }, - resource_fee: 0, - }), - }; - - let (result, meta, events) = client - .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) - .await?; - - tracing::trace!(?result); - tracing::trace!(?meta); - if !events.is_empty() { - tracing::info!("Events:\n {events:#?}"); - } - - // The transaction from core will succeed regardless of whether it actually found & extended - // the entry, so we have to inspect the result meta to tell if it worked or not. - let TransactionMeta::V3(TransactionMetaV3 { operations, .. }) = meta else { - return Err(Error::LedgerEntryNotFound); - }; - - // Simply check if there is exactly one entry here. We only support extending a single - // entry via this command (which we should fix separately, but). - if operations.len() == 0 { - return Err(Error::LedgerEntryNotFound); - } - - if operations[0].changes.is_empty() { - let entry = client.get_full_ledger_entries(&keys).await?; - let extension = entry.entries[0].live_until_ledger_seq; - if entry.latest_ledger + i64::from(extend_to) < i64::from(extension) { - return Ok(extension); - } - } - - match (&operations[0].changes[0], &operations[0].changes[1]) { - ( - LedgerEntryChange::State(_), - LedgerEntryChange::Updated(LedgerEntry { - data: - LedgerEntryData::Ttl(TtlEntry { - live_until_ledger_seq, - .. - }), - .. - }), - ) => Ok(*live_until_ledger_seq), - _ => Err(Error::LedgerEntryNotFound), - } - } -} diff --git a/cmd/soroban-cli/src/commands/contract/fetch.rs b/cmd/soroban-cli/src/commands/contract/fetch.rs deleted file mode 100644 index 61a82fc4..00000000 --- a/cmd/soroban-cli/src/commands/contract/fetch.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::convert::Infallible; - -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::{fmt::Debug, fs, io}; - -use clap::{arg, command, Parser}; -use soroban_env_host::{ - budget::Budget, - storage::Storage, - xdr::{ - self, ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractExecutable, - Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractCode, - LedgerKeyContractData, ScAddress, ScContractInstance, ScVal, - }, -}; - -use soroban_spec::read::FromWasmError; -use stellar_strkey::DecodeError; - -use super::super::config::{self, locator}; -use crate::commands::network::{self, Network}; -use crate::{ - rpc::{self, Client}, - utils, Pwd, -}; - -#[derive(Parser, Debug, Default, Clone)] -#[allow(clippy::struct_excessive_bools)] -#[group(skip)] -pub struct Cmd { - /// Contract ID to fetch - #[arg(long = "id", env = "SOROBAN_CONTRACT_ID")] - pub contract_id: String, - /// Where to write output otherwise stdout is used - #[arg(long, short = 'o')] - pub out_file: Option, - #[command(flatten)] - pub locator: locator::Args, - #[command(flatten)] - pub network: network::Args, -} - -impl FromStr for Cmd { - type Err = clap::error::Error; - - fn from_str(s: &str) -> Result { - use clap::{CommandFactory, FromArgMatches}; - Self::from_arg_matches_mut(&mut Self::command().get_matches_from(s.split_whitespace())) - } -} - -impl Pwd for Cmd { - fn set_pwd(&mut self, pwd: &Path) { - self.locator.set_pwd(pwd); - } -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Locator(#[from] locator::Error), - #[error(transparent)] - Xdr(#[from] XdrError), - #[error(transparent)] - Spec(#[from] soroban_spec::read::FromWasmError), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("missing result")] - MissingResult, - #[error("unexpected contract code data type: {0:?}")] - UnexpectedContractCodeDataType(LedgerEntryData), - #[error("reading file {0:?}: {1}")] - CannotWriteContractFile(PathBuf, io::Error), - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), - #[error("network details not provided")] - NetworkNotProvided, - #[error(transparent)] - Network(#[from] network::Error), - #[error("cannot create contract directory for {0:?}")] - CannotCreateContractDir(PathBuf), -} - -impl From for Error { - fn from(_: Infallible) -> Self { - unreachable!() - } -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let bytes = self.get_bytes().await?; - if let Some(out_file) = &self.out_file { - if let Some(parent) = out_file.parent() { - if !parent.exists() { - fs::create_dir_all(parent) - .map_err(|_| Error::CannotCreateContractDir(out_file.clone()))?; - } - } - fs::write(out_file, bytes) - .map_err(|io| Error::CannotWriteContractFile(out_file.clone(), io)) - } else { - let stdout = std::io::stdout(); - let mut handle = stdout.lock(); - handle.write_all(&bytes)?; - handle.flush()?; - Ok(()) - } - } - - pub async fn get_bytes(&self) -> Result, Error> { - self.run_against_rpc_server().await - } - - pub fn network(&self) -> Result { - Ok(self.network.get(&self.locator)?) - } - - pub async fn run_against_rpc_server(&self) -> Result, Error> { - let network = self.network()?; - tracing::trace!(?network); - let contract_id = self.contract_id()?; - let client = Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - // async closures are not yet stable - Ok(client.get_remote_wasm(&contract_id).await?) - } - - fn contract_id(&self) -> Result<[u8; 32], Error> { - utils::contract_id_from_str(&self.contract_id) - .map_err(|e| Error::CannotParseContractId(self.contract_id.clone(), e)) - } -} - -pub fn get_contract_wasm_from_storage( - storage: &mut Storage, - contract_id: [u8; 32], -) -> Result, FromWasmError> { - let key = LedgerKey::ContractData(LedgerKeyContractData { - contract: ScAddress::Contract(contract_id.into()), - key: ScVal::LedgerKeyContractInstance, - durability: ContractDataDurability::Persistent, - }); - match storage.get(&key.into(), &Budget::default()) { - Ok(rc) => match rc.as_ref() { - xdr::LedgerEntry { - data: - LedgerEntryData::ContractData(ContractDataEntry { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), - .. - } => match executable { - ContractExecutable::Wasm(hash) => { - if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }) - .into(), - &Budget::default(), - ) { - match rc.as_ref() { - xdr::LedgerEntry { - data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), - .. - } => Ok(code.to_vec()), - _ => Err(FromWasmError::NotFound), - } - } else { - Err(FromWasmError::NotFound) - } - } - ContractExecutable::StellarAsset => todo!(), - }, - _ => Err(FromWasmError::NotFound), - }, - _ => Err(FromWasmError::NotFound), - } -} diff --git a/cmd/soroban-cli/src/commands/contract/id.rs b/cmd/soroban-cli/src/commands/contract/id.rs deleted file mode 100644 index bb8744d5..00000000 --- a/cmd/soroban-cli/src/commands/contract/id.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub mod asset; -pub mod wasm; - -#[derive(Debug, clap::Subcommand)] -pub enum Cmd { - /// Deploy builtin Soroban Asset Contract - Asset(asset::Cmd), - /// Deploy normal Wasm Contract - Wasm(wasm::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Asset(#[from] asset::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Asset(asset) => asset.run()?, - Cmd::Wasm(wasm) => wasm.run()?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/id/asset.rs b/cmd/soroban-cli/src/commands/contract/id/asset.rs deleted file mode 100644 index 34e5767a..00000000 --- a/cmd/soroban-cli/src/commands/contract/id/asset.rs +++ /dev/null @@ -1,36 +0,0 @@ -use clap::{arg, command, Parser}; - -use crate::commands::config; - -use crate::utils::contract_id_hash_from_asset; -use crate::utils::parsing::parse_asset; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" - #[arg(long)] - pub asset: String, - - #[command(flatten)] - pub config: config::Args, -} -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - ParseError(#[from] crate::utils::parsing::Error), - #[error(transparent)] - ConfigError(#[from] crate::commands::config::Error), - #[error(transparent)] - Xdr(#[from] soroban_env_host::xdr::Error), -} -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let asset = parse_asset(&self.asset)?; - let network = self.config.get_network()?; - let contract_id = contract_id_hash_from_asset(&asset, &network.network_passphrase)?; - let strkey_contract_id = stellar_strkey::Contract(contract_id.0).to_string(); - println!("{strkey_contract_id}"); - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/id/wasm.rs b/cmd/soroban-cli/src/commands/contract/id/wasm.rs deleted file mode 100644 index 9c02f07d..00000000 --- a/cmd/soroban-cli/src/commands/contract/id/wasm.rs +++ /dev/null @@ -1,68 +0,0 @@ -use clap::{arg, command, Parser}; -use sha2::{Digest, Sha256}; -use soroban_env_host::xdr::{ - self, AccountId, ContractIdPreimage, ContractIdPreimageFromAddress, Hash, HashIdPreimage, - HashIdPreimageContractId, Limits, PublicKey, ScAddress, Uint256, WriteXdr, -}; - -use crate::commands::config; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// ID of the Soroban contract - #[arg(long)] - pub salt: String, - - #[command(flatten)] - pub config: config::Args, -} -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - ParseError(#[from] crate::utils::parsing::Error), - #[error(transparent)] - ConfigError(#[from] crate::commands::config::Error), - #[error(transparent)] - Xdr(#[from] xdr::Error), - #[error("cannot parse salt {0}")] - CannotParseSalt(String), -} -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let salt: [u8; 32] = soroban_spec_tools::utils::padded_hex_from_str(&self.salt, 32) - .map_err(|_| Error::CannotParseSalt(self.salt.clone()))? - .try_into() - .map_err(|_| Error::CannotParseSalt(self.salt.clone()))?; - let contract_id_preimage = - contract_preimage(&self.config.key_pair()?.verifying_key(), salt); - let contract_id = get_contract_id( - contract_id_preimage.clone(), - &self.config.get_network()?.network_passphrase, - )?; - let strkey_contract_id = stellar_strkey::Contract(contract_id.0).to_string(); - println!("{strkey_contract_id}"); - Ok(()) - } -} - -pub fn contract_preimage(key: &ed25519_dalek::VerifyingKey, salt: [u8; 32]) -> ContractIdPreimage { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.to_bytes().into())); - ContractIdPreimage::Address(ContractIdPreimageFromAddress { - address: ScAddress::Account(source_account), - salt: Uint256(salt), - }) -} - -pub fn get_contract_id( - contract_id_preimage: ContractIdPreimage, - network_passphrase: &str, -) -> Result { - let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); - let preimage = HashIdPreimage::ContractId(HashIdPreimageContractId { - network_id, - contract_id_preimage, - }); - let preimage_xdr = preimage.to_xdr(Limits::none())?; - Ok(Hash(Sha256::digest(preimage_xdr).into())) -} diff --git a/cmd/soroban-cli/src/commands/contract/inspect.rs b/cmd/soroban-cli/src/commands/contract/inspect.rs deleted file mode 100644 index 355c18ca..00000000 --- a/cmd/soroban-cli/src/commands/contract/inspect.rs +++ /dev/null @@ -1,49 +0,0 @@ -use clap::{command, Parser}; -use soroban_env_host::xdr; -use std::{fmt::Debug, path::PathBuf}; -use tracing::debug; - -use super::SpecOutput; -use crate::{commands::config::locator, wasm}; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - wasm: wasm::Args, - /// Output just XDR in base64 - #[arg(long, default_value = "docs")] - output: SpecOutput, - - #[clap(flatten)] - locator: locator::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[error("missing spec for {0:?}")] - MissingSpec(PathBuf), - #[error(transparent)] - Xdr(#[from] xdr::Error), - #[error(transparent)] - Spec(#[from] crate::utils::contract_spec::Error), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let wasm = self.wasm.parse()?; - debug!("File: {}", self.wasm.wasm.to_string_lossy()); - let output = match self.output { - SpecOutput::XdrBase64 => wasm - .spec_base64 - .clone() - .ok_or_else(|| Error::MissingSpec(self.wasm.wasm.clone()))?, - SpecOutput::XdrBase64Array => wasm.spec_as_json_array()?, - SpecOutput::Docs => wasm.to_string(), - }; - println!("{output}"); - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs deleted file mode 100644 index 90577529..00000000 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ /dev/null @@ -1,223 +0,0 @@ -use std::array::TryFromSliceError; -use std::fmt::Debug; -use std::num::ParseIntError; - -use clap::{command, Parser}; -use soroban_env_host::xdr::{ - Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Memo, MuxedAccount, Operation, - OperationBody, Preconditions, ScMetaEntry, ScMetaV0, SequenceNumber, Transaction, - TransactionExt, TransactionResult, TransactionResultResult, Uint256, VecM, -}; - -use super::restore; -use crate::key; -use crate::rpc::{self, Client}; -use crate::{commands::config, utils, wasm}; - -const CONTRACT_META_SDK_KEY: &str = "rssdkver"; -const PUBLIC_NETWORK_PASSPHRASE: &str = "Public Global Stellar Network ; September 2015"; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - pub config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, - #[command(flatten)] - pub wasm: wasm::Args, - #[arg(long, short = 'i', default_value = "false")] - /// Whether to ignore safety checks when deploying contracts - pub ignore_checks: bool, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("error parsing int: {0}")] - ParseIntError(#[from] ParseIntError), - #[error("internal conversion error: {0}")] - TryFromSliceError(#[from] TryFromSliceError), - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error("jsonrpc error: {0}")] - JsonRpc(#[from] jsonrpsee_core::Error), - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[error("unexpected ({length}) simulate transaction result length")] - UnexpectedSimulateTransactionResultSize { length: usize }, - #[error(transparent)] - Restore(#[from] restore::Error), - #[error("cannot parse WASM file {wasm}: {error}")] - CannotParseWasm { - wasm: std::path::PathBuf, - error: wasm::Error, - }, - #[error("the deployed smart contract {wasm} was built with Soroban Rust SDK v{version}, a release candidate version not intended for use with the Stellar Public Network. To deploy anyway, use --ignore-checks")] - ContractCompiledWithReleaseCandidateSdk { - wasm: std::path::PathBuf, - version: String, - }, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let res_str = hex::encode(self.run_and_get_hash().await?); - println!("{res_str}"); - Ok(()) - } - - pub async fn run_and_get_hash(&self) -> Result { - self.run_against_rpc_server(&self.wasm.read()?).await - } - - async fn run_against_rpc_server(&self, contract: &[u8]) -> Result { - let network = self.config.get_network()?; - let client = Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - let wasm_spec = &self.wasm.parse().map_err(|e| Error::CannotParseWasm { - wasm: self.wasm.wasm.clone(), - error: e, - })?; - // Check Rust SDK version if using the public network. - if let Some(rs_sdk_ver) = get_contract_meta_sdk_version(wasm_spec) { - if rs_sdk_ver.contains("rc") - && !self.ignore_checks - && network.network_passphrase == PUBLIC_NETWORK_PASSPHRASE - { - return Err(Error::ContractCompiledWithReleaseCandidateSdk { - wasm: self.wasm.wasm.clone(), - version: rs_sdk_ver, - }); - } else if rs_sdk_ver.contains("rc") - && network.network_passphrase == PUBLIC_NETWORK_PASSPHRASE - { - tracing::warn!("the deployed smart contract {path} was built with Soroban Rust SDK v{rs_sdk_ver}, a release candidate version not intended for use with the Stellar Public Network", path = self.wasm.wasm.display()); - } - } - let key = self.config.key_pair()?; - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - - let (tx_without_preflight, hash) = - build_install_contract_code_tx(contract, sequence + 1, self.fee.fee, &key)?; - - // Currently internal errors are not returned if the contract code is expired - if let ( - TransactionResult { - result: TransactionResultResult::TxInternalError, - .. - }, - _, - _, - ) = client - .prepare_and_send_transaction( - &tx_without_preflight, - &key, - &[], - &network.network_passphrase, - None, - None, - ) - .await? - { - // Now just need to restore it and don't have to install again - restore::Cmd { - key: key::Args { - contract_id: None, - key: None, - key_xdr: None, - wasm: Some(self.wasm.wasm.clone()), - wasm_hash: None, - durability: super::Durability::Persistent, - }, - config: self.config.clone(), - fee: self.fee.clone(), - ledgers_to_extend: None, - ttl_ledger_only: true, - } - .run_against_rpc_server() - .await?; - } - - Ok(hash) - } -} - -fn get_contract_meta_sdk_version(wasm_spec: &utils::contract_spec::ContractSpec) -> Option { - let rs_sdk_version_option = if let Some(_meta) = &wasm_spec.meta_base64 { - wasm_spec.meta.iter().find(|entry| match entry { - ScMetaEntry::ScMetaV0(ScMetaV0 { key, .. }) => { - key.to_utf8_string_lossy().contains(CONTRACT_META_SDK_KEY) - } - }) - } else { - None - }; - if let Some(rs_sdk_version_entry) = &rs_sdk_version_option { - match rs_sdk_version_entry { - ScMetaEntry::ScMetaV0(ScMetaV0 { val, .. }) => { - return Some(val.to_utf8_string_lossy()); - } - } - } - None -} - -pub(crate) fn build_install_contract_code_tx( - source_code: &[u8], - sequence: i64, - fee: u32, - key: &ed25519_dalek::SigningKey, -) -> Result<(Transaction, Hash), XdrError> { - let hash = utils::contract_hash(source_code)?; - - let op = Operation { - source_account: Some(MuxedAccount::Ed25519(Uint256( - key.verifying_key().to_bytes(), - ))), - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::UploadContractWasm(source_code.try_into()?), - auth: VecM::default(), - }), - }; - - let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee, - seq_num: SequenceNumber(sequence), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![op].try_into()?, - ext: TransactionExt::V0, - }; - - Ok((tx, hash)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_install_contract_code() { - let result = build_install_contract_code_tx( - b"foo", - 300, - 1, - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), - ); - - assert!(result.is_ok()); - } -} diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs deleted file mode 100644 index 669342b0..00000000 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ /dev/null @@ -1,484 +0,0 @@ -use std::collections::HashMap; -use std::convert::{Infallible, TryInto}; -use std::ffi::OsString; -use std::num::ParseIntError; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::{fmt::Debug, fs, io}; - -use clap::{arg, command, value_parser, Parser}; -use ed25519_dalek::SigningKey; -use heck::ToKebabCase; - -use soroban_env_host::{ - xdr::{ - self, Error as XdrError, Hash, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, - LedgerEntryData, LedgerFootprint, Memo, MuxedAccount, Operation, OperationBody, - Preconditions, ScAddress, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, - SequenceNumber, SorobanAuthorizationEntry, SorobanResources, Transaction, TransactionExt, - Uint256, VecM, - }, - HostError, -}; - -use soroban_spec::read::FromWasmError; -use stellar_strkey::DecodeError; - -use super::super::{ - config::{self, locator}, - events, -}; -use crate::{ - commands::global, - rpc::{self, Client}, - utils::{self, contract_spec}, - Pwd, -}; -use soroban_spec_tools::Spec; - -#[derive(Parser, Debug, Default, Clone)] -#[allow(clippy::struct_excessive_bools)] -#[group(skip)] -pub struct Cmd { - /// Contract ID to invoke - #[arg(long = "id", env = "SOROBAN_CONTRACT_ID")] - pub contract_id: String, - // For testing only - #[arg(skip)] - pub wasm: Option, - /// Output the cost execution to stderr - #[arg(long = "cost")] - pub cost: bool, - /// Function name as subcommand, then arguments for that function as `--arg-name value` - #[arg(last = true, id = "CONTRACT_FN_AND_ARGS")] - pub slop: Vec, - #[command(flatten)] - pub config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, -} - -impl FromStr for Cmd { - type Err = clap::error::Error; - - fn from_str(s: &str) -> Result { - use clap::{CommandFactory, FromArgMatches}; - Self::from_arg_matches_mut(&mut Self::command().get_matches_from(s.split_whitespace())) - } -} - -impl Pwd for Cmd { - fn set_pwd(&mut self, pwd: &Path) { - self.config.set_pwd(pwd); - } -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("parsing argument {arg}: {error}")] - CannotParseArg { - arg: String, - error: soroban_spec_tools::Error, - }, - #[error("cannot add contract to ledger entries: {0}")] - CannotAddContractToLedgerEntries(XdrError), - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), - #[error("reading file {0:?}: {1}")] - CannotReadContractFile(PathBuf, io::Error), - #[error("committing file {filepath}: {error}")] - CannotCommitEventsFile { - filepath: std::path::PathBuf, - error: events::Error, - }, - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), - #[error("function {0} was not found in the contract")] - FunctionNotFoundInContractSpec(String), - #[error("parsing contract spec: {0}")] - CannotParseContractSpec(FromWasmError), - // }, - #[error("function name {0} is too long")] - FunctionNameTooLong(String), - #[error("argument count ({current}) surpasses maximum allowed count ({maximum})")] - MaxNumberOfArgumentsReached { current: usize, maximum: usize }, - #[error("cannot print result {result:?}: {error}")] - CannotPrintResult { - result: ScVal, - error: soroban_spec_tools::Error, - }, - #[error(transparent)] - Xdr(#[from] XdrError), - #[error("error parsing int: {0}")] - ParseIntError(#[from] ParseIntError), - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error("unexpected contract code data type: {0:?}")] - UnexpectedContractCodeDataType(LedgerEntryData), - #[error("missing operation result")] - MissingOperationResult, - #[error("missing result")] - MissingResult, - #[error(transparent)] - StrVal(#[from] soroban_spec_tools::Error), - #[error("error loading signing key: {0}")] - SignatureError(#[from] ed25519_dalek::SignatureError), - #[error(transparent)] - Config(#[from] config::Error), - #[error("unexpected ({length}) simulate transaction result length")] - UnexpectedSimulateTransactionResultSize { length: usize }, - #[error("Missing argument {0}")] - MissingArgument(String), - #[error(transparent)] - Clap(#[from] clap::Error), - #[error(transparent)] - Locator(#[from] locator::Error), - #[error("Contract Error\n{0}: {1}")] - ContractInvoke(String, String), - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), - #[error(transparent)] - ContractSpec(#[from] contract_spec::Error), - #[error("")] - MissingFileArg(PathBuf), -} - -impl From for Error { - fn from(_: Infallible) -> Self { - unreachable!() - } -} - -impl Cmd { - fn build_host_function_parameters( - &self, - contract_id: [u8; 32], - spec_entries: &[ScSpecEntry], - ) -> Result<(String, Spec, InvokeContractArgs, Vec), Error> { - let spec = Spec(Some(spec_entries.to_vec())); - let mut cmd = clap::Command::new(self.contract_id.clone()) - .no_binary_name(true) - .term_width(300) - .max_term_width(300); - - for ScSpecFunctionV0 { name, .. } in spec.find_functions()? { - cmd = cmd.subcommand(build_custom_cmd(&name.to_utf8_string_lossy(), &spec)?); - } - cmd.build(); - let long_help = cmd.render_long_help(); - let mut matches_ = cmd.get_matches_from(&self.slop); - let Some((function, matches_)) = &matches_.remove_subcommand() else { - println!("{long_help}"); - std::process::exit(1); - }; - - let func = spec.find_function(function)?; - // create parsed_args in same order as the inputs to func - let mut signers: Vec = vec![]; - let parsed_args = func - .inputs - .iter() - .map(|i| { - let name = i.name.to_utf8_string()?; - if let Some(mut val) = matches_.get_raw(&name) { - let mut s = val.next().unwrap().to_string_lossy().to_string(); - if matches!(i.type_, ScSpecTypeDef::Address) { - let cmd = crate::commands::keys::address::Cmd { - name: s.clone(), - hd_path: Some(0), - locator: self.config.locator.clone(), - }; - if let Ok(address) = cmd.public_key() { - s = address.to_string(); - } - if let Ok(key) = cmd.private_key() { - signers.push(key); - } - } - spec.from_string(&s, &i.type_) - .map_err(|error| Error::CannotParseArg { arg: name, error }) - } else if matches!(i.type_, ScSpecTypeDef::Option(_)) { - Ok(ScVal::Void) - } else if let Some(arg_path) = - matches_.get_one::(&fmt_arg_file_name(&name)) - { - if matches!(i.type_, ScSpecTypeDef::Bytes | ScSpecTypeDef::BytesN(_)) { - Ok(ScVal::try_from( - &std::fs::read(arg_path) - .map_err(|_| Error::MissingFileArg(arg_path.clone()))?, - ) - .map_err(|()| Error::CannotParseArg { - arg: name.clone(), - error: soroban_spec_tools::Error::Unknown, - })?) - } else { - let file_contents = std::fs::read_to_string(arg_path) - .map_err(|_| Error::MissingFileArg(arg_path.clone()))?; - tracing::debug!( - "file {arg_path:?}, has contents:\n{file_contents}\nAnd type {:#?}\n{}", - i.type_, - file_contents.len() - ); - spec.from_string(&file_contents, &i.type_) - .map_err(|error| Error::CannotParseArg { arg: name, error }) - } - } else { - Err(Error::MissingArgument(name)) - } - }) - .collect::, Error>>()?; - - let contract_address_arg = ScAddress::Contract(Hash(contract_id)); - let function_symbol_arg = function - .try_into() - .map_err(|()| Error::FunctionNameTooLong(function.clone()))?; - - let final_args = - parsed_args - .clone() - .try_into() - .map_err(|_| Error::MaxNumberOfArgumentsReached { - current: parsed_args.len(), - maximum: ScVec::default().max_len(), - })?; - - let invoke_args = InvokeContractArgs { - contract_address: contract_address_arg, - function_name: function_symbol_arg, - args: final_args, - }; - - Ok((function.clone(), spec, invoke_args, signers)) - } - - pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { - let res = self.invoke(global_args).await?; - println!("{res}"); - Ok(()) - } - - pub async fn invoke(&self, global_args: &global::Args) -> Result { - self.run_against_rpc_server(global_args).await - } - - pub async fn run_against_rpc_server( - &self, - global_args: &global::Args, - ) -> Result { - let network = self.config.get_network()?; - tracing::trace!(?network); - let contract_id = self.contract_id()?; - let spec_entries = self.spec_entries()?; - if let Some(spec_entries) = &spec_entries { - // For testing wasm arg parsing - let _ = self.build_host_function_parameters(contract_id, spec_entries)?; - } - let client = Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - let key = self.config.key_pair()?; - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - - // Get the contract - let spec_entries = client.get_remote_contract_spec(&contract_id).await?; - - // Get the ledger footprint - let (function, spec, host_function_params, signers) = - self.build_host_function_parameters(contract_id, &spec_entries)?; - let tx = build_invoke_contract_tx( - host_function_params.clone(), - sequence + 1, - self.fee.fee, - &key, - )?; - - let (result, meta, events) = client - .prepare_and_send_transaction( - &tx, - &key, - &signers, - &network.network_passphrase, - Some(log_events), - (global_args.verbose || global_args.very_verbose || self.cost) - .then_some(log_resources), - ) - .await?; - - tracing::debug!(?result); - crate::log::diagnostic_events(&events, tracing::Level::INFO); - let xdr::TransactionMeta::V3(xdr::TransactionMetaV3 { - soroban_meta: Some(xdr::SorobanTransactionMeta { return_value, .. }), - .. - }) = meta - else { - return Err(Error::MissingOperationResult); - }; - - output_to_string(&spec, &return_value, &function) - } - - pub fn read_wasm(&self) -> Result>, Error> { - Ok(if let Some(wasm) = self.wasm.as_ref() { - Some(fs::read(wasm).map_err(|e| Error::CannotReadContractFile(wasm.clone(), e))?) - } else { - None - }) - } - - pub fn spec_entries(&self) -> Result>, Error> { - self.read_wasm()? - .map(|wasm| { - soroban_spec::read::from_wasm(&wasm).map_err(Error::CannotParseContractSpec) - }) - .transpose() - } -} - -impl Cmd { - fn contract_id(&self) -> Result<[u8; 32], Error> { - utils::contract_id_from_str(&self.contract_id) - .map_err(|e| Error::CannotParseContractId(self.contract_id.clone(), e)) - } -} - -fn log_events( - footprint: &LedgerFootprint, - auth: &[VecM], - events: &[xdr::DiagnosticEvent], -) { - crate::log::auth(auth); - crate::log::diagnostic_events(events, tracing::Level::TRACE); - crate::log::footprint(footprint); -} - -fn log_resources(resources: &SorobanResources) { - crate::log::cost(resources); -} - -pub fn output_to_string(spec: &Spec, res: &ScVal, function: &str) -> Result { - let mut res_str = String::new(); - if let Some(output) = spec.find_function(function)?.outputs.first() { - res_str = spec - .xdr_to_json(res, output) - .map_err(|e| Error::CannotPrintResult { - result: res.clone(), - error: e, - })? - .to_string(); - } - Ok(res_str) -} - -fn build_invoke_contract_tx( - parameters: InvokeContractArgs, - sequence: i64, - fee: u32, - key: &SigningKey, -) -> Result { - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::InvokeContract(parameters), - auth: VecM::default(), - }), - }; - Ok(Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee, - seq_num: SequenceNumber(sequence), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![op].try_into()?, - ext: TransactionExt::V0, - }) -} - -fn build_custom_cmd(name: &str, spec: &Spec) -> Result { - let func = spec - .find_function(name) - .map_err(|_| Error::FunctionNotFoundInContractSpec(name.to_string()))?; - - // Parse the function arguments - let inputs_map = &func - .inputs - .iter() - .map(|i| (i.name.to_utf8_string().unwrap(), i.type_.clone())) - .collect::>(); - let name: &'static str = Box::leak(name.to_string().into_boxed_str()); - let mut cmd = clap::Command::new(name) - .no_binary_name(true) - .term_width(300) - .max_term_width(300); - let kebab_name = name.to_kebab_case(); - if kebab_name != name { - cmd = cmd.alias(kebab_name); - } - let doc: &'static str = Box::leak(func.doc.to_utf8_string_lossy().into_boxed_str()); - let long_doc: &'static str = Box::leak(arg_file_help(doc).into_boxed_str()); - - cmd = cmd.about(Some(doc)).long_about(long_doc); - for (name, type_) in inputs_map { - let mut arg = clap::Arg::new(name); - let file_arg_name = fmt_arg_file_name(name); - let mut file_arg = clap::Arg::new(&file_arg_name); - arg = arg - .long(name) - .alias(name.to_kebab_case()) - .num_args(1) - .value_parser(clap::builder::NonEmptyStringValueParser::new()) - .long_help(spec.doc(name, type_)?); - - file_arg = file_arg - .long(&file_arg_name) - .alias(file_arg_name.to_kebab_case()) - .num_args(1) - .hide(true) - .value_parser(value_parser!(PathBuf)) - .conflicts_with(name); - - if let Some(value_name) = spec.arg_value_name(type_, 0) { - let value_name: &'static str = Box::leak(value_name.into_boxed_str()); - arg = arg.value_name(value_name); - } - - // Set up special-case arg rules - arg = match type_ { - xdr::ScSpecTypeDef::Bool => arg - .num_args(0..1) - .default_missing_value("true") - .default_value("false") - .num_args(0..=1), - xdr::ScSpecTypeDef::Option(_val) => arg.required(false), - xdr::ScSpecTypeDef::I256 - | xdr::ScSpecTypeDef::I128 - | xdr::ScSpecTypeDef::I64 - | xdr::ScSpecTypeDef::I32 => arg.allow_hyphen_values(true), - _ => arg, - }; - - cmd = cmd.arg(arg); - cmd = cmd.arg(file_arg); - } - Ok(cmd) -} - -fn fmt_arg_file_name(name: &str) -> String { - format!("{name}-file-path") -} - -fn arg_file_help(docs: &str) -> String { - format!( - r#"{docs} -Usage Notes: -Each arg has a corresponding ---file-path which is a path to a file containing the corresponding JSON argument. -Note: The only types which aren't JSON are Bytes and Bytes which are raw bytes"# - ) -} diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs deleted file mode 100644 index 35be97a7..00000000 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ /dev/null @@ -1,158 +0,0 @@ -pub mod asset; -pub mod bindings; -pub mod build; -pub mod deploy; -pub mod extend; -pub mod fetch; -pub mod id; -pub mod inspect; -pub mod install; -pub mod invoke; -pub mod optimize; -pub mod read; -pub mod restore; - -use crate::commands::global; - -#[derive(Debug, clap::Subcommand)] -pub enum Cmd { - /// Utilities to deploy a Stellar Asset Contract or get its id - #[command(subcommand)] - Asset(asset::Cmd), - /// Generate code client bindings for a contract - #[command(subcommand)] - Bindings(bindings::Cmd), - - Build(build::Cmd), - - /// Extend the time to live ledger of a contract-data ledger entry. - /// - /// If no keys are specified the contract itself is extended. - Extend(extend::Cmd), - - /// Deploy a wasm contract - Deploy(deploy::wasm::Cmd), - - /// Fetch a contract's Wasm binary - Fetch(fetch::Cmd), - - /// Generate the contract id for a given contract or asset - #[command(subcommand)] - Id(id::Cmd), - - /// Inspect a WASM file listing contract functions, meta, etc - Inspect(inspect::Cmd), - - /// Install a WASM file to the ledger without creating a contract instance - Install(install::Cmd), - - /// Invoke a contract function - /// - /// Generates an "implicit CLI" for the specified contract on-the-fly using the contract's - /// schema, which gets embedded into every Soroban contract. The "slop" in this command, - /// everything after the `--`, gets passed to this implicit CLI. Get in-depth help for a given - /// contract: - /// - /// soroban contract invoke ... -- --help - Invoke(invoke::Cmd), - - /// Optimize a WASM file - Optimize(optimize::Cmd), - - /// Print the current value of a contract-data ledger entry - Read(read::Cmd), - - /// Restore an evicted value for a contract-data legder entry. - /// - /// If no keys are specificed the contract itself is restored. - Restore(restore::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Asset(#[from] asset::Error), - - #[error(transparent)] - Bindings(#[from] bindings::Error), - - #[error(transparent)] - Build(#[from] build::Error), - - #[error(transparent)] - Extend(#[from] extend::Error), - - #[error(transparent)] - Deploy(#[from] deploy::wasm::Error), - - #[error(transparent)] - Fetch(#[from] fetch::Error), - #[error(transparent)] - Id(#[from] id::Error), - - #[error(transparent)] - Inspect(#[from] inspect::Error), - - #[error(transparent)] - Install(#[from] install::Error), - - #[error(transparent)] - Invoke(#[from] invoke::Error), - - #[error(transparent)] - Optimize(#[from] optimize::Error), - - #[error(transparent)] - Read(#[from] read::Error), - - #[error(transparent)] - Restore(#[from] restore::Error), -} - -impl Cmd { - pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { - match &self { - Cmd::Asset(asset) => asset.run().await?, - Cmd::Bindings(bindings) => bindings.run().await?, - Cmd::Build(build) => build.run()?, - Cmd::Extend(extend) => extend.run().await?, - Cmd::Deploy(deploy) => deploy.run().await?, - Cmd::Id(id) => id.run()?, - Cmd::Inspect(inspect) => inspect.run()?, - Cmd::Install(install) => install.run().await?, - Cmd::Invoke(invoke) => invoke.run(global_args).await?, - Cmd::Optimize(optimize) => optimize.run()?, - Cmd::Fetch(fetch) => fetch.run().await?, - Cmd::Read(read) => read.run().await?, - Cmd::Restore(restore) => restore.run().await?, - } - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)] -pub enum Durability { - /// Persistent - Persistent, - /// Temporary - Temporary, -} - -impl From<&Durability> for soroban_env_host::xdr::ContractDataDurability { - fn from(d: &Durability) -> Self { - match d { - Durability::Persistent => soroban_env_host::xdr::ContractDataDurability::Persistent, - Durability::Temporary => soroban_env_host::xdr::ContractDataDurability::Temporary, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)] -pub enum SpecOutput { - /// XDR of array of contract spec entries - XdrBase64, - /// Array of xdr of contract spec entries - XdrBase64Array, - /// Pretty print of contract spec entries - Docs, -} diff --git a/cmd/soroban-cli/src/commands/contract/optimize.rs b/cmd/soroban-cli/src/commands/contract/optimize.rs deleted file mode 100644 index 751dabb1..00000000 --- a/cmd/soroban-cli/src/commands/contract/optimize.rs +++ /dev/null @@ -1,80 +0,0 @@ -use clap::{arg, command, Parser}; -use std::fmt::Debug; -#[cfg(feature = "opt")] -use wasm_opt::{Feature, OptimizationError, OptimizationOptions}; - -use crate::wasm; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - wasm: wasm::Args, - /// Path to write the optimized WASM file to (defaults to same location as --wasm with .optimized.wasm suffix) - #[arg(long)] - wasm_out: Option, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[cfg(feature = "opt")] - #[error("optimization error: {0}")] - OptimizationError(OptimizationError), - #[cfg(not(feature = "opt"))] - #[error("Must install with \"opt\" feature, e.g. `cargo install soroban-cli --features opt")] - Install, -} - -impl Cmd { - #[cfg(not(feature = "opt"))] - pub fn run(&self) -> Result<(), Error> { - Err(Error::Install) - } - - #[cfg(feature = "opt")] - pub fn run(&self) -> Result<(), Error> { - let wasm_size = self.wasm.len()?; - - println!( - "Reading: {} ({} bytes)", - self.wasm.wasm.to_string_lossy(), - wasm_size - ); - - let wasm_out = self.wasm_out.as_ref().cloned().unwrap_or_else(|| { - let mut wasm_out = self.wasm.wasm.clone(); - wasm_out.set_extension("optimized.wasm"); - wasm_out - }); - println!("Writing to: {}...", wasm_out.to_string_lossy()); - - let mut options = OptimizationOptions::new_optimize_for_size_aggressively(); - options.converge = true; - - // Explicitly set to MVP + sign-ext + mutable-globals, which happens to - // also be the default featureset, but just to be extra clear we set it - // explicitly. - // - // Formerly Soroban supported only the MVP feature set, but Rust 1.70 as - // well as Clang generate code with sign-ext + mutable-globals enabled, - // so Soroban has taken a change to support them also. - options.mvp_features_only(); - options.enable_feature(Feature::MutableGlobals); - options.enable_feature(Feature::SignExt); - - options - .run(&self.wasm.wasm, &wasm_out) - .map_err(Error::OptimizationError)?; - - let wasm_out_size = wasm::len(&wasm_out)?; - println!( - "Optimized: {} ({} bytes)", - wasm_out.to_string_lossy(), - wasm_out_size - ); - - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs deleted file mode 100644 index 842832d5..00000000 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ /dev/null @@ -1,180 +0,0 @@ -use std::{ - fmt::Debug, - io::{self, stdout}, -}; - -use clap::{command, Parser, ValueEnum}; -use soroban_env_host::{ - xdr::{ - ContractDataEntry, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractData, - ScVal, WriteXdr, - }, - HostError, -}; -use soroban_sdk::xdr::Limits; - -use crate::{ - commands::config, - key, - rpc::{self, Client, FullLedgerEntries, FullLedgerEntry}, -}; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// Type of output to generate - #[arg(long, value_enum, default_value("string"))] - pub output: Output, - #[command(flatten)] - pub key: key::Args, - #[command(flatten)] - config: config::Args, -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] -pub enum Output { - /// String - String, - /// Json - Json, - /// XDR - Xdr, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("parsing key {key}: {error}")] - CannotParseKey { - key: String, - error: soroban_spec_tools::Error, - }, - #[error("parsing XDR key {key}: {error}")] - CannotParseXdrKey { key: String, error: XdrError }, - #[error("cannot parse contract ID {contract_id}: {error}")] - CannotParseContractId { - contract_id: String, - error: stellar_strkey::DecodeError, - }, - #[error("cannot print result {result:?}: {error}")] - CannotPrintResult { - result: ScVal, - error: soroban_spec_tools::Error, - }, - #[error("cannot print result {result:?}: {error}")] - CannotPrintJsonResult { - result: ScVal, - error: serde_json::Error, - }, - #[error("cannot print as csv: {error}")] - CannotPrintAsCsv { error: csv::Error }, - #[error("cannot print: {error}")] - CannotPrintFlush { error: io::Error }, - #[error(transparent)] - Config(#[from] config::Error), - #[error("either `--key` or `--key-xdr` are required when querying a network")] - KeyIsRequired, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Xdr(#[from] XdrError), - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), - #[error("no matching contract data entries were found for the specified contract id")] - NoContractDataEntryFoundForContractID, - #[error(transparent)] - Key(#[from] key::Error), - #[error("Only contract data and code keys are allowed")] - OnlyDataAllowed, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let entries = self.run_against_rpc_server().await?; - self.output_entries(&entries) - } - - async fn run_against_rpc_server(&self) -> Result { - let network = self.config.get_network()?; - tracing::trace!(?network); - let network = &self.config.get_network()?; - let client = Client::new(&network.rpc_url)?; - let keys = self.key.parse_keys()?; - Ok(client.get_full_ledger_entries(&keys).await?) - } - - fn output_entries(&self, entries: &FullLedgerEntries) -> Result<(), Error> { - if entries.entries.is_empty() { - return Err(Error::NoContractDataEntryFoundForContractID); - } - tracing::trace!("{entries:#?}"); - let mut out = csv::Writer::from_writer(stdout()); - for FullLedgerEntry { - key, - val, - live_until_ledger_seq, - last_modified_ledger, - } in &entries.entries - { - let ( - LedgerKey::ContractData(LedgerKeyContractData { key, .. }), - LedgerEntryData::ContractData(ContractDataEntry { val, .. }), - ) = (key, val) - else { - return Err(Error::OnlyDataAllowed); - }; - let output = match self.output { - Output::String => [ - soroban_spec_tools::to_string(key).map_err(|e| Error::CannotPrintResult { - result: key.clone(), - error: e, - })?, - soroban_spec_tools::to_string(val).map_err(|e| Error::CannotPrintResult { - result: val.clone(), - error: e, - })?, - last_modified_ledger.to_string(), - live_until_ledger_seq.to_string(), - ], - Output::Json => [ - serde_json::to_string_pretty(&key).map_err(|error| { - Error::CannotPrintJsonResult { - result: key.clone(), - error, - } - })?, - serde_json::to_string_pretty(&val).map_err(|error| { - Error::CannotPrintJsonResult { - result: val.clone(), - error, - } - })?, - serde_json::to_string_pretty(&last_modified_ledger).map_err(|error| { - Error::CannotPrintJsonResult { - result: val.clone(), - error, - } - })?, - serde_json::to_string_pretty(&live_until_ledger_seq).map_err(|error| { - Error::CannotPrintJsonResult { - result: val.clone(), - error, - } - })?, - ], - Output::Xdr => [ - key.to_xdr_base64(Limits::none())?, - val.to_xdr_base64(Limits::none())?, - last_modified_ledger.to_xdr_base64(Limits::none())?, - live_until_ledger_seq.to_xdr_base64(Limits::none())?, - ], - }; - out.write_record(output) - .map_err(|e| Error::CannotPrintAsCsv { error: e })?; - } - out.flush() - .map_err(|e| Error::CannotPrintFlush { error: e })?; - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs deleted file mode 100644 index 38b8a84a..00000000 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::{fmt::Debug, path::Path, str::FromStr}; - -use clap::{command, Parser}; -use soroban_env_host::xdr::{ - Error as XdrError, ExtensionPoint, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, Memo, MuxedAccount, Operation, OperationBody, OperationMeta, Preconditions, - RestoreFootprintOp, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, - TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, Uint256, -}; -use stellar_strkey::DecodeError; - -use crate::{ - commands::{ - config::{self, locator}, - contract::extend, - }, - key, - rpc::{self, Client}, - wasm, Pwd, -}; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - pub key: key::Args, - /// Number of ledgers to extend the entry - #[arg(long)] - pub ledgers_to_extend: Option, - /// Only print the new Time To Live ledger - #[arg(long)] - pub ttl_ledger_only: bool, - #[command(flatten)] - pub config: config::Args, - #[command(flatten)] - pub fee: crate::fee::Args, -} - -impl FromStr for Cmd { - type Err = clap::error::Error; - - fn from_str(s: &str) -> Result { - use clap::{CommandFactory, FromArgMatches}; - Self::from_arg_matches_mut(&mut Self::command().get_matches_from(s.split_whitespace())) - } -} - -impl Pwd for Cmd { - fn set_pwd(&mut self, pwd: &Path) { - self.config.set_pwd(pwd); - } -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("parsing key {key}: {error}")] - CannotParseKey { - key: String, - error: soroban_spec_tools::Error, - }, - #[error("parsing XDR key {key}: {error}")] - CannotParseXdrKey { key: String, error: XdrError }, - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), - #[error(transparent)] - Config(#[from] config::Error), - #[error("either `--key` or `--key-xdr` are required")] - KeyIsRequired, - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error("Ledger entry not found")] - LedgerEntryNotFound, - #[error(transparent)] - Locator(#[from] locator::Error), - #[error("missing operation result")] - MissingOperationResult, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Wasm(#[from] wasm::Error), - #[error(transparent)] - Key(#[from] key::Error), - #[error(transparent)] - Extend(#[from] extend::Error), -} - -impl Cmd { - #[allow(clippy::too_many_lines)] - pub async fn run(&self) -> Result<(), Error> { - let expiration_ledger_seq = self.run_against_rpc_server().await?; - - if let Some(ledgers_to_extend) = self.ledgers_to_extend { - extend::Cmd { - key: self.key.clone(), - ledgers_to_extend, - config: self.config.clone(), - fee: self.fee.clone(), - ttl_ledger_only: false, - } - .run() - .await?; - } else { - println!("New ttl ledger: {expiration_ledger_seq}"); - } - - Ok(()) - } - - pub async fn run_against_rpc_server(&self) -> Result { - let network = self.config.get_network()?; - tracing::trace!(?network); - let entry_keys = self.key.parse_keys()?; - let network = &self.config.get_network()?; - let client = Client::new(&network.rpc_url)?; - let key = self.config.key_pair()?; - - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; - let sequence: i64 = account_details.seq_num.into(); - - let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), - fee: self.fee.fee, - seq_num: SequenceNumber(sequence + 1), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![Operation { - source_account: None, - body: OperationBody::RestoreFootprint(RestoreFootprintOp { - ext: ExtensionPoint::V0, - }), - }] - .try_into()?, - ext: TransactionExt::V1(SorobanTransactionData { - ext: ExtensionPoint::V0, - resources: SorobanResources { - footprint: LedgerFootprint { - read_only: vec![].try_into()?, - read_write: entry_keys.try_into()?, - }, - instructions: 0, - read_bytes: 0, - write_bytes: 0, - }, - resource_fee: 0, - }), - }; - - let (result, meta, events) = client - .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) - .await?; - - tracing::trace!(?result); - tracing::trace!(?meta); - if !events.is_empty() { - tracing::info!("Events:\n {events:#?}"); - } - - // The transaction from core will succeed regardless of whether it actually found & - // restored the entry, so we have to inspect the result meta to tell if it worked or not. - let TransactionMeta::V3(TransactionMetaV3 { operations, .. }) = meta else { - return Err(Error::LedgerEntryNotFound); - }; - tracing::debug!("Operations:\nlen:{}\n{operations:#?}", operations.len()); - - // Simply check if there is exactly one entry here. We only support extending a single - // entry via this command (which we should fix separately, but). - if operations.len() == 0 { - return Err(Error::LedgerEntryNotFound); - } - - if operations.len() != 1 { - tracing::warn!( - "Unexpected number of operations: {}. Currently only handle one.", - operations[0].changes.len() - ); - } - parse_operations(&operations).ok_or(Error::MissingOperationResult) - } -} - -fn parse_operations(ops: &[OperationMeta]) -> Option { - ops.first().and_then(|op| { - op.changes.iter().find_map(|entry| match entry { - LedgerEntryChange::Updated(LedgerEntry { - data: - LedgerEntryData::Ttl(TtlEntry { - live_until_ledger_seq, - .. - }), - .. - }) - | LedgerEntryChange::Created(LedgerEntry { - data: - LedgerEntryData::Ttl(TtlEntry { - live_until_ledger_seq, - .. - }), - .. - }) => Some(*live_until_ledger_seq), - _ => None, - }) - }) -} diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs deleted file mode 100644 index aa46bbe2..00000000 --- a/cmd/soroban-cli/src/commands/events.rs +++ /dev/null @@ -1,221 +0,0 @@ -use clap::{arg, command, Parser}; -use std::io; - -use soroban_env_host::xdr::{self, Limits, ReadXdr}; - -use super::{config::locator, network}; -use crate::{rpc, utils}; - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd { - /// The first ledger sequence number in the range to pull events - /// https://developers.stellar.org/docs/encyclopedia/ledger-headers#ledger-sequence - #[arg(long, conflicts_with = "cursor", required_unless_present = "cursor")] - start_ledger: Option, - /// The cursor corresponding to the start of the event range. - #[arg( - long, - conflicts_with = "start_ledger", - required_unless_present = "start_ledger" - )] - cursor: Option, - /// Output formatting options for event stream - #[arg(long, value_enum, default_value = "pretty")] - output: OutputFormat, - /// The maximum number of events to display (defer to the server-defined limit). - #[arg(short, long, default_value = "10")] - count: usize, - /// A set of (up to 5) contract IDs to filter events on. This parameter can - /// be passed multiple times, e.g. `--id C123.. --id C456..`, or passed with - /// multiple parameters, e.g. `--id C123 C456`. - /// - /// Though the specification supports multiple filter objects (i.e. - /// combinations of type, IDs, and topics), only one set can be specified on - /// the command-line today, though that set can have multiple IDs/topics. - #[arg( - long = "id", - num_args = 1..=6, - help_heading = "FILTERS" - )] - contract_ids: Vec, - /// A set of (up to 4) topic filters to filter event topics on. A single - /// topic filter can contain 1-4 different segment filters, separated by - /// commas, with an asterisk (* character) indicating a wildcard segment. - /// - /// For example, this is one topic filter with two segments: - /// - /// --topic "AAAABQAAAAdDT1VOVEVSAA==,*" - /// - /// This is two topic filters with one and two segments each: - /// - /// --topic "AAAABQAAAAdDT1VOVEVSAA==" --topic '*,*' - /// - /// Note that all of these topic filters are combined with the contract IDs - /// into a single filter (i.e. combination of type, IDs, and topics). - #[arg( - long = "topic", - num_args = 1..=5, - help_heading = "FILTERS" - )] - topic_filters: Vec, - /// Specifies which type of contract events to display. - #[arg( - long = "type", - value_enum, - default_value = "all", - help_heading = "FILTERS" - )] - event_type: rpc::EventType, - #[command(flatten)] - locator: locator::Args, - #[command(flatten)] - network: network::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("cursor is not valid")] - InvalidCursor, - #[error("filepath does not exist: {path}")] - InvalidFile { path: String }, - #[error("filepath ({path}) cannot be read: {error}")] - CannotReadFile { path: String, error: String }, - #[error("cannot parse topic filter {topic} into 1-4 segments")] - InvalidTopicFilter { topic: String }, - #[error("invalid segment ({segment}) in topic filter ({topic}): {error}")] - InvalidSegment { - topic: String, - segment: String, - error: xdr::Error, - }, - #[error("cannot parse contract ID {contract_id}: {error}")] - InvalidContractId { - contract_id: String, - error: stellar_strkey::DecodeError, - }, - #[error("invalid JSON string: {error} ({debug})")] - InvalidJson { - debug: String, - error: serde_json::Error, - }, - #[error("invalid timestamp in event: {ts}")] - InvalidTimestamp { ts: String }, - #[error("missing start_ledger and cursor")] - MissingStartLedgerAndCursor, - #[error("missing target")] - MissingTarget, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Generic(#[from] Box), - #[error(transparent)] - Io(#[from] io::Error), - #[error(transparent)] - Xdr(#[from] xdr::Error), - #[error(transparent)] - Serde(#[from] serde_json::Error), - #[error(transparent)] - Network(#[from] network::Error), - #[error(transparent)] - Locator(#[from] locator::Error), -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)] -pub enum OutputFormat { - /// Colorful, human-oriented console output - Pretty, - /// Human-oriented console output without colors - Plain, - /// JSONified console output - Json, -} - -impl Cmd { - pub async fn run(&mut self) -> Result<(), Error> { - // Validate that topics are made up of segments. - for topic in &self.topic_filters { - for (i, segment) in topic.split(',').enumerate() { - if i > 4 { - return Err(Error::InvalidTopicFilter { - topic: topic.to_string(), - }); - } - - if segment != "*" { - if let Err(e) = xdr::ScVal::from_xdr_base64(segment, Limits::none()) { - return Err(Error::InvalidSegment { - topic: topic.to_string(), - segment: segment.to_string(), - error: e, - }); - } - } - } - } - - // Validate contract_ids - for id in &mut self.contract_ids { - utils::contract_id_from_str(id).map_err(|e| Error::InvalidContractId { - contract_id: id.clone(), - error: e, - })?; - } - - let response = self.run_against_rpc_server().await?; - - for event in &response.events { - match self.output { - // Should we pretty-print the JSON like we're doing here or just - // dump an event in raw JSON on each line? The latter is easier - // to consume programmatically. - OutputFormat::Json => { - println!( - "{}", - serde_json::to_string_pretty(&event).map_err(|e| { - Error::InvalidJson { - debug: format!("{event:#?}"), - error: e, - } - })?, - ); - } - OutputFormat::Plain => println!("{event}"), - OutputFormat::Pretty => event.pretty_print()?, - } - } - println!("Latest Ledger: {}", response.latest_ledger); - - Ok(()) - } - - async fn run_against_rpc_server(&self) -> Result { - let start = self.start()?; - let network = self.network.get(&self.locator)?; - - let client = rpc::Client::new(&network.rpc_url)?; - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - client - .get_events( - start, - Some(self.event_type), - &self.contract_ids, - &self.topic_filters, - Some(self.count), - ) - .await - .map_err(Error::Rpc) - } - - fn start(&self) -> Result { - let start = match (self.start_ledger, self.cursor.clone()) { - (Some(start), _) => rpc::EventStart::Ledger(start), - (_, Some(c)) => rpc::EventStart::Cursor(c), - // should never happen because of required_unless_present flags - _ => return Err(Error::MissingStartLedgerAndCursor), - }; - Ok(start) - } -} diff --git a/cmd/soroban-cli/src/commands/global.rs b/cmd/soroban-cli/src/commands/global.rs deleted file mode 100644 index c606bd1b..00000000 --- a/cmd/soroban-cli/src/commands/global.rs +++ /dev/null @@ -1,61 +0,0 @@ -use clap::arg; -use std::path::PathBuf; - -use super::config; - -#[derive(Debug, clap::Args, Clone, Default)] -#[group(skip)] -#[allow(clippy::struct_excessive_bools)] -pub struct Args { - #[clap(flatten)] - pub locator: config::locator::Args, - - /// Filter logs output. To turn on "soroban_cli::log::footprint=debug" or off "=off". Can also use env var `RUST_LOG`. - #[arg(long, short = 'f')] - pub filter_logs: Vec, - - /// Do not write logs to stderr including `INFO` - #[arg(long, short = 'q')] - pub quiet: bool, - - /// Log DEBUG events - #[arg(long, short = 'v')] - pub verbose: bool, - - /// Log DEBUG and TRACE events - #[arg(long, visible_alias = "vv")] - pub very_verbose: bool, - - /// List installed plugins. E.g. `soroban-hello` - #[arg(long)] - pub list: bool, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("reading file {filepath}: {error}")] - CannotReadLedgerFile { - filepath: PathBuf, - error: soroban_ledger_snapshot::Error, - }, - - #[error("committing file {filepath}: {error}")] - CannotCommitLedgerFile { - filepath: PathBuf, - error: soroban_ledger_snapshot::Error, - }, -} - -impl Args { - pub fn log_level(&self) -> Option { - if self.quiet { - None - } else if self.very_verbose { - Some(tracing::Level::TRACE) - } else if self.verbose { - Some(tracing::Level::DEBUG) - } else { - Some(tracing::Level::INFO) - } - } -} diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs deleted file mode 100644 index 2868c737..00000000 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::command; - -use super::super::config::{locator, secret}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - Config(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Name of identity - pub name: String, - - #[command(flatten)] - pub secrets: secret::Args, - - #[command(flatten)] - pub config_locator: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - Ok(self - .config_locator - .write_identity(&self.name, &self.secrets.read_secret()?)?) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs deleted file mode 100644 index d13381b4..00000000 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::commands::config::secret; - -use super::super::config::locator; -use clap::arg; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), - - #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Name of identity to lookup, default test identity used if not provided - pub name: String, - - /// If identity is a seed phrase use this hd path, default is 0 - #[arg(long)] - pub hd_path: Option, - - #[command(flatten)] - pub locator: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - println!("{}", self.public_key()?); - Ok(()) - } - - pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .key_pair(self.hd_path)?) - } - - pub fn public_key(&self) -> Result { - if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { - Ok(key) - } else { - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - self.private_key()?.verifying_key().as_bytes(), - )?) - } - } -} diff --git a/cmd/soroban-cli/src/commands/keys/fund.rs b/cmd/soroban-cli/src/commands/keys/fund.rs deleted file mode 100644 index b6c088f1..00000000 --- a/cmd/soroban-cli/src/commands/keys/fund.rs +++ /dev/null @@ -1,34 +0,0 @@ -use clap::command; - -use crate::commands::network; - -use super::address; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Address(#[from] address::Error), - #[error(transparent)] - Network(#[from] network::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - pub network: network::Args, - /// Address to fund - #[command(flatten)] - pub address: address::Cmd, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let addr = self.address.public_key()?; - self.network - .get(&self.address.locator)? - .fund_address(&addr) - .await?; - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs deleted file mode 100644 index 07782b21..00000000 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ /dev/null @@ -1,75 +0,0 @@ -use clap::{arg, command}; - -use crate::commands::network; - -use super::super::config::{ - locator, - secret::{self, Secret}, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), - #[error(transparent)] - Secret(#[from] secret::Error), - #[error(transparent)] - Network(#[from] network::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Name of identity - pub name: String, - /// Do not fund address - #[arg(long)] - pub no_fund: bool, - /// Optional seed to use when generating seed phrase. - /// Random otherwise. - #[arg(long, conflicts_with = "default_seed")] - pub seed: Option, - - /// Output the generated identity as a secret key - #[arg(long, short = 's')] - pub as_secret: bool, - - #[command(flatten)] - pub config_locator: locator::Args, - - /// When generating a secret key, which hd_path should be used from the original seed_phrase. - #[arg(long)] - pub hd_path: Option, - - /// Generate the default seed phrase. Useful for testing. - /// Equivalent to --seed 0000000000000000 - #[arg(long, short = 'd', conflicts_with = "seed")] - pub default_seed: bool, - - #[command(flatten)] - pub network: network::Args, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let seed_phrase = if self.default_seed { - Secret::test_seed_phrase() - } else { - Secret::from_seed(self.seed.as_deref()) - }?; - let secret = if self.as_secret { - seed_phrase.private_key(self.hd_path)?.into() - } else { - seed_phrase - }; - self.config_locator.write_identity(&self.name, &secret)?; - if !self.no_fund { - let addr = secret.public_key(self.hd_path)?; - let network = self.network.get(&self.config_locator)?; - network.fund_address(&addr).await.unwrap_or_else(|_| { - tracing::warn!("Failed to fund address: {addr} on at {}", network.rpc_url); - }); - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/ls.rs b/cmd/soroban-cli/src/commands/keys/ls.rs deleted file mode 100644 index bc46ffcd..00000000 --- a/cmd/soroban-cli/src/commands/keys/ls.rs +++ /dev/null @@ -1,45 +0,0 @@ -use clap::command; - -use super::super::config::locator; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - pub config_locator: locator::Args, - - #[arg(long, short = 'l')] - pub long: bool, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let res = if self.long { self.ls_l() } else { self.ls() }?.join("\n"); - println!("{res}"); - Ok(()) - } - - pub fn ls(&self) -> Result, Error> { - let mut list = self.config_locator.list_identities()?; - let test = "test".to_string(); - if !list.contains(&test) { - list.push(test); - } - Ok(list) - } - - pub fn ls_l(&self) -> Result, Error> { - Ok(self - .config_locator - .list_identities_long()? - .into_iter() - .map(|(name, location)| format!("{location}\nName: {name}\n")) - .collect::>()) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs deleted file mode 100644 index 42814092..00000000 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -use clap::Parser; - -pub mod add; -pub mod address; -pub mod fund; -pub mod generate; -pub mod ls; -pub mod rm; -pub mod show; - -#[derive(Debug, Parser)] -pub enum Cmd { - /// Add a new identity (keypair, ledger, macOS keychain) - Add(add::Cmd), - /// Given an identity return its address (public key) - Address(address::Cmd), - /// Fund an identity on a test network - Fund(fund::Cmd), - /// Generate a new identity with a seed phrase, currently 12 words - Generate(generate::Cmd), - /// List identities - Ls(ls::Cmd), - /// Remove an identity - Rm(rm::Cmd), - /// Given an identity return its private key - Show(show::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Add(#[from] add::Error), - - #[error(transparent)] - Address(#[from] address::Error), - #[error(transparent)] - Fund(#[from] fund::Error), - - #[error(transparent)] - Generate(#[from] generate::Error), - #[error(transparent)] - Rm(#[from] rm::Error), - #[error(transparent)] - Ls(#[from] ls::Error), - - #[error(transparent)] - Show(#[from] show::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match self { - Cmd::Add(cmd) => cmd.run()?, - Cmd::Address(cmd) => cmd.run()?, - Cmd::Fund(cmd) => cmd.run().await?, - Cmd::Generate(cmd) => cmd.run().await?, - Cmd::Ls(cmd) => cmd.run()?, - Cmd::Rm(cmd) => cmd.run()?, - Cmd::Show(cmd) => cmd.run()?, - }; - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/rm.rs b/cmd/soroban-cli/src/commands/keys/rm.rs deleted file mode 100644 index df48108d..00000000 --- a/cmd/soroban-cli/src/commands/keys/rm.rs +++ /dev/null @@ -1,25 +0,0 @@ -use clap::command; - -use super::super::config::locator; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Locator(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Identity to remove - pub name: String, - - #[command(flatten)] - pub config: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - Ok(self.config.remove_identity(&self.name)?) - } -} diff --git a/cmd/soroban-cli/src/commands/keys/show.rs b/cmd/soroban-cli/src/commands/keys/show.rs deleted file mode 100644 index b99478cb..00000000 --- a/cmd/soroban-cli/src/commands/keys/show.rs +++ /dev/null @@ -1,43 +0,0 @@ -use clap::arg; - -use super::super::config::{locator, secret}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), - - #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Name of identity to lookup, default is test identity - pub name: String, - - /// If identity is a seed phrase use this hd path, default is 0 - #[arg(long)] - pub hd_path: Option, - - #[command(flatten)] - pub locator: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - println!("{}", self.private_key()?.to_string()); - Ok(()) - } - - pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .private_key(self.hd_path)?) - } -} diff --git a/cmd/soroban-cli/src/commands/lab/mod.rs b/cmd/soroban-cli/src/commands/lab/mod.rs deleted file mode 100644 index f405efe6..00000000 --- a/cmd/soroban-cli/src/commands/lab/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -use clap::Subcommand; -use stellar_xdr::cli as xdr; - -pub mod token; - -#[derive(Debug, Subcommand)] -pub enum Cmd { - /// Wrap, create, and manage token contracts - Token(token::Root), - - /// Decode xdr - Xdr(xdr::Root), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Token(#[from] token::Error), - #[error(transparent)] - Xdr(#[from] xdr::Error), -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - match &self { - Cmd::Token(token) => token.run().await?, - Cmd::Xdr(xdr) => xdr.run()?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/lab/token/mod.rs b/cmd/soroban-cli/src/commands/lab/token/mod.rs deleted file mode 100644 index bd7eacf3..00000000 --- a/cmd/soroban-cli/src/commands/lab/token/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::fmt::Debug; - -use crate::commands::contract::{deploy, id}; -use clap::{Parser, Subcommand}; - -#[derive(Parser, Debug)] -pub struct Root { - #[clap(subcommand)] - cmd: Cmd, -} - -#[derive(Subcommand, Debug)] -enum Cmd { - /// Deploy a token contract to wrap an existing Stellar classic asset for smart contract usage - /// Deprecated, use `soroban contract deploy asset` instead - Wrap(deploy::asset::Cmd), - /// Compute the expected contract id for the given asset - /// Deprecated, use `soroban contract id asset` instead - Id(id::asset::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Wrap(#[from] deploy::asset::Error), - #[error(transparent)] - Id(#[from] id::asset::Error), -} - -impl Root { - pub async fn run(&self) -> Result<(), Error> { - match &self.cmd { - Cmd::Wrap(wrap) => wrap.run().await?, - Cmd::Id(id) => id.run()?, - } - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs deleted file mode 100644 index 952869af..00000000 --- a/cmd/soroban-cli/src/commands/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::str::FromStr; - -use clap::{command, error::ErrorKind, CommandFactory, FromArgMatches, Parser}; - -pub mod completion; -pub mod config; -pub mod contract; -pub mod events; -pub mod global; -pub mod keys; -pub mod lab; -pub mod network; -pub mod plugin; -pub mod version; - -pub const HEADING_RPC: &str = "Options (RPC)"; -const ABOUT: &str = "Build, deploy, & interact with contracts; set identities to sign with; configure networks; generate keys; and more. - -Intro: https://soroban.stellar.org -CLI Reference: https://github.com/stellar/soroban-tools/tree/main/docs/soroban-cli-full-docs.md"; - -// long_about is shown when someone uses `--help`; short help when using `-h` -const LONG_ABOUT: &str = " - -The easiest way to get started is to generate a new identity: - - soroban config identity generate alice - -You can use identities with the `--source` flag in other commands later. - -Commands that relate to smart contract interactions are organized under the `contract` subcommand. List them: - - soroban contract --help - -A Soroban contract has its interface schema types embedded in the binary that gets deployed on-chain, making it possible to dynamically generate a custom CLI for each. `soroban contract invoke` makes use of this: - - soroban contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- \ - --help - -Anything after the `--` double dash (the \"slop\") is parsed as arguments to the contract-specific CLI, generated on-the-fly from the embedded schema. For the hello world example, with a function called `hello` that takes one string argument `to`, here's how you invoke it: - - soroban contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- \ - hello --to world - -Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soroban-cli-full-docs.md"; - -#[derive(Parser, Debug)] -#[command( - name = "soroban", - about = ABOUT, - version = version::long(), - long_about = ABOUT.to_string() + LONG_ABOUT, - disable_help_subcommand = true, -)] -pub struct Root { - #[clap(flatten)] - pub global_args: global::Args, - - #[command(subcommand)] - pub cmd: Cmd, -} - -impl Root { - pub fn new() -> Result { - Self::try_parse().map_err(|e| { - if std::env::args().any(|s| s == "--list") { - let plugins = plugin::list().unwrap_or_default(); - if plugins.is_empty() { - println!("No Plugins installed. E.g. soroban-hello"); - } else { - println!("Installed Plugins:\n {}", plugins.join("\n ")); - } - std::process::exit(0); - } - match e.kind() { - ErrorKind::InvalidSubcommand => match plugin::run() { - Ok(()) => Error::Clap(e), - Err(e) => Error::Plugin(e), - }, - _ => Error::Clap(e), - } - }) - } - - pub fn from_arg_matches(itr: I) -> Result - where - I: IntoIterator, - T: Into + Clone, - { - Self::from_arg_matches_mut(&mut Self::command().get_matches_from(itr)) - } - pub async fn run(&mut self) -> Result<(), Error> { - match &mut self.cmd { - Cmd::Completion(completion) => completion.run(), - Cmd::Contract(contract) => contract.run(&self.global_args).await?, - Cmd::Events(events) => events.run().await?, - Cmd::Lab(lab) => lab.run().await?, - Cmd::Network(network) => network.run()?, - Cmd::Version(version) => version.run(), - Cmd::Keys(id) => id.run().await?, - Cmd::Config(c) => c.run().await?, - }; - Ok(()) - } -} - -impl FromStr for Root { - type Err = clap::Error; - - fn from_str(s: &str) -> Result { - Self::from_arg_matches(s.split_whitespace()) - } -} - -#[derive(Parser, Debug)] -pub enum Cmd { - /// Print shell completion code for the specified shell. - #[command(long_about = completion::LONG_ABOUT)] - Completion(completion::Cmd), - /// Deprecated, use `soroban keys` and `soroban network` instead - #[command(subcommand)] - Config(config::Cmd), - /// Tools for smart contract developers - #[command(subcommand)] - Contract(contract::Cmd), - /// Watch the network for contract events - Events(events::Cmd), - /// Create and manage identities including keys and addresses - #[command(subcommand)] - Keys(keys::Cmd), - /// Experiment with early features and expert tools - #[command(subcommand)] - Lab(lab::Cmd), - /// Start and configure networks - #[command(subcommand)] - Network(network::Cmd), - /// Print version information - Version(version::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - // TODO: stop using Debug for displaying errors - #[error(transparent)] - Contract(#[from] contract::Error), - #[error(transparent)] - Events(#[from] events::Error), - #[error(transparent)] - Keys(#[from] keys::Error), - #[error(transparent)] - Lab(#[from] lab::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Clap(#[from] clap::error::Error), - #[error(transparent)] - Plugin(#[from] plugin::Error), - #[error(transparent)] - Network(#[from] network::Error), -} diff --git a/cmd/soroban-cli/src/commands/network/add.rs b/cmd/soroban-cli/src/commands/network/add.rs deleted file mode 100644 index b6a2ddd3..00000000 --- a/cmd/soroban-cli/src/commands/network/add.rs +++ /dev/null @@ -1,32 +0,0 @@ -use super::super::config::{locator, secret}; -use clap::command; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - Config(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Name of network - pub name: String, - - #[command(flatten)] - pub network: super::Network, - - #[command(flatten)] - pub config_locator: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - Ok(self - .config_locator - .write_network(&self.name, &self.network)?) - } -} diff --git a/cmd/soroban-cli/src/commands/network/ls.rs b/cmd/soroban-cli/src/commands/network/ls.rs deleted file mode 100644 index cc542b3e..00000000 --- a/cmd/soroban-cli/src/commands/network/ls.rs +++ /dev/null @@ -1,44 +0,0 @@ -use clap::command; - -use super::locator; -use crate::commands::config::locator::Location; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - #[command(flatten)] - pub config_locator: locator::Args, - /// Get more info about the networks - #[arg(long, short = 'l')] - pub long: bool, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - let res = if self.long { self.ls_l() } else { self.ls() }?.join("\n"); - println!("{res}"); - Ok(()) - } - - pub fn ls(&self) -> Result, Error> { - Ok(self.config_locator.list_networks()?) - } - - pub fn ls_l(&self) -> Result, Error> { - Ok(self - .config_locator - .list_networks_long()? - .iter() - .filter_map(|(name, network, location)| { - (!self.config_locator.global || matches!(location, Location::Global(_))) - .then(|| Some(format!("{location}\nName: {name}\n{network:#?}\n")))? - }) - .collect()) - } -} diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs deleted file mode 100644 index 22cba190..00000000 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ /dev/null @@ -1,197 +0,0 @@ -use std::str::FromStr; - -use clap::{arg, Parser}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use stellar_strkey::ed25519::PublicKey; - -use crate::{ - commands::HEADING_RPC, - rpc::{self, Client}, -}; - -use super::config::locator; - -pub mod add; -pub mod ls; -pub mod rm; - -#[derive(Debug, Parser)] -pub enum Cmd { - /// Add a new network - Add(add::Cmd), - /// Remove a network - Rm(rm::Cmd), - /// List networks - Ls(ls::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Add(#[from] add::Error), - - #[error(transparent)] - Rm(#[from] rm::Error), - - #[error(transparent)] - Ls(#[from] ls::Error), - - #[error(transparent)] - Config(#[from] locator::Error), - - #[error("network arg or rpc url and network passphrase are required if using the network")] - Network, - #[error(transparent)] - Rpc(#[from] rpc::Error), - #[error(transparent)] - Hyper(#[from] hyper::Error), - #[error("Failed to parse JSON from {0}, {1}")] - FailedToParseJSON(String, serde_json::Error), - #[error("Invalid URL {0}")] - InvalidUrl(String), - #[error("Inproper response {0}")] - InproperResponse(String), - #[error("Currently not supported on windows. Please visit:\n{0}")] - WindowsNotSupported(String), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - match self { - Cmd::Add(cmd) => cmd.run()?, - Cmd::Rm(new) => new.run()?, - Cmd::Ls(cmd) => cmd.run()?, - }; - Ok(()) - } -} - -#[derive(Debug, clap::Args, Clone, Default)] -#[group(skip)] -pub struct Args { - /// RPC server endpoint - #[arg( - long = "rpc-url", - requires = "network_passphrase", - required_unless_present = "network", - env = "SOROBAN_RPC_URL", - help_heading = HEADING_RPC, - )] - pub rpc_url: Option, - /// Network passphrase to sign the transaction sent to the rpc server - #[arg( - long = "network-passphrase", - requires = "rpc_url", - required_unless_present = "network", - env = "SOROBAN_NETWORK_PASSPHRASE", - help_heading = HEADING_RPC, - )] - pub network_passphrase: Option, - /// Name of network to use from config - #[arg( - long, - required_unless_present = "rpc_url", - env = "SOROBAN_NETWORK", - help_heading = HEADING_RPC, - )] - pub network: Option, -} - -impl Args { - pub fn get(&self, locator: &locator::Args) -> Result { - if let Some(name) = self.network.as_deref() { - if let Ok(network) = locator.read_network(name) { - return Ok(network); - } - } - if let (Some(rpc_url), Some(network_passphrase)) = - (self.rpc_url.clone(), self.network_passphrase.clone()) - { - Ok(Network { - rpc_url, - network_passphrase, - }) - } else { - Err(Error::Network) - } - } -} - -#[derive(Debug, clap::Args, Serialize, Deserialize, Clone)] -#[group(skip)] -pub struct Network { - /// RPC server endpoint - #[arg( - long = "rpc-url", - env = "SOROBAN_RPC_URL", - help_heading = HEADING_RPC, - )] - pub rpc_url: String, - /// Network passphrase to sign the transaction sent to the rpc server - #[arg( - long, - env = "SOROBAN_NETWORK_PASSPHRASE", - help_heading = HEADING_RPC, - )] - pub network_passphrase: String, -} - -impl Network { - pub async fn helper_url(&self, addr: &str) -> Result { - tracing::debug!("address {addr:?}"); - let client = Client::new(&self.rpc_url)?; - let helper_url_root = client.friendbot_url().await?; - let uri = http::Uri::from_str(&helper_url_root) - .map_err(|_| Error::InvalidUrl(helper_url_root.to_string()))?; - http::Uri::from_str(&format!("{uri:?}?addr={addr}")) - .map_err(|_| Error::InvalidUrl(helper_url_root.to_string())) - } - - #[allow(clippy::similar_names)] - pub async fn fund_address(&self, addr: &PublicKey) -> Result<(), Error> { - let uri = self.helper_url(&addr.to_string()).await?; - tracing::debug!("URL {uri:?}"); - let response = match uri.scheme_str() { - Some("http") => hyper::Client::new().get(uri.clone()).await?, - Some("https") => { - #[cfg(target_os = "windows")] - { - return Err(Error::WindowsNotSupported(uri.to_string())); - } - #[cfg(not(target_os = "windows"))] - { - let https = hyper_tls::HttpsConnector::new(); - hyper::Client::builder() - .build::<_, hyper::Body>(https) - .get(uri.clone()) - .await? - } - } - _ => { - return Err(Error::InvalidUrl(uri.to_string())); - } - }; - let body = hyper::body::to_bytes(response.into_body()).await?; - let res = serde_json::from_slice::(&body) - .map_err(|e| Error::FailedToParseJSON(uri.to_string(), e))?; - tracing::debug!("{res:#?}"); - if let Some(detail) = res.get("detail").and_then(Value::as_str) { - if detail.contains("createAccountAlreadyExist") { - tracing::warn!("Account already exists"); - } - } else if res.get("successful").is_none() { - return Err(Error::InproperResponse(res.to_string())); - } - Ok(()) - } -} - -impl Network { - pub fn futurenet() -> Self { - Network { - rpc_url: "https://rpc-futurenet.stellar.org:443".to_owned(), - network_passphrase: "Test SDF Future Network ; October 2022".to_owned(), - } - } -} diff --git a/cmd/soroban-cli/src/commands/network/rm.rs b/cmd/soroban-cli/src/commands/network/rm.rs deleted file mode 100644 index 7051dc6b..00000000 --- a/cmd/soroban-cli/src/commands/network/rm.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::locator; -use clap::command; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Locator(#[from] locator::Error), -} - -#[derive(Debug, clap::Parser, Clone)] -#[group(skip)] -pub struct Cmd { - /// Network to remove - pub name: String, - - #[command(flatten)] - pub config: locator::Args, -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - Ok(self.config.remove_network(&self.name)?) - } -} diff --git a/cmd/soroban-cli/src/commands/plugin.rs b/cmd/soroban-cli/src/commands/plugin.rs deleted file mode 100644 index 27c191f0..00000000 --- a/cmd/soroban-cli/src/commands/plugin.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::process::Command; - -use clap::CommandFactory; -use which::which; - -use crate::{utils, Root}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Plugin not provided. Should be `soroban plugin` for a binary `soroban-plugin`")] - MissingSubcommand, - #[error(transparent)] - IO(#[from] std::io::Error), - #[error( - r#"error: no such command: `{0}` - - {1}View all installed plugins with `soroban --list`"# - )] - ExecutableNotFound(String, String), - #[error(transparent)] - Which(#[from] which::Error), - #[error(transparent)] - Regex(#[from] regex::Error), -} - -const SUBCOMMAND_TOLERANCE: f64 = 0.75; -const PLUGIN_TOLERANCE: f64 = 0.75; -const MIN_LENGTH: usize = 4; - -/// Tries to run a plugin, if the plugin's name is similar enough to any of the current subcommands return Ok. -/// Otherwise only errors can be returned because this process will exit with the plugin. -pub fn run() -> Result<(), Error> { - let (name, args) = { - let mut args = std::env::args().skip(1); - let name = args.next().ok_or(Error::MissingSubcommand)?; - (name, args) - }; - - if Root::command().get_subcommands().any(|c| { - let sc_name = c.get_name(); - sc_name.starts_with(&name) - || (name.len() >= MIN_LENGTH && strsim::jaro(sc_name, &name) >= SUBCOMMAND_TOLERANCE) - }) { - return Ok(()); - } - - let bin = which(format!("soroban-{name}")).map_err(|_| { - let suggestion = if let Ok(bins) = list() { - let suggested_name = bins - .iter() - .map(|b| (b, strsim::jaro_winkler(&name, b))) - .filter(|(_, i)| *i > PLUGIN_TOLERANCE) - .min_by(|a, b| a.1.total_cmp(&b.1)) - .map(|(a, _)| a.to_string()) - .unwrap_or_default(); - if suggested_name.is_empty() { - suggested_name - } else { - format!( - r#"Did you mean `{suggested_name}`? - "# - ) - } - } else { - String::new() - }; - Error::ExecutableNotFound(name, suggestion) - })?; - std::process::exit( - Command::new(bin) - .args(args) - .spawn()? - .wait()? - .code() - .unwrap(), - ); -} - -const MAX_HEX_LENGTH: usize = 10; - -pub fn list() -> Result, Error> { - let re_str = if cfg!(target_os = "windows") { - r"^soroban-.*.exe$" - } else { - r"^soroban-.*" - }; - let re = regex::Regex::new(re_str)?; - Ok(which::which_re(re)? - .filter_map(|b| { - let s = b.file_name()?.to_str()?; - Some(s.strip_suffix(".exe").unwrap_or(s).to_string()) - }) - .filter(|s| !(utils::is_hex_string(s) && s.len() > MAX_HEX_LENGTH)) - .map(|s| s.replace("soroban-", "")) - .collect()) -} diff --git a/cmd/soroban-cli/src/commands/version.rs b/cmd/soroban-cli/src/commands/version.rs deleted file mode 100644 index d9fc091b..00000000 --- a/cmd/soroban-cli/src/commands/version.rs +++ /dev/null @@ -1,32 +0,0 @@ -use clap::Parser; -use soroban_env_host::meta; -use std::fmt::Debug; - -const GIT_REVISION: &str = env!("GIT_REVISION"); - -#[derive(Parser, Debug, Clone)] -#[group(skip)] -pub struct Cmd; - -impl Cmd { - #[allow(clippy::unused_self)] - pub fn run(&self) { - println!("soroban {}", long()); - } -} - -pub fn long() -> String { - let env = soroban_env_host::VERSION; - let xdr = soroban_env_host::VERSION.xdr; - [ - format!("{} ({GIT_REVISION})", env!("CARGO_PKG_VERSION")), - format!("soroban-env {} ({})", env.pkg, env.rev), - format!("soroban-env interface version {}", meta::INTERFACE_VERSION), - format!( - "stellar-xdr {} ({}) -xdr curr ({})", - xdr.pkg, xdr.rev, xdr.xdr_curr, - ), - ] - .join("\n") -} diff --git a/cmd/soroban-cli/src/fee.rs b/cmd/soroban-cli/src/fee.rs deleted file mode 100644 index ee8b9614..00000000 --- a/cmd/soroban-cli/src/fee.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::commands::HEADING_RPC; -use clap::arg; - -#[derive(Debug, clap::Args, Clone)] -#[group(skip)] -pub struct Args { - /// fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm - #[arg(long, default_value = "100", env = "SOROBAN_FEE", help_heading = HEADING_RPC)] - pub fee: u32, -} - -impl Default for Args { - fn default() -> Self { - Self { fee: 100 } - } -} diff --git a/cmd/soroban-cli/src/key.rs b/cmd/soroban-cli/src/key.rs deleted file mode 100644 index e9901abd..00000000 --- a/cmd/soroban-cli/src/key.rs +++ /dev/null @@ -1,110 +0,0 @@ -use clap::arg; -use soroban_env_host::xdr::{ - self, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Limits, ReadXdr, ScAddress, - ScVal, -}; -use std::path::PathBuf; - -use crate::{ - commands::contract::Durability, - utils::{self}, - wasm, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Spec(#[from] soroban_spec_tools::Error), - #[error(transparent)] - Xdr(#[from] xdr::Error), - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, stellar_strkey::DecodeError), - #[error(transparent)] - Wasm(#[from] wasm::Error), -} - -#[derive(Debug, clap::Args, Clone)] -#[group(skip)] -pub struct Args { - /// Contract ID to which owns the data entries. - /// If no keys provided the Contract's instance will be extended - #[arg( - long = "id", - required_unless_present = "wasm", - required_unless_present = "wasm_hash" - )] - pub contract_id: Option, - /// Storage key (symbols only) - #[arg(long = "key", conflicts_with = "key_xdr")] - pub key: Option>, - /// Storage key (base64-encoded XDR) - #[arg(long = "key-xdr", conflicts_with = "key")] - pub key_xdr: Option>, - /// Path to Wasm file of contract code to extend - #[arg( - long, - conflicts_with = "contract_id", - conflicts_with = "key", - conflicts_with = "key_xdr", - conflicts_with = "wasm_hash" - )] - pub wasm: Option, - /// Path to Wasm file of contract code to extend - #[arg( - long, - conflicts_with = "contract_id", - conflicts_with = "key", - conflicts_with = "key_xdr", - conflicts_with = "wasm" - )] - pub wasm_hash: Option, - /// Storage entry durability - #[arg(long, value_enum, required = true)] - pub durability: Durability, -} - -impl Args { - pub fn parse_keys(&self) -> Result, Error> { - let keys = if let Some(keys) = &self.key { - keys.iter() - .map(|key| { - Ok(soroban_spec_tools::from_string_primitive( - key, - &xdr::ScSpecTypeDef::Symbol, - )?) - }) - .collect::, Error>>()? - } else if let Some(keys) = &self.key_xdr { - keys.iter() - .map(|s| Ok(ScVal::from_xdr_base64(s, Limits::none())?)) - .collect::, Error>>()? - } else if let Some(wasm) = &self.wasm { - return Ok(vec![crate::wasm::Args { wasm: wasm.clone() }.try_into()?]); - } else if let Some(wasm_hash) = &self.wasm_hash { - return Ok(vec![LedgerKey::ContractCode(LedgerKeyContractCode { - hash: xdr::Hash( - utils::contract_id_from_str(wasm_hash) - .map_err(|e| Error::CannotParseContractId(wasm_hash.clone(), e))?, - ), - })]); - } else { - vec![ScVal::LedgerKeyContractInstance] - }; - let contract_id = contract_id(self.contract_id.as_ref().unwrap())?; - - Ok(keys - .into_iter() - .map(|key| { - LedgerKey::ContractData(LedgerKeyContractData { - contract: ScAddress::Contract(xdr::Hash(contract_id)), - durability: (&self.durability).into(), - key, - }) - }) - .collect()) - } -} - -fn contract_id(s: &str) -> Result<[u8; 32], Error> { - utils::contract_id_from_str(s).map_err(|e| Error::CannotParseContractId(s.to_string(), e)) -} diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs deleted file mode 100644 index 3aad487c..00000000 --- a/cmd/soroban-cli/src/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow( - clippy::missing_errors_doc, - clippy::must_use_candidate, - clippy::missing_panics_doc -)] -pub mod commands; -pub mod fee; -pub mod key; -pub mod log; -pub mod rpc; -pub mod toid; -pub mod utils; -pub mod wasm; - -use std::path::Path; - -pub use commands::Root; - -pub fn parse_cmd(s: &str) -> Result -where - T: clap::CommandFactory + clap::FromArgMatches, -{ - let input = shlex::split(s).ok_or_else(|| { - clap::Error::raw( - clap::error::ErrorKind::InvalidValue, - format!("Invalid input for command:\n{s}"), - ) - })?; - T::from_arg_matches_mut(&mut T::command().no_binary_name(true).get_matches_from(input)) -} - -pub trait CommandParser { - fn parse(s: &str) -> Result; - - fn parse_arg_vec(s: &[&str]) -> Result; -} - -impl CommandParser for T -where - T: clap::CommandFactory + clap::FromArgMatches, -{ - fn parse(s: &str) -> Result { - parse_cmd(s) - } - - fn parse_arg_vec(args: &[&str]) -> Result { - T::from_arg_matches_mut(&mut T::command().no_binary_name(true).get_matches_from(args)) - } -} - -pub trait Pwd { - fn set_pwd(&mut self, pwd: &Path); -} diff --git a/cmd/soroban-cli/src/log.rs b/cmd/soroban-cli/src/log.rs deleted file mode 100644 index 16121982..00000000 --- a/cmd/soroban-cli/src/log.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod auth; -pub mod budget; -pub mod cost; -pub mod diagnostic_event; -pub mod footprint; -pub mod host_event; - -pub use auth::*; -pub use budget::*; -pub use cost::*; -pub use diagnostic_event::*; -pub use footprint::*; -pub use host_event::*; diff --git a/cmd/soroban-cli/src/log/auth.rs b/cmd/soroban-cli/src/log/auth.rs deleted file mode 100644 index c37e7ed3..00000000 --- a/cmd/soroban-cli/src/log/auth.rs +++ /dev/null @@ -1,7 +0,0 @@ -use soroban_env_host::xdr::{SorobanAuthorizationEntry, VecM}; - -pub fn auth(auth: &[VecM]) { - if !auth.is_empty() { - tracing::debug!("{auth:#?}"); - } -} diff --git a/cmd/soroban-cli/src/log/budget.rs b/cmd/soroban-cli/src/log/budget.rs deleted file mode 100644 index 59ff4aad..00000000 --- a/cmd/soroban-cli/src/log/budget.rs +++ /dev/null @@ -1,5 +0,0 @@ -use soroban_env_host::budget::Budget; - -pub fn budget(budget: &Budget) { - tracing::debug!("{budget:#?}"); -} diff --git a/cmd/soroban-cli/src/log/cost.rs b/cmd/soroban-cli/src/log/cost.rs deleted file mode 100644 index 3e049a6c..00000000 --- a/cmd/soroban-cli/src/log/cost.rs +++ /dev/null @@ -1,27 +0,0 @@ -use soroban_env_host::xdr::SorobanResources; -use std::fmt::{Debug, Display}; - -struct Cost<'a>(&'a SorobanResources); - -impl Debug for Cost<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // TODO: Should we output the footprint here? - writeln!(f, "==================== Cost ====================")?; - writeln!(f, "CPU used: {}", self.0.instructions,)?; - writeln!(f, "Bytes read: {}", self.0.read_bytes,)?; - writeln!(f, "Bytes written: {}", self.0.write_bytes,)?; - writeln!(f, "==============================================")?; - Ok(()) - } -} - -impl Display for Cost<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(self, f) - } -} - -pub fn cost(resources: &SorobanResources) { - let cost = Cost(resources); - tracing::debug!(?cost); -} diff --git a/cmd/soroban-cli/src/log/diagnostic_event.rs b/cmd/soroban-cli/src/log/diagnostic_event.rs deleted file mode 100644 index 68af67a4..00000000 --- a/cmd/soroban-cli/src/log/diagnostic_event.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub fn diagnostic_events(events: &[impl std::fmt::Debug], level: tracing::Level) { - for (i, event) in events.iter().enumerate() { - if level == tracing::Level::TRACE { - tracing::trace!("{i}: {event:#?}"); - } else if level == tracing::Level::INFO { - tracing::info!("{i}: {event:#?}"); - } else if level == tracing::Level::ERROR { - tracing::error!("{i}: {event:#?}"); - } - } -} diff --git a/cmd/soroban-cli/src/log/footprint.rs b/cmd/soroban-cli/src/log/footprint.rs deleted file mode 100644 index bfbc9f7a..00000000 --- a/cmd/soroban-cli/src/log/footprint.rs +++ /dev/null @@ -1,5 +0,0 @@ -use soroban_env_host::xdr::LedgerFootprint; - -pub fn footprint(footprint: &LedgerFootprint) { - tracing::debug!("{footprint:#?}"); -} diff --git a/cmd/soroban-cli/src/log/host_event.rs b/cmd/soroban-cli/src/log/host_event.rs deleted file mode 100644 index 4238a74c..00000000 --- a/cmd/soroban-cli/src/log/host_event.rs +++ /dev/null @@ -1,7 +0,0 @@ -use soroban_env_host::events::HostEvent; - -pub fn host_events(events: &[HostEvent]) { - for (i, event) in events.iter().enumerate() { - tracing::info!("{i}: {event:#?}"); - } -} diff --git a/cmd/soroban-cli/src/rpc/fixtures/event_response.json b/cmd/soroban-cli/src/rpc/fixtures/event_response.json deleted file mode 100644 index 6f520fdf..00000000 --- a/cmd/soroban-cli/src/rpc/fixtures/event_response.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "events": [{ - "eventType": "system", - "ledger": "43601283", - "ledgerClosedAt": "2022-11-16T16:10:41Z", - "contractId": "CDR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OO5Z", - "id": "0164090849041387521-0000000003", - "pagingToken": "164090849041387521-3", - "topic": [ - "AAAABQAAAAh0cmFuc2Zlcg==", - "AAAAAQB6Mcc=" - ], - "value": "AAAABQAAAApHaWJNb255UGxzAAA=" - }, { - "eventType": "contract", - "ledger": "43601284", - "ledgerClosedAt": "2022-11-16T16:10:41Z", - "contractId": "CDR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OO5Z", - "id": "0164090849041387521-0000000003", - "pagingToken": "164090849041387521-3", - "topic": [ - "AAAABQAAAAh0cmFuc2Zlcg==", - "AAAAAQB6Mcc=" - ], - "value": "AAAABQAAAApHaWJNb255UGxzAAA=" - }, { - "eventType": "system", - "ledger": "43601285", - "ledgerClosedAt": "2022-11-16T16:10:41Z", - "contractId": "CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2", - "id": "0164090849041387521-0000000003", - "pagingToken": "164090849041387521-3", - "topic": [ - "AAAABQAAAAh0cmFuc2Zlcg==", - "AAAAAQB6Mcc=" - ], - "value": "AAAABQAAAApHaWJNb255UGxzAAA=" - }] -} \ No newline at end of file diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs deleted file mode 100644 index 53542629..00000000 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ /dev/null @@ -1,1141 +0,0 @@ -use http::{uri::Authority, Uri}; -use itertools::Itertools; -use jsonrpsee_core::params::ObjectParams; -use jsonrpsee_core::{self, client::ClientT, rpc_params}; -use jsonrpsee_http_client::{HeaderMap, HttpClient, HttpClientBuilder}; -use serde_aux::prelude::{ - deserialize_default_from_null, deserialize_number_from_string, - deserialize_option_number_from_string, -}; -use soroban_env_host::xdr::{ - self, AccountEntry, AccountId, ContractDataEntry, DiagnosticEvent, Error as XdrError, - LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, Limited, PublicKey, ReadXdr, - SorobanAuthorizationEntry, SorobanResources, SorobanTransactionData, Transaction, - TransactionEnvelope, TransactionMeta, TransactionMetaV3, TransactionResult, Uint256, VecM, - WriteXdr, -}; -use soroban_sdk::token; -use soroban_sdk::xdr::Limits; -use std::{ - fmt::Display, - str::FromStr, - time::{Duration, Instant}, -}; -use termcolor::{Color, ColorChoice, StandardStream, WriteColor}; -use termcolor_output::colored; -use tokio::time::sleep; - -use crate::utils::contract_spec; - -mod txn; - -const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); - -pub type LogEvents = fn( - footprint: &LedgerFootprint, - auth: &[VecM], - events: &[DiagnosticEvent], -) -> (); - -pub type LogResources = fn(resources: &SorobanResources) -> (); - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - InvalidAddress(#[from] stellar_strkey::DecodeError), - #[error("invalid response from server")] - InvalidResponse, - #[error("provided network passphrase {expected:?} does not match the server: {server:?}")] - InvalidNetworkPassphrase { expected: String, server: String }, - #[error("xdr processing error: {0}")] - Xdr(#[from] XdrError), - #[error("invalid rpc url: {0}")] - InvalidRpcUrl(http::uri::InvalidUri), - #[error("invalid rpc url: {0}")] - InvalidRpcUrlFromUriParts(http::uri::InvalidUriParts), - #[error("invalid friendbot url: {0}")] - InvalidUrl(String), - #[error("jsonrpc error: {0}")] - JsonRpc(#[from] jsonrpsee_core::Error), - #[error("json decoding error: {0}")] - Serde(#[from] serde_json::Error), - #[error("transaction failed: {0}")] - TransactionFailed(String), - #[error("transaction submission failed: {0}")] - TransactionSubmissionFailed(String), - #[error("expected transaction status: {0}")] - UnexpectedTransactionStatus(String), - #[error("transaction submission timeout")] - TransactionSubmissionTimeout, - #[error("transaction simulation failed: {0}")] - TransactionSimulationFailed(String), - #[error("{0} not found: {1}")] - NotFound(String, String), - #[error("Missing result in successful response")] - MissingResult, - #[error("Failed to read Error response from server")] - MissingError, - #[error("Missing signing key for account {address}")] - MissingSignerForAddress { address: String }, - #[error("cursor is not valid")] - InvalidCursor, - #[error("unexpected ({length}) simulate transaction result length")] - UnexpectedSimulateTransactionResultSize { length: usize }, - #[error("unexpected ({count}) number of operations")] - UnexpectedOperationCount { count: usize }, - #[error("Transaction contains unsupported operation type")] - UnsupportedOperationType, - #[error("unexpected contract code data type: {0:?}")] - UnexpectedContractCodeDataType(LedgerEntryData), - #[error(transparent)] - CouldNotParseContractSpec(#[from] contract_spec::Error), - #[error("unexpected contract code got token")] - UnexpectedToken(ContractDataEntry), - #[error(transparent)] - Spec(#[from] soroban_spec::read::FromWasmError), - #[error(transparent)] - SpecBase64(#[from] soroban_spec::read::ParseSpecBase64Error), - #[error("Fee was too large {0}")] - LargeFee(u64), - #[error("Cannot authorize raw transactions")] - CannotAuthorizeRawTransaction, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct SendTransactionResponse { - pub hash: String, - pub status: String, - #[serde( - rename = "errorResultXdr", - skip_serializing_if = "Option::is_none", - default - )] - pub error_result_xdr: Option, - #[serde(rename = "latestLedger")] - pub latest_ledger: u32, - #[serde( - rename = "latestLedgerCloseTime", - deserialize_with = "deserialize_number_from_string" - )] - pub latest_ledger_close_time: u32, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetTransactionResponseRaw { - pub status: String, - #[serde( - rename = "envelopeXdr", - skip_serializing_if = "Option::is_none", - default - )] - pub envelope_xdr: Option, - #[serde(rename = "resultXdr", skip_serializing_if = "Option::is_none", default)] - pub result_xdr: Option, - #[serde( - rename = "resultMetaXdr", - skip_serializing_if = "Option::is_none", - default - )] - pub result_meta_xdr: Option, - // TODO: add ledger info and application order -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetTransactionResponse { - pub status: String, - pub envelope: Option, - pub result: Option, - pub result_meta: Option, -} - -impl TryInto for GetTransactionResponseRaw { - type Error = xdr::Error; - - fn try_into(self) -> Result { - Ok(GetTransactionResponse { - status: self.status, - envelope: self - .envelope_xdr - .map(|v| ReadXdr::from_xdr_base64(v, Limits::none())) - .transpose()?, - result: self - .result_xdr - .map(|v| ReadXdr::from_xdr_base64(v, Limits::none())) - .transpose()?, - result_meta: self - .result_meta_xdr - .map(|v| ReadXdr::from_xdr_base64(v, Limits::none())) - .transpose()?, - }) - } -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct LedgerEntryResult { - pub key: String, - pub xdr: String, - #[serde(rename = "lastModifiedLedgerSeq")] - pub last_modified_ledger: u32, - #[serde( - rename = "liveUntilLedgerSeq", - skip_serializing_if = "Option::is_none", - deserialize_with = "deserialize_option_number_from_string", - default - )] - pub live_until_ledger_seq_ledger_seq: Option, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetLedgerEntriesResponse { - pub entries: Option>, - #[serde(rename = "latestLedger")] - pub latest_ledger: i64, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetNetworkResponse { - #[serde( - rename = "friendbotUrl", - skip_serializing_if = "Option::is_none", - default - )] - pub friendbot_url: Option, - pub passphrase: String, - #[serde(rename = "protocolVersion")] - pub protocol_version: u32, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetLatestLedgerResponse { - pub id: String, - #[serde(rename = "protocolVersion")] - pub protocol_version: u32, - pub sequence: u32, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug, Default)] -pub struct Cost { - #[serde( - rename = "cpuInsns", - deserialize_with = "deserialize_number_from_string" - )] - pub cpu_insns: u64, - #[serde( - rename = "memBytes", - deserialize_with = "deserialize_number_from_string" - )] - pub mem_bytes: u64, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct SimulateHostFunctionResultRaw { - #[serde(deserialize_with = "deserialize_default_from_null")] - pub auth: Vec, - pub xdr: String, -} - -#[derive(Debug)] -pub struct SimulateHostFunctionResult { - pub auth: Vec, - pub xdr: xdr::ScVal, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug, Default)] -pub struct SimulateTransactionResponse { - #[serde( - rename = "minResourceFee", - deserialize_with = "deserialize_number_from_string", - default - )] - pub min_resource_fee: u64, - #[serde(default)] - pub cost: Cost, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub results: Vec, - #[serde(rename = "transactionData", default)] - pub transaction_data: String, - #[serde( - deserialize_with = "deserialize_default_from_null", - skip_serializing_if = "Vec::is_empty", - default - )] - pub events: Vec, - #[serde( - rename = "restorePreamble", - skip_serializing_if = "Option::is_none", - default - )] - pub restore_preamble: Option, - #[serde(rename = "latestLedger")] - pub latest_ledger: u32, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub error: Option, -} - -impl SimulateTransactionResponse { - pub fn results(&self) -> Result, Error> { - self.results - .iter() - .map(|r| { - Ok(SimulateHostFunctionResult { - auth: r - .auth - .iter() - .map(|a| { - Ok(SorobanAuthorizationEntry::from_xdr_base64( - a, - Limits::none(), - )?) - }) - .collect::>()?, - xdr: xdr::ScVal::from_xdr_base64(&r.xdr, Limits::none())?, - }) - }) - .collect() - } - - pub fn events(&self) -> Result, Error> { - self.events - .iter() - .map(|e| Ok(DiagnosticEvent::from_xdr_base64(e, Limits::none())?)) - .collect() - } - - pub fn transaction_data(&self) -> Result { - Ok(SorobanTransactionData::from_xdr_base64( - &self.transaction_data, - Limits::none(), - )?) - } -} - -#[derive(serde::Deserialize, serde::Serialize, Debug, Default)] -pub struct RestorePreamble { - #[serde(rename = "transactionData")] - pub transaction_data: String, - #[serde( - rename = "minResourceFee", - deserialize_with = "deserialize_number_from_string" - )] - pub min_resource_fee: u64, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetEventsResponse { - #[serde(deserialize_with = "deserialize_default_from_null")] - pub events: Vec, - #[serde(rename = "latestLedger")] - pub latest_ledger: u32, -} - -// Determines whether or not a particular filter matches a topic based on the -// same semantics as the RPC server: -// -// - for an exact segment match, the filter is a base64-encoded ScVal -// - for a wildcard, single-segment match, the string "*" matches exactly one -// segment -// -// The expectation is that a `filter` is a comma-separated list of segments that -// has previously been validated, and `topic` is the list of segments applicable -// for this event. -// -// [API -// Reference](https://docs.google.com/document/d/1TZUDgo_3zPz7TiPMMHVW_mtogjLyPL0plvzGMsxSz6A/edit#bookmark=id.35t97rnag3tx) -// [Code -// Reference](https://github.com/stellar/soroban-tools/blob/bac1be79e8c2590c9c35ad8a0168aab0ae2b4171/cmd/soroban-rpc/internal/methods/get_events.go#L182-L203) -pub fn does_topic_match(topic: &[String], filter: &[String]) -> bool { - filter.len() == topic.len() - && filter - .iter() - .enumerate() - .all(|(i, s)| *s == "*" || topic[i] == *s) -} - -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] -pub struct Event { - #[serde(rename = "type")] - pub event_type: String, - - pub ledger: u32, - #[serde(rename = "ledgerClosedAt")] - pub ledger_closed_at: String, - - pub id: String, - #[serde(rename = "pagingToken")] - pub paging_token: String, - - #[serde(rename = "contractId")] - pub contract_id: String, - pub topic: Vec, - pub value: String, -} - -impl Display for Event { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - "Event {} [{}]:", - self.paging_token, - self.event_type.to_ascii_uppercase() - )?; - writeln!( - f, - " Ledger: {} (closed at {})", - self.ledger, self.ledger_closed_at - )?; - writeln!(f, " Contract: {}", self.contract_id)?; - writeln!(f, " Topics:")?; - for topic in &self.topic { - let scval = - xdr::ScVal::from_xdr_base64(topic, Limits::none()).map_err(|_| std::fmt::Error)?; - writeln!(f, " {scval:?}")?; - } - let scval = xdr::ScVal::from_xdr_base64(&self.value, Limits::none()) - .map_err(|_| std::fmt::Error)?; - writeln!(f, " Value: {scval:?}") - } -} - -impl Event { - pub fn parse_cursor(&self) -> Result<(u64, i32), Error> { - parse_cursor(&self.id) - } - - pub fn pretty_print(&self) -> Result<(), Box> { - let mut stdout = StandardStream::stdout(ColorChoice::Auto); - if !stdout.supports_color() { - println!("{self}"); - return Ok(()); - } - - let color = match self.event_type.as_str() { - "system" => Color::Yellow, - _ => Color::Blue, - }; - colored!( - stdout, - "{}Event{} {}{}{} [{}{}{}{}]:\n", - bold!(true), - bold!(false), - fg!(Some(Color::Green)), - self.paging_token, - reset!(), - bold!(true), - fg!(Some(color)), - self.event_type.to_ascii_uppercase(), - reset!(), - )?; - - colored!( - stdout, - " Ledger: {}{}{} (closed at {}{}{})\n", - fg!(Some(Color::Green)), - self.ledger, - reset!(), - fg!(Some(Color::Green)), - self.ledger_closed_at, - reset!(), - )?; - - colored!( - stdout, - " Contract: {}{}{}\n", - fg!(Some(Color::Green)), - self.contract_id, - reset!(), - )?; - - colored!(stdout, " Topics:\n")?; - for topic in &self.topic { - let scval = xdr::ScVal::from_xdr_base64(topic, Limits::none())?; - colored!( - stdout, - " {}{:?}{}\n", - fg!(Some(Color::Green)), - scval, - reset!(), - )?; - } - - let scval = xdr::ScVal::from_xdr_base64(&self.value, Limits::none())?; - colored!( - stdout, - " Value: {}{:?}{}\n", - fg!(Some(Color::Green)), - scval, - reset!(), - )?; - - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)] -pub enum EventType { - All, - Contract, - System, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum EventStart { - Ledger(u32), - Cursor(String), -} - -#[derive(Debug)] -pub struct FullLedgerEntry { - pub key: LedgerKey, - pub val: LedgerEntryData, - pub last_modified_ledger: u32, - pub live_until_ledger_seq: u32, -} - -#[derive(Debug)] -pub struct FullLedgerEntries { - pub entries: Vec, - pub latest_ledger: i64, -} - -pub struct Client { - base_url: String, -} - -impl Client { - pub fn new(base_url: &str) -> Result { - // Add the port to the base URL if there is no port explicitly included - // in the URL and the scheme allows us to infer a default port. - // Jsonrpsee requires a port to always be present even if one can be - // inferred. This may change: https://github.com/paritytech/jsonrpsee/issues/1048. - let uri = base_url.parse::().map_err(Error::InvalidRpcUrl)?; - let mut parts = uri.into_parts(); - if let (Some(scheme), Some(authority)) = (&parts.scheme, &parts.authority) { - if authority.port().is_none() { - let port = match scheme.as_str() { - "http" => Some(80), - "https" => Some(443), - _ => None, - }; - if let Some(port) = port { - let host = authority.host(); - parts.authority = Some( - Authority::from_str(&format!("{host}:{port}")) - .map_err(Error::InvalidRpcUrl)?, - ); - } - } - } - let uri = Uri::from_parts(parts).map_err(Error::InvalidRpcUrlFromUriParts)?; - tracing::trace!(?uri); - Ok(Self { - base_url: uri.to_string(), - }) - } - - fn client(&self) -> Result { - let url = self.base_url.clone(); - let mut headers = HeaderMap::new(); - headers.insert("X-Client-Name", "soroban-cli".parse().unwrap()); - let version = VERSION.unwrap_or("devel"); - headers.insert("X-Client-Version", version.parse().unwrap()); - Ok(HttpClientBuilder::default() - .set_headers(headers) - .build(url)?) - } - - pub async fn friendbot_url(&self) -> Result { - let network = self.get_network().await?; - tracing::trace!("{network:#?}"); - network.friendbot_url.ok_or_else(|| { - Error::NotFound( - "Friendbot".to_string(), - "Friendbot is not available on this network".to_string(), - ) - }) - } - - pub async fn verify_network_passphrase(&self, expected: Option<&str>) -> Result { - let server = self.get_network().await?.passphrase; - if let Some(expected) = expected { - if expected != server { - return Err(Error::InvalidNetworkPassphrase { - expected: expected.to_string(), - server, - }); - } - } - Ok(server) - } - - pub async fn get_network(&self) -> Result { - tracing::trace!("Getting network"); - Ok(self.client()?.request("getNetwork", rpc_params![]).await?) - } - - pub async fn get_latest_ledger(&self) -> Result { - tracing::trace!("Getting latest ledger"); - Ok(self - .client()? - .request("getLatestLedger", rpc_params![]) - .await?) - } - - pub async fn get_account(&self, address: &str) -> Result { - tracing::trace!("Getting address {}", address); - let key = LedgerKey::Account(LedgerKeyAccount { - account_id: AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( - stellar_strkey::ed25519::PublicKey::from_string(address)?.0, - ))), - }); - let keys = Vec::from([key]); - let response = self.get_ledger_entries(&keys).await?; - let entries = response.entries.unwrap_or_default(); - if entries.is_empty() { - return Err(Error::NotFound( - "Account".to_string(), - format!( - r#"{address} -Might need to fund account like: -soroban config identity fund {address} --network -soroban config identity fund {address} --helper-url "# - ), - )); - } - let ledger_entry = &entries[0]; - let mut read = Limited::new(ledger_entry.xdr.as_bytes(), Limits::none()); - if let LedgerEntryData::Account(entry) = LedgerEntryData::read_xdr_base64(&mut read)? { - tracing::trace!(account=?entry); - Ok(entry) - } else { - Err(Error::InvalidResponse) - } - } - - pub async fn send_transaction( - &self, - tx: &TransactionEnvelope, - ) -> Result<(TransactionResult, TransactionMeta, Vec), Error> { - let client = self.client()?; - tracing::trace!("Sending:\n{tx:#?}"); - let SendTransactionResponse { - hash, - error_result_xdr, - status, - .. - } = client - .request( - "sendTransaction", - rpc_params![tx.to_xdr_base64(Limits::none())?], - ) - .await - .map_err(|err| { - Error::TransactionSubmissionFailed(format!("No status yet:\n {err:#?}")) - })?; - - if status == "ERROR" { - let error = error_result_xdr - .ok_or(Error::MissingError) - .and_then(|x| { - TransactionResult::read_xdr_base64(&mut Limited::new( - x.as_bytes(), - Limits::none(), - )) - .map_err(|_| Error::InvalidResponse) - }) - .map(|r| r.result); - tracing::error!("TXN failed:\n {error:#?}"); - return Err(Error::TransactionSubmissionFailed(format!("{:#?}", error?))); - } - // even if status == "success" we need to query the transaction status in order to get the result - - // Poll the transaction status - let start = Instant::now(); - loop { - let response: GetTransactionResponse = self.get_transaction(&hash).await?.try_into()?; - match response.status.as_str() { - "SUCCESS" => { - // TODO: the caller should probably be printing this - tracing::trace!("{response:#?}"); - let GetTransactionResponse { - result, - result_meta, - .. - } = response; - let meta = result_meta.ok_or(Error::MissingResult)?; - let events = extract_events(&meta); - return Ok((result.ok_or(Error::MissingResult)?, meta, events)); - } - "FAILED" => { - tracing::error!("{response:#?}"); - // TODO: provide a more elaborate error - return Err(Error::TransactionSubmissionFailed(format!( - "{:#?}", - response.result - ))); - } - "NOT_FOUND" => (), - _ => { - return Err(Error::UnexpectedTransactionStatus(response.status)); - } - }; - let duration = start.elapsed(); - // TODO: parameterize the timeout instead of using a magic constant - if duration.as_secs() > 10 { - return Err(Error::TransactionSubmissionTimeout); - } - sleep(Duration::from_secs(1)).await; - } - } - - pub async fn simulate_transaction( - &self, - tx: &TransactionEnvelope, - ) -> Result { - tracing::trace!("Simulating:\n{tx:#?}"); - let base64_tx = tx.to_xdr_base64(Limits::none())?; - let mut builder = ObjectParams::new(); - builder.insert("transaction", base64_tx)?; - let response: SimulateTransactionResponse = self - .client()? - .request("simulateTransaction", builder) - .await?; - tracing::trace!("Simulation response:\n {response:#?}"); - match response.error { - None => Ok(response), - Some(e) => { - crate::log::diagnostic_events(&response.events, tracing::Level::ERROR); - Err(Error::TransactionSimulationFailed(e)) - } - } - } - - pub async fn prepare_and_send_transaction( - &self, - tx_without_preflight: &Transaction, - source_key: &ed25519_dalek::SigningKey, - signers: &[ed25519_dalek::SigningKey], - network_passphrase: &str, - log_events: Option, - log_resources: Option, - ) -> Result<(TransactionResult, TransactionMeta, Vec), Error> { - let txn = txn::Assembled::new(tx_without_preflight, self).await?; - let seq_num = txn.sim_res().latest_ledger + 60; //5 min; - let authorized = txn - .handle_restore(self, source_key, network_passphrase) - .await? - .authorize(self, source_key, signers, seq_num, network_passphrase) - .await?; - authorized.log(log_events, log_resources)?; - let tx = authorized.sign(source_key, network_passphrase)?; - self.send_transaction(&tx).await - } - - pub async fn get_transaction(&self, tx_id: &str) -> Result { - Ok(self - .client()? - .request("getTransaction", rpc_params![tx_id]) - .await?) - } - - pub async fn get_ledger_entries( - &self, - keys: &[LedgerKey], - ) -> Result { - let mut base64_keys: Vec = vec![]; - for k in keys { - let base64_result = k.to_xdr_base64(Limits::none()); - if base64_result.is_err() { - return Err(Error::Xdr(XdrError::Invalid)); - } - base64_keys.push(k.to_xdr_base64(Limits::none()).unwrap()); - } - Ok(self - .client()? - .request("getLedgerEntries", rpc_params![base64_keys]) - .await?) - } - - pub async fn get_full_ledger_entries( - &self, - ledger_keys: &[LedgerKey], - ) -> Result { - let keys = ledger_keys - .iter() - .filter(|key| !matches!(key, LedgerKey::Ttl(_))) - .map(Clone::clone) - .collect::>(); - tracing::trace!("keys: {keys:#?}"); - let GetLedgerEntriesResponse { - entries, - latest_ledger, - } = self.get_ledger_entries(&keys).await?; - tracing::trace!("raw: {entries:#?}"); - let entries = entries - .unwrap_or_default() - .iter() - .map( - |LedgerEntryResult { - key, - xdr, - last_modified_ledger, - live_until_ledger_seq_ledger_seq, - }| { - Ok(FullLedgerEntry { - key: LedgerKey::from_xdr_base64(key, Limits::none())?, - val: LedgerEntryData::from_xdr_base64(xdr, Limits::none())?, - live_until_ledger_seq: live_until_ledger_seq_ledger_seq.unwrap_or_default(), - last_modified_ledger: *last_modified_ledger, - }) - }, - ) - .collect::, Error>>()?; - tracing::trace!("parsed: {entries:#?}"); - Ok(FullLedgerEntries { - entries, - latest_ledger, - }) - } - - pub async fn get_events( - &self, - start: EventStart, - event_type: Option, - contract_ids: &[String], - topics: &[String], - limit: Option, - ) -> Result { - let mut filters = serde_json::Map::new(); - - event_type - .and_then(|t| match t { - EventType::All => None, // all is the default, so avoid incl. the param - EventType::Contract => Some("contract"), - EventType::System => Some("system"), - }) - .map(|t| filters.insert("type".to_string(), t.into())); - - filters.insert("topics".to_string(), topics.into()); - filters.insert("contractIds".to_string(), contract_ids.into()); - - let mut pagination = serde_json::Map::new(); - if let Some(limit) = limit { - pagination.insert("limit".to_string(), limit.into()); - } - - let mut oparams = ObjectParams::new(); - match start { - EventStart::Ledger(l) => oparams.insert("startLedger", l)?, - EventStart::Cursor(c) => { - pagination.insert("cursor".to_string(), c.into()); - } - }; - oparams.insert("filters", vec![filters])?; - oparams.insert("pagination", pagination)?; - - Ok(self.client()?.request("getEvents", oparams).await?) - } - - pub async fn get_contract_data( - &self, - contract_id: &[u8; 32], - ) -> Result { - // Get the contract from the network - let contract_key = LedgerKey::ContractData(xdr::LedgerKeyContractData { - contract: xdr::ScAddress::Contract(xdr::Hash(*contract_id)), - key: xdr::ScVal::LedgerKeyContractInstance, - durability: xdr::ContractDataDurability::Persistent, - }); - let contract_ref = self.get_ledger_entries(&[contract_key]).await?; - let entries = contract_ref.entries.unwrap_or_default(); - if entries.is_empty() { - let contract_address = stellar_strkey::Contract(*contract_id).to_string(); - return Err(Error::NotFound("Contract".to_string(), contract_address)); - } - let contract_ref_entry = &entries[0]; - match LedgerEntryData::from_xdr_base64(&contract_ref_entry.xdr, Limits::none())? { - LedgerEntryData::ContractData(contract_data) => Ok(contract_data), - scval => Err(Error::UnexpectedContractCodeDataType(scval)), - } - } - - pub async fn get_remote_wasm(&self, contract_id: &[u8; 32]) -> Result, Error> { - match self.get_contract_data(contract_id).await? { - xdr::ContractDataEntry { - val: - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::Wasm(hash), - .. - }), - .. - } => self.get_remote_wasm_from_hash(hash).await, - scval => Err(Error::UnexpectedToken(scval)), - } - } - - pub async fn get_remote_wasm_from_hash(&self, hash: xdr::Hash) -> Result, Error> { - let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { hash: hash.clone() }); - let contract_data = self.get_ledger_entries(&[code_key]).await?; - let entries = contract_data.entries.unwrap_or_default(); - if entries.is_empty() { - return Err(Error::NotFound( - "Contract Code".to_string(), - hex::encode(hash), - )); - } - let contract_data_entry = &entries[0]; - match LedgerEntryData::from_xdr_base64(&contract_data_entry.xdr, Limits::none())? { - LedgerEntryData::ContractCode(xdr::ContractCodeEntry { code, .. }) => Ok(code.into()), - scval => Err(Error::UnexpectedContractCodeDataType(scval)), - } - } - - pub async fn get_remote_contract_spec( - &self, - contract_id: &[u8; 32], - ) -> Result, Error> { - let contract_data = self.get_contract_data(contract_id).await?; - match contract_data.val { - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::Wasm(hash), - .. - }) => Ok(contract_spec::ContractSpec::new( - &self.get_remote_wasm_from_hash(hash).await?, - ) - .map_err(Error::CouldNotParseContractSpec)? - .spec), - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::StellarAsset, - .. - }) => Ok(soroban_spec::read::parse_raw( - &token::StellarAssetSpec::spec_xdr(), - )?), - _ => Err(Error::Xdr(XdrError::Invalid)), - } - } -} - -fn extract_events(tx_meta: &TransactionMeta) -> Vec { - match tx_meta { - TransactionMeta::V3(TransactionMetaV3 { - soroban_meta: Some(meta), - .. - }) => { - // NOTE: we assume there can only be one operation, since we only send one - if meta.diagnostic_events.len() == 1 { - meta.diagnostic_events.clone().into() - } else if meta.events.len() == 1 { - meta.events - .iter() - .map(|e| DiagnosticEvent { - in_successful_contract_call: true, - event: e.clone(), - }) - .collect() - } else { - Vec::new() - } - } - _ => Vec::new(), - } -} - -pub fn parse_cursor(c: &str) -> Result<(u64, i32), Error> { - let (toid_part, event_index) = c.split('-').collect_tuple().ok_or(Error::InvalidCursor)?; - let toid_part: u64 = toid_part.parse().map_err(|_| Error::InvalidCursor)?; - let start_index: i32 = event_index.parse().map_err(|_| Error::InvalidCursor)?; - Ok((toid_part, start_index)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn simulation_transaction_response_parsing() { - let s = r#"{ - "minResourceFee": "100000000", - "cost": { "cpuInsns": "1000", "memBytes": "1000" }, - "transactionData": "", - "latestLedger": 1234 - }"#; - - let resp: SimulateTransactionResponse = serde_json::from_str(s).unwrap(); - assert_eq!(resp.min_resource_fee, 100_000_000); - } - - #[test] - fn simulation_transaction_response_parsing_mostly_empty() { - let s = r#"{ - "latestLedger": 1234 - }"#; - - let resp: SimulateTransactionResponse = serde_json::from_str(s).unwrap(); - assert_eq!(resp.latest_ledger, 1_234); - } - - #[test] - fn test_rpc_url_default_ports() { - // Default ports are added. - let client = Client::new("http://example.com").unwrap(); - assert_eq!(client.base_url, "http://example.com:80/"); - let client = Client::new("https://example.com").unwrap(); - assert_eq!(client.base_url, "https://example.com:443/"); - - // Ports are not added when already present. - let client = Client::new("http://example.com:8080").unwrap(); - assert_eq!(client.base_url, "http://example.com:8080/"); - let client = Client::new("https://example.com:8080").unwrap(); - assert_eq!(client.base_url, "https://example.com:8080/"); - - // Paths are not modified. - let client = Client::new("http://example.com/a/b/c").unwrap(); - assert_eq!(client.base_url, "http://example.com:80/a/b/c"); - let client = Client::new("https://example.com/a/b/c").unwrap(); - assert_eq!(client.base_url, "https://example.com:443/a/b/c"); - let client = Client::new("http://example.com/a/b/c/").unwrap(); - assert_eq!(client.base_url, "http://example.com:80/a/b/c/"); - let client = Client::new("https://example.com/a/b/c/").unwrap(); - assert_eq!(client.base_url, "https://example.com:443/a/b/c/"); - let client = Client::new("http://example.com/a/b:80/c/").unwrap(); - assert_eq!(client.base_url, "http://example.com:80/a/b:80/c/"); - let client = Client::new("https://example.com/a/b:80/c/").unwrap(); - assert_eq!(client.base_url, "https://example.com:443/a/b:80/c/"); - } - - #[test] - // Taken from [RPC server - // tests](https://github.com/stellar/soroban-tools/blob/main/cmd/soroban-rpc/internal/methods/get_events_test.go#L21). - fn test_does_topic_match() { - struct TestCase<'a> { - name: &'a str, - filter: Vec<&'a str>, - includes: Vec>, - excludes: Vec>, - } - - let xfer = "AAAABQAAAAh0cmFuc2Zlcg=="; - let number = "AAAAAQB6Mcc="; - let star = "*"; - - for tc in vec![ - // No filter means match nothing. - TestCase { - name: "", - filter: vec![], - includes: vec![], - excludes: vec![vec![xfer]], - }, - // "*" should match "transfer/" but not "transfer/transfer" or - // "transfer/amount", because * is specified as a SINGLE segment - // wildcard. - TestCase { - name: "*", - filter: vec![star], - includes: vec![vec![xfer]], - excludes: vec![vec![xfer, xfer], vec![xfer, number]], - }, - // "*/transfer" should match anything preceding "transfer", but - // nothing that isn't exactly two segments long. - TestCase { - name: "*/transfer", - filter: vec![star, xfer], - includes: vec![vec![number, xfer], vec![xfer, xfer]], - excludes: vec![ - vec![number], - vec![number, number], - vec![number, xfer, number], - vec![xfer], - vec![xfer, number], - vec![xfer, xfer, xfer], - ], - }, - // The inverse case of before: "transfer/*" should match any single - // segment after a segment that is exactly "transfer", but no - // additional segments. - TestCase { - name: "transfer/*", - filter: vec![xfer, star], - includes: vec![vec![xfer, number], vec![xfer, xfer]], - excludes: vec![ - vec![number], - vec![number, number], - vec![number, xfer, number], - vec![xfer], - vec![number, xfer], - vec![xfer, xfer, xfer], - ], - }, - // Here, we extend to exactly two wild segments after transfer. - TestCase { - name: "transfer/*/*", - filter: vec![xfer, star, star], - includes: vec![vec![xfer, number, number], vec![xfer, xfer, xfer]], - excludes: vec![ - vec![number], - vec![number, number], - vec![number, xfer], - vec![number, xfer, number, number], - vec![xfer], - vec![xfer, xfer, xfer, xfer], - ], - }, - // Here, we ensure wildcards can be in the middle of a filter: only - // exact matches happen on the ends, while the middle can be - // anything. - TestCase { - name: "transfer/*/number", - filter: vec![xfer, star, number], - includes: vec![vec![xfer, number, number], vec![xfer, xfer, number]], - excludes: vec![ - vec![number], - vec![number, number], - vec![number, number, number], - vec![number, xfer, number], - vec![xfer], - vec![number, xfer], - vec![xfer, xfer, xfer], - vec![xfer, number, xfer], - ], - }, - ] { - for topic in tc.includes { - assert!( - does_topic_match( - &topic - .iter() - .map(std::string::ToString::to_string) - .collect::>(), - &tc.filter - .iter() - .map(std::string::ToString::to_string) - .collect::>() - ), - "test: {}, topic ({:?}) should be matched by filter ({:?})", - tc.name, - topic, - tc.filter - ); - } - - for topic in tc.excludes { - assert!( - !does_topic_match( - // make deep copies of the vecs - &topic - .iter() - .map(std::string::ToString::to_string) - .collect::>(), - &tc.filter - .iter() - .map(std::string::ToString::to_string) - .collect::>() - ), - "test: {}, topic ({:?}) should NOT be matched by filter ({:?})", - tc.name, - topic, - tc.filter - ); - } - } - } -} diff --git a/cmd/soroban-cli/src/rpc/txn.rs b/cmd/soroban-cli/src/rpc/txn.rs deleted file mode 100644 index 9e36938d..00000000 --- a/cmd/soroban-cli/src/rpc/txn.rs +++ /dev/null @@ -1,610 +0,0 @@ -use ed25519_dalek::Signer; -use sha2::{Digest, Sha256}; -use soroban_env_host::xdr::{ - self, AccountId, DecoratedSignature, ExtensionPoint, Hash, HashIdPreimage, - HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Memo, Operation, - OperationBody, Preconditions, PublicKey, ReadXdr, RestoreFootprintOp, ScAddress, ScMap, - ScSymbol, ScVal, Signature, SignatureHint, SorobanAddressCredentials, - SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanCredentials, SorobanResources, - SorobanTransactionData, Transaction, TransactionEnvelope, TransactionExt, - TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, - TransactionV1Envelope, Uint256, VecM, WriteXdr, -}; - -use crate::rpc::{Client, Error, RestorePreamble, SimulateTransactionResponse}; - -use super::{LogEvents, LogResources}; - -pub struct Assembled { - txn: Transaction, - sim_res: SimulateTransactionResponse, -} - -impl Assembled { - pub async fn new(txn: &Transaction, client: &Client) -> Result { - let sim_res = Self::simulate(txn, client).await?; - let txn = assemble(txn, &sim_res)?; - Ok(Self { txn, sim_res }) - } - - pub fn hash(&self, network_passphrase: &str) -> Result<[u8; 32], xdr::Error> { - let signature_payload = TransactionSignaturePayload { - network_id: Hash(Sha256::digest(network_passphrase).into()), - tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(self.txn.clone()), - }; - Ok(Sha256::digest(signature_payload.to_xdr(Limits::none())?).into()) - } - - pub fn sign( - self, - key: &ed25519_dalek::SigningKey, - network_passphrase: &str, - ) -> Result { - let tx = self.txn(); - let tx_hash = self.hash(network_passphrase)?; - let tx_signature = key.sign(&tx_hash); - - let decorated_signature = DecoratedSignature { - hint: SignatureHint(key.verifying_key().to_bytes()[28..].try_into()?), - signature: Signature(tx_signature.to_bytes().try_into()?), - }; - - Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: vec![decorated_signature].try_into()?, - })) - } - - pub async fn simulate( - tx: &Transaction, - client: &Client, - ) -> Result { - client - .simulate_transaction(&TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: VecM::default(), - })) - .await - } - - pub async fn handle_restore( - self, - client: &Client, - source_key: &ed25519_dalek::SigningKey, - network_passphrase: &str, - ) -> Result { - if let Some(restore_preamble) = &self.sim_res.restore_preamble { - // Build and submit the restore transaction - client - .send_transaction( - &Assembled::new(&restore(self.txn(), restore_preamble)?, client) - .await? - .sign(source_key, network_passphrase)?, - ) - .await?; - Ok(self.bump_seq_num()) - } else { - Ok(self) - } - } - - pub fn txn(&self) -> &Transaction { - &self.txn - } - - pub fn sim_res(&self) -> &SimulateTransactionResponse { - &self.sim_res - } - - pub async fn authorize( - self, - client: &Client, - source_key: &ed25519_dalek::SigningKey, - signers: &[ed25519_dalek::SigningKey], - seq_num: u32, - network_passphrase: &str, - ) -> Result { - if let Some(txn) = sign_soroban_authorizations( - self.txn(), - source_key, - signers, - seq_num, - network_passphrase, - )? { - Self::new(&txn, client).await - } else { - Ok(self) - } - } - - pub fn bump_seq_num(mut self) -> Self { - self.txn.seq_num.0 += 1; - self - } - - pub fn auth(&self) -> VecM { - self.txn - .operations - .first() - .and_then(|op| match op.body { - OperationBody::InvokeHostFunction(ref body) => (matches!( - body.auth.first().map(|x| &x.root_invocation.function), - Some(&SorobanAuthorizedFunction::ContractFn(_)) - )) - .then_some(body.auth.clone()), - _ => None, - }) - .unwrap_or_default() - } - - pub fn log( - &self, - log_events: Option, - log_resources: Option, - ) -> Result<(), Error> { - if let TransactionExt::V1(SorobanTransactionData { - resources: resources @ SorobanResources { footprint, .. }, - .. - }) = &self.txn.ext - { - if let Some(log) = log_resources { - log(resources); - } - if let Some(log) = log_events { - log(footprint, &[self.auth()], &self.sim_res.events()?); - }; - } - Ok(()) - } -} - -// Apply the result of a simulateTransaction onto a transaction envelope, preparing it for -// submission to the network. -pub fn assemble( - raw: &Transaction, - simulation: &SimulateTransactionResponse, -) -> Result { - let mut tx = raw.clone(); - - // Right now simulate.results is one-result-per-function, and assumes there is only one - // operation in the txn, so we need to enforce that here. I (Paul) think that is a bug - // in soroban-rpc.simulateTransaction design, and we should fix it there. - // TODO: We should to better handling so non-soroban txns can be a passthrough here. - if tx.operations.len() != 1 { - return Err(Error::UnexpectedOperationCount { - count: tx.operations.len(), - }); - } - - let transaction_data = simulation.transaction_data()?; - - let mut op = tx.operations[0].clone(); - if let OperationBody::InvokeHostFunction(ref mut body) = &mut op.body { - if body.auth.is_empty() { - if simulation.results.len() != 1 { - return Err(Error::UnexpectedSimulateTransactionResultSize { - length: simulation.results.len(), - }); - } - - let auths = simulation - .results - .iter() - .map(|r| { - VecM::try_from( - r.auth - .iter() - .map(|v| SorobanAuthorizationEntry::from_xdr_base64(v, Limits::none())) - .collect::, _>>()?, - ) - }) - .collect::, _>>()?; - if !auths.is_empty() { - body.auth = auths[0].clone(); - } - } - } - - // update the fees of the actual transaction to meet the minimum resource fees. - let classic_transaction_fees = crate::fee::Args::default().fee; - // Pad the fees up by 15% for a bit of wiggle room. - tx.fee = (tx.fee.max( - classic_transaction_fees - + u32::try_from(simulation.min_resource_fee) - .map_err(|_| Error::LargeFee(simulation.min_resource_fee))?, - ) * 115) - / 100; - - tx.operations = vec![op].try_into()?; - tx.ext = TransactionExt::V1(transaction_data); - Ok(tx) -} - -// Use the given source_key and signers, to sign all SorobanAuthorizationEntry's in the given -// transaction. If unable to sign, return an error. -fn sign_soroban_authorizations( - raw: &Transaction, - source_key: &ed25519_dalek::SigningKey, - signers: &[ed25519_dalek::SigningKey], - signature_expiration_ledger: u32, - network_passphrase: &str, -) -> Result, Error> { - let mut tx = raw.clone(); - let mut op = match tx.operations.as_slice() { - [op @ Operation { - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { auth, .. }), - .. - }] if matches!( - auth.first().map(|x| &x.root_invocation.function), - Some(&SorobanAuthorizedFunction::ContractFn(_)) - ) => - { - op.clone() - } - _ => return Ok(None), - }; - - let Operation { - body: OperationBody::InvokeHostFunction(ref mut body), - .. - } = op - else { - return Ok(None); - }; - - let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); - - let verification_key = source_key.verifying_key(); - let source_address = verification_key.as_bytes(); - - let signed_auths = body - .auth - .as_slice() - .iter() - .map(|raw_auth| { - let mut auth = raw_auth.clone(); - let SorobanAuthorizationEntry { - credentials: SorobanCredentials::Address(ref mut credentials), - .. - } = auth - else { - // Doesn't need special signing - return Ok(auth); - }; - let SorobanAddressCredentials { ref address, .. } = credentials; - - // See if we have a signer for this authorizationEntry - // If not, then we Error - let needle = match address { - ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(ref a)))) => a, - ScAddress::Contract(Hash(c)) => { - // This address is for a contract. This means we're using a custom - // smart-contract account. Currently the CLI doesn't support that yet. - return Err(Error::MissingSignerForAddress { - address: stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*c)) - .to_string(), - }); - } - }; - let signer = if let Some(s) = signers - .iter() - .find(|s| needle == s.verifying_key().as_bytes()) - { - s - } else if needle == source_address { - // This is the source address, so we can sign it - source_key - } else { - // We don't have a signer for this address - return Err(Error::MissingSignerForAddress { - address: stellar_strkey::Strkey::PublicKeyEd25519( - stellar_strkey::ed25519::PublicKey(*needle), - ) - .to_string(), - }); - }; - - sign_soroban_authorization_entry( - raw_auth, - signer, - signature_expiration_ledger, - &network_id, - ) - }) - .collect::, Error>>()?; - - body.auth = signed_auths.try_into()?; - tx.operations = vec![op].try_into()?; - Ok(Some(tx)) -} - -fn sign_soroban_authorization_entry( - raw: &SorobanAuthorizationEntry, - signer: &ed25519_dalek::SigningKey, - signature_expiration_ledger: u32, - network_id: &Hash, -) -> Result { - let mut auth = raw.clone(); - let SorobanAuthorizationEntry { - credentials: SorobanCredentials::Address(ref mut credentials), - .. - } = auth - else { - // Doesn't need special signing - return Ok(auth); - }; - let SorobanAddressCredentials { nonce, .. } = credentials; - - let preimage = HashIdPreimage::SorobanAuthorization(HashIdPreimageSorobanAuthorization { - network_id: network_id.clone(), - invocation: auth.root_invocation.clone(), - nonce: *nonce, - signature_expiration_ledger, - }) - .to_xdr(Limits::none())?; - - let payload = Sha256::digest(preimage); - let signature = signer.sign(&payload); - - let map = ScMap::sorted_from(vec![ - ( - ScVal::Symbol(ScSymbol("public_key".try_into()?)), - ScVal::Bytes( - signer - .verifying_key() - .to_bytes() - .to_vec() - .try_into() - .map_err(Error::Xdr)?, - ), - ), - ( - ScVal::Symbol(ScSymbol("signature".try_into()?)), - ScVal::Bytes( - signature - .to_bytes() - .to_vec() - .try_into() - .map_err(Error::Xdr)?, - ), - ), - ]) - .map_err(Error::Xdr)?; - credentials.signature = ScVal::Vec(Some( - vec![ScVal::Map(Some(map))].try_into().map_err(Error::Xdr)?, - )); - credentials.signature_expiration_ledger = signature_expiration_ledger; - auth.credentials = SorobanCredentials::Address(credentials.clone()); - Ok(auth) -} - -pub fn restore(parent: &Transaction, restore: &RestorePreamble) -> Result { - let transaction_data = - SorobanTransactionData::from_xdr_base64(&restore.transaction_data, Limits::none())?; - let fee = u32::try_from(restore.min_resource_fee) - .map_err(|_| Error::LargeFee(restore.min_resource_fee))?; - Ok(Transaction { - source_account: parent.source_account.clone(), - fee: parent - .fee - .checked_add(fee) - .ok_or(Error::LargeFee(restore.min_resource_fee))?, - seq_num: parent.seq_num.clone(), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![Operation { - source_account: None, - body: OperationBody::RestoreFootprint(RestoreFootprintOp { - ext: ExtensionPoint::V0, - }), - }] - .try_into() - .unwrap(), - ext: TransactionExt::V1(transaction_data), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - use super::super::SimulateHostFunctionResultRaw; - use soroban_env_host::xdr::{ - self, AccountId, ChangeTrustAsset, ChangeTrustOp, ExtensionPoint, Hash, HostFunction, - InvokeContractArgs, InvokeHostFunctionOp, LedgerFootprint, Memo, MuxedAccount, Operation, - Preconditions, PublicKey, ScAddress, ScSymbol, ScVal, SequenceNumber, - SorobanAuthorizedFunction, SorobanAuthorizedInvocation, SorobanResources, - SorobanTransactionData, Uint256, WriteXdr, - }; - use stellar_strkey::ed25519::PublicKey as Ed25519PublicKey; - - const SOURCE: &str = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"; - - fn transaction_data() -> SorobanTransactionData { - SorobanTransactionData { - resources: SorobanResources { - footprint: LedgerFootprint { - read_only: VecM::default(), - read_write: VecM::default(), - }, - instructions: 0, - read_bytes: 5, - write_bytes: 0, - }, - resource_fee: 0, - ext: ExtensionPoint::V0, - } - } - - fn simulation_response() -> SimulateTransactionResponse { - let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; - let fn_auth = &SorobanAuthorizationEntry { - credentials: xdr::SorobanCredentials::Address(xdr::SorobanAddressCredentials { - address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( - source_bytes, - )))), - nonce: 0, - signature_expiration_ledger: 0, - signature: ScVal::Void, - }), - root_invocation: SorobanAuthorizedInvocation { - function: SorobanAuthorizedFunction::ContractFn(InvokeContractArgs { - contract_address: ScAddress::Contract(Hash([0; 32])), - function_name: ScSymbol("fn".try_into().unwrap()), - args: VecM::default(), - }), - sub_invocations: VecM::default(), - }, - }; - - SimulateTransactionResponse { - min_resource_fee: 115, - latest_ledger: 3, - results: vec![SimulateHostFunctionResultRaw { - auth: vec![fn_auth.to_xdr_base64(Limits::none()).unwrap()], - xdr: ScVal::U32(0).to_xdr_base64(Limits::none()).unwrap(), - }], - transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), - ..Default::default() - } - } - - fn single_contract_fn_transaction() -> Transaction { - let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; - Transaction { - source_account: MuxedAccount::Ed25519(Uint256(source_bytes)), - fee: 100, - seq_num: SequenceNumber(0), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::InvokeContract(InvokeContractArgs { - contract_address: ScAddress::Contract(Hash([0x0; 32])), - function_name: ScSymbol::default(), - args: VecM::default(), - }), - auth: VecM::default(), - }), - }] - .try_into() - .unwrap(), - ext: TransactionExt::V0, - } - } - - #[test] - fn test_assemble_transaction_updates_tx_data_from_simulation_response() { - let sim = simulation_response(); - let txn = single_contract_fn_transaction(); - let Ok(result) = assemble(&txn, &sim) else { - panic!("assemble failed"); - }; - - // validate it auto updated the tx fees from sim response fees - // since it was greater than tx.fee - assert_eq!(247, result.fee); - - // validate it updated sorobantransactiondata block in the tx ext - assert_eq!(TransactionExt::V1(transaction_data()), result.ext); - } - - #[test] - fn test_assemble_transaction_adds_the_auth_to_the_host_function() { - let sim = simulation_response(); - let txn = single_contract_fn_transaction(); - let Ok(result) = assemble(&txn, &sim) else { - panic!("assemble failed"); - }; - - assert_eq!(1, result.operations.len()); - let OperationBody::InvokeHostFunction(ref op) = result.operations[0].body else { - panic!("unexpected operation type: {:#?}", result.operations[0]); - }; - - assert_eq!(1, op.auth.len()); - let auth = &op.auth[0]; - - let xdr::SorobanAuthorizedFunction::ContractFn(xdr::InvokeContractArgs { - ref function_name, - .. - }) = auth.root_invocation.function - else { - panic!("unexpected function type"); - }; - assert_eq!("fn".to_string(), format!("{}", function_name.0)); - - let xdr::SorobanCredentials::Address(xdr::SorobanAddressCredentials { - address: - xdr::ScAddress::Account(xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(address))), - .. - }) = &auth.credentials - else { - panic!("unexpected credentials type"); - }; - assert_eq!( - SOURCE.to_string(), - stellar_strkey::ed25519::PublicKey(address.0).to_string() - ); - } - - #[test] - fn test_assemble_transaction_errors_for_non_invokehostfn_ops() { - let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; - let txn = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(source_bytes)), - fee: 100, - seq_num: SequenceNumber(0), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![Operation { - source_account: None, - body: OperationBody::ChangeTrust(ChangeTrustOp { - line: ChangeTrustAsset::Native, - limit: 0, - }), - }] - .try_into() - .unwrap(), - ext: TransactionExt::V0, - }; - - let result = assemble( - &txn, - &SimulateTransactionResponse { - min_resource_fee: 115, - transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), - latest_ledger: 3, - ..Default::default() - }, - ); - - match result { - Ok(_) => {} - Err(e) => panic!("expected assembled operation, got: {e:#?}"), - } - } - - #[test] - fn test_assemble_transaction_errors_for_errors_for_mismatched_simulation() { - let txn = single_contract_fn_transaction(); - - let result = assemble( - &txn, - &SimulateTransactionResponse { - min_resource_fee: 115, - transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), - latest_ledger: 3, - ..Default::default() - }, - ); - - match result { - Err(Error::UnexpectedSimulateTransactionResultSize { length }) => { - assert_eq!(0, length); - } - r => panic!("expected UnexpectedSimulateTransactionResultSize error, got: {r:#?}"), - } - } -} diff --git a/cmd/soroban-cli/src/toid.rs b/cmd/soroban-cli/src/toid.rs deleted file mode 100644 index 55c89049..00000000 --- a/cmd/soroban-cli/src/toid.rs +++ /dev/null @@ -1,69 +0,0 @@ -/// A barebones implementation of Total Order IDs (TOIDs) from -/// [SEP-35](https://stellar.org/protocol/sep-35), using the reference -/// implementation from the Go -/// [`stellar/go/toid`](https://github.com/stellar/go/blob/b4ba6f8e67f274bf84d21b0effb01ea8a914b766/toid/main.go#L8-L56) -/// package. -#[derive(Copy, Clone)] -pub struct Toid { - ledger_sequence: u32, - transaction_order: u32, - operation_order: u32, -} - -const LEDGER_MASK: u64 = (1 << 32) - 1; -const TRANSACTION_MASK: u64 = (1 << 20) - 1; -const OPERATION_MASK: u64 = (1 << 12) - 1; -const LEDGER_SHIFT: u64 = 32; -const TRANSACTION_SHIFT: u64 = 12; -const OPERATION_SHIFT: u64 = 0; - -impl Toid { - pub fn new(ledger: u32, tx_order: u32, op_order: u32) -> Toid { - Toid { - ledger_sequence: ledger, - transaction_order: tx_order, - operation_order: op_order, - } - } - - pub fn to_paging_token(self) -> String { - let u: u64 = self.into(); - format!("{u:019}") - } -} - -impl From for Toid { - fn from(item: u64) -> Self { - let ledger: u32 = ((item & LEDGER_MASK) >> LEDGER_SHIFT).try_into().unwrap(); - let tx_order: u32 = ((item & TRANSACTION_MASK) >> TRANSACTION_SHIFT) - .try_into() - .unwrap(); - let op_order: u32 = ((item & OPERATION_MASK) >> OPERATION_SHIFT) - .try_into() - .unwrap(); - - Toid::new(ledger, tx_order, op_order) - } -} - -impl From for u64 { - fn from(item: Toid) -> Self { - let l: u64 = item.ledger_sequence.into(); - let t: u64 = item.transaction_order.into(); - let o: u64 = item.operation_order.into(); - - let mut result: u64 = 0; - result |= (l & LEDGER_MASK) << LEDGER_SHIFT; - result |= (t & TRANSACTION_MASK) << TRANSACTION_SHIFT; - result |= (o & OPERATION_MASK) << OPERATION_SHIFT; - - result - } -} - -impl ToString for Toid { - fn to_string(&self) -> String { - let u: u64 = (*self).into(); - u.to_string() - } -} diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs deleted file mode 100644 index ff0018a9..00000000 --- a/cmd/soroban-cli/src/utils.rs +++ /dev/null @@ -1,244 +0,0 @@ -use ed25519_dalek::Signer; -use sha2::{Digest, Sha256}; -use stellar_strkey::ed25519::PrivateKey; - -use soroban_env_host::xdr::{ - Asset, ContractIdPreimage, DecoratedSignature, Error as XdrError, Hash, HashIdPreimage, - HashIdPreimageContractId, Limits, Signature, SignatureHint, Transaction, TransactionEnvelope, - TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, - TransactionV1Envelope, WriteXdr, -}; - -pub mod contract_spec; - -/// # Errors -/// -/// Might return an error -pub fn contract_hash(contract: &[u8]) -> Result { - Ok(Hash(Sha256::digest(contract).into())) -} - -/// # Errors -/// -/// Might return an error -pub fn transaction_hash(tx: &Transaction, network_passphrase: &str) -> Result<[u8; 32], XdrError> { - let signature_payload = TransactionSignaturePayload { - network_id: Hash(Sha256::digest(network_passphrase).into()), - tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(tx.clone()), - }; - Ok(Sha256::digest(signature_payload.to_xdr(Limits::none())?).into()) -} - -/// # Errors -/// -/// Might return an error -pub fn sign_transaction( - key: &ed25519_dalek::SigningKey, - tx: &Transaction, - network_passphrase: &str, -) -> Result { - let tx_hash = transaction_hash(tx, network_passphrase)?; - let tx_signature = key.sign(&tx_hash); - - let decorated_signature = DecoratedSignature { - hint: SignatureHint(key.verifying_key().to_bytes()[28..].try_into()?), - signature: Signature(tx_signature.to_bytes().try_into()?), - }; - - Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: vec![decorated_signature].try_into()?, - })) -} - -/// # Errors -/// -/// Might return an error -pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strkey::DecodeError> { - stellar_strkey::Contract::from_string(contract_id) - .map(|strkey| strkey.0) - .or_else(|_| { - // strkey failed, try to parse it as a hex string, for backwards compatibility. - soroban_spec_tools::utils::padded_hex_from_str(contract_id, 32) - .map_err(|_| stellar_strkey::DecodeError::Invalid)? - .try_into() - .map_err(|_| stellar_strkey::DecodeError::Invalid) - }) - .map_err(|_| stellar_strkey::DecodeError::Invalid) -} - -/// # Errors -/// May not find a config dir -pub fn find_config_dir(mut pwd: std::path::PathBuf) -> std::io::Result { - let soroban_dir = |p: &std::path::Path| p.join(".soroban"); - while !soroban_dir(&pwd).exists() { - if !pwd.pop() { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "soroban directory not found", - )); - } - } - Ok(soroban_dir(&pwd)) -} - -pub(crate) fn into_signing_key(key: &PrivateKey) -> ed25519_dalek::SigningKey { - let secret: ed25519_dalek::SecretKey = key.0; - ed25519_dalek::SigningKey::from_bytes(&secret) -} - -/// Used in tests -#[allow(unused)] -pub(crate) fn parse_secret_key( - s: &str, -) -> Result { - Ok(into_signing_key(&PrivateKey::from_string(s)?)) -} - -pub fn is_hex_string(s: &str) -> bool { - s.chars().all(|s| s.is_ascii_hexdigit()) -} - -pub fn contract_id_hash_from_asset( - asset: &Asset, - network_passphrase: &str, -) -> Result { - let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); - let preimage = HashIdPreimage::ContractId(HashIdPreimageContractId { - network_id, - contract_id_preimage: ContractIdPreimage::Asset(asset.clone()), - }); - let preimage_xdr = preimage.to_xdr(Limits::none())?; - Ok(Hash(Sha256::digest(preimage_xdr).into())) -} - -pub mod parsing { - - use regex::Regex; - use soroban_env_host::xdr::{ - AccountId, AlphaNum12, AlphaNum4, Asset, AssetCode12, AssetCode4, PublicKey, - }; - - #[derive(thiserror::Error, Debug)] - pub enum Error { - #[error("invalid asset code: {asset}")] - InvalidAssetCode { asset: String }, - #[error("cannot parse account id: {account_id}")] - CannotParseAccountId { account_id: String }, - #[error("cannot parse asset: {asset}")] - CannotParseAsset { asset: String }, - #[error(transparent)] - Regex(#[from] regex::Error), - } - - pub fn parse_asset(str: &str) -> Result { - if str == "native" { - return Ok(Asset::Native); - } - let split: Vec<&str> = str.splitn(2, ':').collect(); - if split.len() != 2 { - return Err(Error::CannotParseAsset { - asset: str.to_string(), - }); - } - let code = split[0]; - let issuer = split[1]; - let re = Regex::new("^[[:alnum:]]{1,12}$")?; - if !re.is_match(code) { - return Err(Error::InvalidAssetCode { - asset: str.to_string(), - }); - } - if code.len() <= 4 { - let mut asset_code: [u8; 4] = [0; 4]; - for (i, b) in code.as_bytes().iter().enumerate() { - asset_code[i] = *b; - } - Ok(Asset::CreditAlphanum4(AlphaNum4 { - asset_code: AssetCode4(asset_code), - issuer: parse_account_id(issuer)?, - })) - } else { - let mut asset_code: [u8; 12] = [0; 12]; - for (i, b) in code.as_bytes().iter().enumerate() { - asset_code[i] = *b; - } - Ok(Asset::CreditAlphanum12(AlphaNum12 { - asset_code: AssetCode12(asset_code), - issuer: parse_account_id(issuer)?, - })) - } - } - - pub fn parse_account_id(str: &str) -> Result { - let pk_bytes = stellar_strkey::ed25519::PublicKey::from_string(str) - .map_err(|_| Error::CannotParseAccountId { - account_id: str.to_string(), - })? - .0; - Ok(AccountId(PublicKey::PublicKeyTypeEd25519(pk_bytes.into()))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_contract_id_from_str() { - // strkey - match contract_id_from_str("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") { - Ok(contract_id) => assert_eq!( - contract_id, - [ - 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7, - 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c, - 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, - ] - ), - Err(err) => panic!("Failed to parse contract id: {err}"), - } - - // hex - match contract_id_from_str( - "363eaa3867841fbad0f4ed88c779e4fe66e56a2470dc98c0ec9c073d05c7b103", - ) { - Ok(contract_id) => assert_eq!( - contract_id, - [ - 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7, - 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c, - 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03, - ] - ), - Err(err) => panic!("Failed to parse contract id: {err}"), - } - - // unpadded-hex - match contract_id_from_str("1") { - Ok(contract_id) => assert_eq!( - contract_id, - [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - ] - ), - Err(err) => panic!("Failed to parse contract id: {err}"), - } - - // invalid hex - match contract_id_from_str("foobar") { - Ok(_) => panic!("Expected parsing to fail"), - Err(err) => assert_eq!(err, stellar_strkey::DecodeError::Invalid), - } - - // hex too long (33 bytes) - match contract_id_from_str( - "000000000000000000000000000000000000000000000000000000000000000000", - ) { - Ok(_) => panic!("Expected parsing to fail"), - Err(err) => assert_eq!(err, stellar_strkey::DecodeError::Invalid), - } - } -} diff --git a/cmd/soroban-cli/src/utils/contract_spec.rs b/cmd/soroban-cli/src/utils/contract_spec.rs deleted file mode 100644 index b4f24abe..00000000 --- a/cmd/soroban-cli/src/utils/contract_spec.rs +++ /dev/null @@ -1,276 +0,0 @@ -use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use std::{ - fmt::Display, - io::{self, Cursor}, -}; - -use soroban_env_host::xdr::{ - self, Limited, Limits, ReadXdr, ScEnvMetaEntry, ScMetaEntry, ScMetaV0, ScSpecEntry, - ScSpecFunctionV0, ScSpecUdtEnumV0, ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionV0, - StringM, WriteXdr, -}; - -pub struct ContractSpec { - pub env_meta_base64: Option, - pub env_meta: Vec, - pub meta_base64: Option, - pub meta: Vec, - pub spec_base64: Option, - pub spec: Vec, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("reading file {filepath}: {error}")] - CannotReadContractFile { - filepath: std::path::PathBuf, - error: io::Error, - }, - #[error("cannot parse wasm file {file}: {error}")] - CannotParseWasm { - file: std::path::PathBuf, - error: wasmparser::BinaryReaderError, - }, - #[error("xdr processing error: {0}")] - Xdr(#[from] xdr::Error), - - #[error(transparent)] - Parser(#[from] wasmparser::BinaryReaderError), -} - -impl ContractSpec { - pub fn new(bytes: &[u8]) -> Result { - let mut env_meta: Option<&[u8]> = None; - let mut meta: Option<&[u8]> = None; - let mut spec: Option<&[u8]> = None; - for payload in wasmparser::Parser::new(0).parse_all(bytes) { - let payload = payload?; - if let wasmparser::Payload::CustomSection(section) = payload { - let out = match section.name() { - "contractenvmetav0" => &mut env_meta, - "contractmetav0" => &mut meta, - "contractspecv0" => &mut spec, - _ => continue, - }; - *out = Some(section.data()); - }; - } - - let mut env_meta_base64 = None; - let env_meta = if let Some(env_meta) = env_meta { - env_meta_base64 = Some(base64.encode(env_meta)); - let cursor = Cursor::new(env_meta); - let mut read = Limited::new(cursor, Limits::none()); - ScEnvMetaEntry::read_xdr_iter(&mut read).collect::, xdr::Error>>()? - } else { - vec![] - }; - - let mut meta_base64 = None; - let meta = if let Some(meta) = meta { - meta_base64 = Some(base64.encode(meta)); - let cursor = Cursor::new(meta); - let mut depth_limit_read = Limited::new(cursor, Limits::none()); - ScMetaEntry::read_xdr_iter(&mut depth_limit_read) - .collect::, xdr::Error>>()? - } else { - vec![] - }; - - let mut spec_base64 = None; - let spec = if let Some(spec) = spec { - spec_base64 = Some(base64.encode(spec)); - let cursor = Cursor::new(spec); - let mut read = Limited::new(cursor, Limits::none()); - ScSpecEntry::read_xdr_iter(&mut read).collect::, xdr::Error>>()? - } else { - vec![] - }; - - Ok(ContractSpec { - env_meta_base64, - env_meta, - meta_base64, - meta, - spec_base64, - spec, - }) - } - - pub fn spec_as_json_array(&self) -> Result { - let spec = self - .spec - .iter() - .map(|e| Ok(format!("\"{}\"", e.to_xdr_base64(Limits::none())?))) - .collect::, Error>>()? - .join(",\n"); - Ok(format!("[{spec}]")) - } -} - -impl Display for ContractSpec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(env_meta) = &self.env_meta_base64 { - writeln!(f, "Env Meta: {env_meta}")?; - for env_meta_entry in &self.env_meta { - match env_meta_entry { - ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) => { - writeln!(f, " • Interface Version: {v}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Env Meta: None\n")?; - } - - if let Some(_meta) = &self.meta_base64 { - writeln!(f, "Contract Meta:")?; - for meta_entry in &self.meta { - match meta_entry { - ScMetaEntry::ScMetaV0(ScMetaV0 { key, val }) => { - writeln!(f, " • {key}: {val}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Contract Meta: None\n")?; - } - - if let Some(_spec_base64) = &self.spec_base64 { - writeln!(f, "Contract Spec:")?; - for spec_entry in &self.spec { - match spec_entry { - ScSpecEntry::FunctionV0(func) => write_func(f, func)?, - ScSpecEntry::UdtUnionV0(udt) => write_union(f, udt)?, - ScSpecEntry::UdtStructV0(udt) => write_struct(f, udt)?, - ScSpecEntry::UdtEnumV0(udt) => write_enum(f, udt)?, - ScSpecEntry::UdtErrorEnumV0(udt) => write_error(f, udt)?, - } - } - } else { - writeln!(f, "Contract Spec: None")?; - } - Ok(()) - } -} - -fn write_func(f: &mut std::fmt::Formatter<'_>, func: &ScSpecFunctionV0) -> std::fmt::Result { - writeln!(f, " • Function: {}", func.name.to_utf8_string_lossy())?; - if func.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - &indent(&func.doc.to_utf8_string_lossy(), 11).trim() - )?; - } - writeln!( - f, - " Inputs: {}", - indent(&format!("{:#?}", func.inputs), 5).trim() - )?; - writeln!( - f, - " Output: {}", - indent(&format!("{:#?}", func.outputs), 5).trim() - )?; - writeln!(f)?; - Ok(()) -} - -fn write_union(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtUnionV0) -> std::fmt::Result { - writeln!(f, " • Union: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_struct(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtStructV0) -> std::fmt::Result { - writeln!(f, " • Struct: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Fields:")?; - for field in udt.fields.iter() { - writeln!( - f, - " • {}: {}", - field.name.to_utf8_string_lossy(), - indent(&format!("{:#?}", field.type_), 8).trim() - )?; - if field.doc.len() > 0 { - writeln!(f, "{}", indent(&format!("{:#?}", field.doc), 8))?; - } - } - writeln!(f)?; - Ok(()) -} - -fn write_enum(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtEnumV0) -> std::fmt::Result { - writeln!(f, " • Enum: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_error(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtErrorEnumV0) -> std::fmt::Result { - writeln!(f, " • Error: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn indent(s: &str, n: usize) -> String { - let pad = " ".repeat(n); - s.lines() - .map(|line| format!("{pad}{line}")) - .collect::>() - .join("\n") -} - -fn format_name(lib: &StringM<80>, name: &StringM<60>) -> String { - if lib.len() > 0 { - format!( - "{}::{}", - lib.to_utf8_string_lossy(), - name.to_utf8_string_lossy() - ) - } else { - name.to_utf8_string_lossy() - } -} diff --git a/cmd/soroban-cli/src/wasm.rs b/cmd/soroban-cli/src/wasm.rs deleted file mode 100644 index fce44c7c..00000000 --- a/cmd/soroban-cli/src/wasm.rs +++ /dev/null @@ -1,93 +0,0 @@ -use clap::arg; -use soroban_env_host::xdr::{self, LedgerKey, LedgerKeyContractCode}; -use std::{ - fs, io, - path::{Path, PathBuf}, -}; - -use crate::utils::{self, contract_spec::ContractSpec}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("reading file {filepath}: {error}")] - CannotReadContractFile { - filepath: std::path::PathBuf, - error: io::Error, - }, - #[error("cannot parse wasm file {file}: {error}")] - CannotParseWasm { - file: std::path::PathBuf, - error: wasmparser::BinaryReaderError, - }, - #[error("xdr processing error: {0}")] - Xdr(#[from] xdr::Error), - - #[error(transparent)] - Parser(#[from] wasmparser::BinaryReaderError), - #[error(transparent)] - ContractSpec(#[from] crate::utils::contract_spec::Error), -} - -#[derive(Debug, clap::Args, Clone)] -#[group(skip)] -pub struct Args { - /// Path to wasm binary - #[arg(long)] - pub wasm: PathBuf, -} - -impl Args { - /// # Errors - /// May fail to read wasm file - pub fn read(&self) -> Result, Error> { - fs::read(&self.wasm).map_err(|e| Error::CannotReadContractFile { - filepath: self.wasm.clone(), - error: e, - }) - } - - /// # Errors - /// May fail to read wasm file - pub fn len(&self) -> Result { - len(&self.wasm) - } - - /// # Errors - /// May fail to read wasm file - pub fn is_empty(&self) -> Result { - self.len().map(|len| len == 0) - } - - /// # Errors - /// May fail to read wasm file or parse xdr section - pub fn parse(&self) -> Result { - let contents = self.read()?; - Ok(ContractSpec::new(&contents)?) - } -} - -impl From<&PathBuf> for Args { - fn from(wasm: &PathBuf) -> Self { - Self { wasm: wasm.clone() } - } -} - -impl TryInto for Args { - type Error = Error; - fn try_into(self) -> Result { - Ok(LedgerKey::ContractCode(LedgerKeyContractCode { - hash: utils::contract_hash(&self.read()?)?, - })) - } -} - -/// # Errors -/// May fail to read wasm file -pub fn len(p: &Path) -> Result { - Ok(std::fs::metadata(p) - .map_err(|e| Error::CannotReadContractFile { - filepath: p.to_path_buf(), - error: e, - })? - .len()) -} diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index 997372ed..db138c86 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -277,11 +277,11 @@ func runSuccessfulCLICmd(t *testing.T, cmd string) string { } func runCLICommand(t *testing.T, cmd string) *icmd.Result { - args := []string{"run", "-q", "--", "--vv"} + args := []string{"-q", "--vv"} parsedArgs, err := shlex.Split(cmd) require.NoError(t, err, cmd) args = append(args, parsedArgs...) - c := icmd.Command("cargo", args...) + c := icmd.Command("soroban", args...) c.Env = append(os.Environ(), fmt.Sprintf("SOROBAN_RPC_URL=http://localhost:%d/", sorobanRPCPort), fmt.Sprintf("SOROBAN_NETWORK_PASSPHRASE=%s", StandaloneNetworkPassphrase),