diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml new file mode 100644 index 000000000..3fb8c407e --- /dev/null +++ b/.github/workflows/bindings-ts.yml @@ -0,0 +1,40 @@ +name: bindings typescript + +on: [push] + +jobs: + test: + name: test generated libraries + runs-on: ubuntu-20.04 + services: + rpc: + image: stellar/quickstart:soroban-dev@sha256:a6b03cf6b0433c99f2f799b719f0faadbb79684b1b763e7674ba749fb0f648ee + ports: + - 8000:8000 + env: + ENABLE_LOGS: true + NETWORK: standalone + ENABLE_SOROBAN_RPC: true + options: >- + --health-cmd "curl -X POST \"http://localhost:8000/soroban/rpc\"" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - run: echo $CARGO_TARGET_DIR + - 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/.gitignore b/.gitignore index 3c608cc13..00ec6c1d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ target/ .soroban/ !test.toml - -cmd/crates/soroban-spec-typescript/fixtures/ts/package-lock.json diff --git a/Cargo.lock b/Cargo.lock index 9b3313282..db3c864e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,6 +741,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "downcast-rs" version = "1.2.0" @@ -2448,6 +2454,7 @@ dependencies = [ "crate-git-revision 0.0.4", "csv", "dirs", + "dotenv", "ed25519-dalek", "ethnum", "heck", diff --git a/cmd/crates/soroban-spec-json/src/lib.rs b/cmd/crates/soroban-spec-json/src/lib.rs index 9394f33db..8626262b8 100644 --- a/cmd/crates/soroban-spec-json/src/lib.rs +++ b/cmd/crates/soroban-spec-json/src/lib.rs @@ -55,6 +55,7 @@ pub fn generate_from_wasm(wasm: &[u8]) -> Result { Ok(json) } +/// # Panics 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") 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 index bd2b1e4ef..7815e5772 100644 --- 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 @@ -182,7 +182,6 @@ class Contract { }); } catch (e) { - console.log(e); if (typeof e === 'string') { let err = parseError(e); if (err) diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/invoke.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/invoke.js index f7de5eede..5cb3264a9 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/invoke.js +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/invoke.js @@ -39,9 +39,7 @@ async function invoke({ method, args = [], fee = 100, responseType, parseResultX .addOperation(contract.call(method, ...args)) .setTimeout(SorobanClient.TimeoutInfinite) .build(); - console.log(method, args); const simulated = await server.simulateTransaction(tx); - console.log("---\n", simulated.result.retval, "\n----"); if (simulated.error) throw simulated.error; if (responseType === "simulated") @@ -87,9 +85,9 @@ async function invoke({ method, args = [], fee = 100, responseType, parseResultX if (responseType === "full") return raw; // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `getTransaction`, which has a `resultXdr` field - if ("resultXdr" in raw) - return parse(raw.resultXdr.result().toXDR("base64")); + // `getTransaction`, which has a `returnValue` field + if ("returnValue" in raw) + return parse(raw.returnValue); // otherwise, it returned the result of `sendTransaction` if ("errorResultXdr" in raw) return parse(raw.errorResultXdr); 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 index 9552e6182..9a1518303 100644 --- 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 @@ -163,7 +163,6 @@ export class Contract { }); } catch (e) { - console.log(e); if (typeof e === 'string') { let err = parseError(e); if (err) diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/invoke.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/invoke.js index 0ea2d1a4c..5e2606682 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/invoke.js +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/invoke.js @@ -35,9 +35,7 @@ export async function invoke({ method, args = [], fee = 100, responseType, parse .addOperation(contract.call(method, ...args)) .setTimeout(SorobanClient.TimeoutInfinite) .build(); - console.log(method, args); const simulated = await server.simulateTransaction(tx); - console.log("---\n", simulated.result.retval, "\n----"); if (simulated.error) throw simulated.error; if (responseType === "simulated") @@ -83,9 +81,9 @@ export async function invoke({ method, args = [], fee = 100, responseType, parse if (responseType === "full") return raw; // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `getTransaction`, which has a `resultXdr` field - if ("resultXdr" in raw) - return parse(raw.resultXdr.result().toXDR("base64")); + // `getTransaction`, which has a `returnValue` field + if ("returnValue" in raw) + return parse(raw.returnValue); // otherwise, it returned the result of `sendTransaction` if ("errorResultXdr" in raw) return parse(raw.errorResultXdr); 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 index 07186bd45..5cce69fe0 100644 --- 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 @@ -277,7 +277,6 @@ export class Contract { }, }); } catch (e) { - console.log(e) if (typeof e === 'string') { let err = parseError(e); if (err) return err; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts index 41c300144..ed42bdb54 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts @@ -165,8 +165,8 @@ export async function invoke({ if (responseType === "full") return raw; // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `getTransaction`, which has a `resultXdr` field - if ("resultXdr" in raw) return parse(raw.resultXdr.result().toXDR("base64")); + // `getTransaction`, which has a `returnValue` field + if ("returnValue" in raw) return parse(raw.returnValue); // otherwise, it returned the result of `sendTransaction` if ("errorResultXdr" in raw) return parse(raw.errorResultXdr); diff --git a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs b/cmd/crates/soroban-spec-typescript/src/boilerplate.rs index fee02b05a..829a45491 100644 --- a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs +++ b/cmd/crates/soroban-spec-typescript/src/boilerplate.rs @@ -13,7 +13,7 @@ use super::generate; static PROJECT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src/project_template"); const NETWORK_PASSPHRASE_FUTURENET: &str = "Test SDF Future Network ; October 2022"; -const NETWORK_PASSPHRASE_LOCALNET: &str = "Standalone Network ; February 2017"; +const NETWORK_PASSPHRASE_STANDALONE: &str = "Standalone Network ; February 2017"; pub struct Project(PathBuf); @@ -112,8 +112,8 @@ impl Project { fn format_networks_object(contract_id: &str, network_passphrase: &str) -> String { let network = if network_passphrase == NETWORK_PASSPHRASE_FUTURENET { "futurenet" - } else if network_passphrase == NETWORK_PASSPHRASE_LOCALNET { - "localnet" + } else if network_passphrase == NETWORK_PASSPHRASE_STANDALONE { + "standalone" } else { "unknown" }; diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/package.json b/cmd/crates/soroban-spec-typescript/src/project_template/package.json index 50f019a65..404c1e0f9 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/package.json +++ b/cmd/crates/soroban-spec-typescript/src/project_template/package.json @@ -4,7 +4,7 @@ "dependencies": { "@stellar/freighter-api": "1.5.1", "buffer": "6.0.3", - "soroban-client": "0.11.0" + "soroban-client": "0.11.1" }, "scripts": { "build": "node ./scripts/build.mjs" diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts b/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts index 41c300144..ed42bdb54 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts +++ b/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts @@ -165,8 +165,8 @@ export async function invoke({ if (responseType === "full") return raw; // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `getTransaction`, which has a `resultXdr` field - if ("resultXdr" in raw) return parse(raw.resultXdr.result().toXDR("base64")); + // `getTransaction`, which has a `returnValue` field + if ("returnValue" in raw) return parse(raw.returnValue); // otherwise, it returned the result of `sendTransaction` if ("errorResultXdr" in raw) return parse(raw.errorResultXdr); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.env b/cmd/crates/soroban-spec-typescript/ts-tests/.env new file mode 100644 index 000000000..b86805d8d --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.env @@ -0,0 +1 @@ +SOROBAN_NETWORK=standalone \ No newline at end of file diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore b/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore index a9cc241d8..0e1c46aba 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.gitignore @@ -1,3 +1,5 @@ build node_modules -yarn.lock \ No newline at end of file +yarn.lock +!.soroban/network/standalone.toml +contract-id-*.txt diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.soroban/network/standalone.toml b/cmd/crates/soroban-spec-typescript/ts-tests/.soroban/network/standalone.toml new file mode 100644 index 000000000..5ab086450 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.soroban/network/standalone.toml @@ -0,0 +1,2 @@ +rpc_url = "http://localhost:8000/soroban/rpc" +network_passphrase = "Standalone Network ; February 2017" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json index 954928b09..b803e94f3 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json @@ -4,13 +4,27 @@ "requires": true, "packages": { "": { + "hasInstallScript": true, "devDependencies": { "@ava/typescript": "^4.1.0", "@types/node": "^20.4.9", "ava": "^5.3.1", + "soroban-client": "^0.11.0", "typescript": "^5.1.6" } }, + "../fixtures/test_custom_types": { + "version": "0.0.0", + "extraneous": true, + "dependencies": { + "@stellar/freighter-api": "1.5.1", + "buffer": "6.0.3", + "soroban-client": "0.9.2" + }, + "devDependencies": { + "typescript": "5.1.6" + } + }, "node_modules/@ava/typescript": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-4.1.0.tgz", @@ -178,6 +192,12 @@ "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", @@ -243,6 +263,55 @@ } } }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "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==", + "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.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "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", @@ -270,6 +339,30 @@ "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.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.0.0.tgz", @@ -494,6 +587,18 @@ "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", @@ -589,6 +694,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "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", @@ -772,6 +886,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "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/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -847,6 +995,26 @@ "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.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -886,6 +1054,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -1009,6 +1183,12 @@ "node": ">= 0.8" } }, + "node_modules/js-xdr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/js-xdr/-/js-xdr-3.0.0.tgz", + "integrity": "sha512-tSt6UKJ2L7t+yaQURGkHo9kop9qnVbChTlCu62zNiDbDZQoZb/YjUj2iFJ3lgelhfg9p5bhO2o/QX+g36TPsSQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1150,6 +1330,27 @@ "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", @@ -1168,6 +1369,18 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "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", @@ -1406,6 +1619,12 @@ "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/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1501,6 +1720,26 @@ "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", @@ -1531,6 +1770,19 @@ "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", @@ -1592,6 +1844,30 @@ "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/soroban-client": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-0.11.0.tgz", + "integrity": "sha512-I7861W31Lruy57JaRsK9Hn78zX+VSVf9ocNoVQZBQTfYQ3fJF1tNqyeDXkmIwmBl2aljtgFOChqIl2cufh/8EA==", + "dev": true, + "dependencies": { + "axios": "^1.4.0", + "bignumber.js": "^9.1.1", + "buffer": "^6.0.3", + "stellar-base": "10.0.0-soroban.7", + "urijs": "^1.19.1" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1619,6 +1895,23 @@ "node": ">=8" } }, + "node_modules/stellar-base": { + "version": "10.0.0-soroban.7", + "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-soroban.7.tgz", + "integrity": "sha512-5+qqGsFXagtbG0L/oIiTDEIUsnerE7Vwvv/Hp2ugG0sKulcnfxgYjNqeon3Zv13uoJ4dtjslQpVOzLxPlklViQ==", + "dev": true, + "dependencies": { + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.1", + "buffer": "^6.0.3", + "js-xdr": "^3.0.0", + "sha.js": "^2.3.6", + "tweetnacl": "^1.0.3" + }, + "optionalDependencies": { + "sodium-native": "^4.0.1" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -1708,6 +2001,12 @@ "node": ">=8.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-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", @@ -1733,6 +2032,12 @@ "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==", + "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", diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package.json b/cmd/crates/soroban-spec-typescript/ts-tests/package.json index c840d9335..2cfba0cd4 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package.json @@ -2,12 +2,21 @@ "private": true, "type": "module", "scripts": { + "postinstall": "curl -X POST \"http://localhost:8000/soroban/rpc\" && npm run fund && npm run deploy && npm run bindings || { echo \"Make sure you're running standalone RPC network on localhost:8000\" && exit 1; }", + "fund": "./soroban config identity fund", + "bindings:custom-types": "./soroban contract bindings typescript --contract-id $(cat contract-id-custom-types.txt) --output-dir ./node_modules/test-custom-types --overwrite", + "bindings:hello-world": "./soroban contract bindings typescript --contract-id $(cat contract-id-hello-world.txt) --output-dir ./node_modules/test-hello-world --overwrite", + "bindings": "npm run bindings:custom-types && npm run bindings:hello-world", + "deploy:custom-types": "(./soroban contract deploy --wasm ../../../../target/wasm32-unknown-unknown/test-wasms/test_custom_types.wasm) > contract-id-custom-types.txt", + "deploy:hello-world": "(./soroban contract deploy --wasm ../../../../target/wasm32-unknown-unknown/test-wasms/test_hello_world.wasm) > contract-id-hello-world.txt", + "deploy": "npm run deploy:custom-types && npm run deploy:hello-world", "test": "ava" }, "devDependencies": { "@ava/typescript": "^4.1.0", "@types/node": "^20.4.9", "ava": "^5.3.1", + "soroban-client": "^0.11.0", "typescript": "^5.1.6" }, "ava": { diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/soroban b/cmd/crates/soroban-spec-typescript/ts-tests/soroban new file mode 100755 index 000000000..8e5e7fcd5 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/soroban @@ -0,0 +1,3 @@ +#!/bin/bash + +cargo run -p soroban-cli -- "$@" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts similarity index 89% rename from cmd/crates/soroban-spec-typescript/ts-tests/src/test.ts rename to cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts index b72168984..13c9e0712 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/test.ts +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-custom-types.ts @@ -1,24 +1,10 @@ import test from 'ava' -import { Contract, Ok, Err, networks } from '../../fixtures/test_custom_types/dist/esm/index.js' +import { publicKey, rpcUrl, wallet } from './util.js' +import { Contract, Ok, Err, networks, Address } from 'test-custom-types' -const rpcUrl = 'https://rpc-futurenet.stellar.org' -const publicKey = 'GCBVOLOM32I7OD5TWZQCIXCXML3TK56MDY7ZMTAILIBQHHKPCVU42XYW' const addr = Address.fromString(publicKey) -const contract = new Contract({ - ...networks.futurenet, - rpcUrl, - wallet: { - isConnected: () => Promise.resolve(true), - isAllowed: () => Promise.resolve(true), - getUserInfo: () => Promise.resolve({ publicKey }), - signTransaction: async (tx: string, opts?: { - network?: string, - networkPassphrase?: string, - accountToSign?: string, - }) => tx, - }, -}) +const contract = new Contract({ ...networks.standalone, rpcUrl, wallet}); test('hello', async t => { t.is(await contract.hello({ hello: 'tests' }), 'tests') @@ -28,6 +14,7 @@ test('woid', async t => { t.is(await contract.woid(), undefined) }) +// Bug in soroban client, will be fixed in next release test('u32_fail_on_even', async t => { t.deepEqual(await contract.u32FailOnEven({ u32_: 1 }), new Ok(1)) t.deepEqual(await contract.u32FailOnEven({ u32_: 0 }), new Err({ message: "Please provide an odd number" })) @@ -186,4 +173,4 @@ 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', values: undefined }] t.deepEqual(await contract.tupleStrukt({ tuple_strukt: arg }), res) -}) \ No newline at end of file +}) 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 new file mode 100644 index 000000000..7ec386f54 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-hello-world.ts @@ -0,0 +1,22 @@ +import test from "ava"; +import { wallet, publicKey, rpcUrl } from "./util.js"; +import { Address, Contract, networks } from "test-hello-world"; + +const addr = Address.fromString(publicKey); + +const contract = new Contract({...networks.standalone, rpcUrl, wallet}); + +test("hello", async (t) => { + t.deepEqual(await contract.hello({ world: "tests" }), ["Hello", "tests"]); +}); + +// Currently must run tests in serial because nonce logic not smart enough to handle concurrent calls. +test.serial("auth", async (t) => { + t.deepEqual(await contract.auth({ addr, world: 'lol' }), addr) +}); + +test.serial("inc", async (t) => { + t.is(await contract.getCount(), 0); + t.is(await contract.inc({}), 1) + t.is(await contract.getCount(), 1); +}); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts new file mode 100644 index 000000000..f8955c44e --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts @@ -0,0 +1,27 @@ +import { Keypair, TransactionBuilder } from "soroban-client"; + +export const rpcUrl = "http://localhost:8000/soroban/rpc"; +export const secretKey = + "SC36BWNUOCZAO7DMEJNNKFV6BOTPJP7IG5PSHLUOLT6DZFRU3D3XGIXW"; + +const keypair = Keypair.fromSecret(secretKey); +export const publicKey = keypair.publicKey(); +const networkPassphrase = "Standalone Network ; February 2017"; + +export const wallet = { + isConnected: () => Promise.resolve(true), + isAllowed: () => Promise.resolve(true), + getUserInfo: () => Promise.resolve({ publicKey }), + signTransaction: async ( + tx: string, + _opts?: { + network?: string; + networkPassphrase?: string; + accountToSign?: string; + } + ) => { + const t = TransactionBuilder.fromXDR(tx, networkPassphrase); + t.sign(keypair); + return t.toXDR(); + }, +}; diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs index e4657db60..291d195da 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs @@ -30,8 +30,14 @@ impl Contract { addr } - pub fn inc(env: Env) { - let mut count: u32 = env.storage().temporary().get(&COUNTER).unwrap_or(0); // Panic if the value of COUNTER is not u32. + // get current count + pub fn get_count(env: Env) -> u32 { + env.storage().temporary().get(&COUNTER).unwrap_or(0) + } + + // increment count and return new one + pub fn inc(env: Env) -> u32 { + let mut count: u32 = env.storage().temporary().get(&COUNTER).unwrap_or(0); log!(&env, "count: {}", count); // Increment the count. @@ -39,6 +45,8 @@ impl Contract { // Save the count. env.storage().temporary().set(&COUNTER, &count); + + count } #[allow(unused_variables)] diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index f7093112a..f8bca6ec4 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -91,6 +91,7 @@ tracing-appender = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } cargo_metadata = "0.15.4" pathdiff = "0.2.1" +dotenv = "0.15.0" # For hyper-tls [target.'cfg(unix)'.dependencies] openssl = { version = "0.10.55", features = ["vendored"] } diff --git a/cmd/soroban-cli/src/bin/main.rs b/cmd/soroban-cli/src/bin/main.rs index c8bda845c..b51f1dfcb 100644 --- a/cmd/soroban-cli/src/bin/main.rs +++ b/cmd/soroban-cli/src/bin/main.rs @@ -1,10 +1,11 @@ use clap::{CommandFactory, Parser}; -use tracing_subscriber::{fmt, EnvFilter}; - +use dotenv::dotenv; use soroban_cli::{commands::plugin, Root}; +use tracing_subscriber::{fmt, EnvFilter}; #[tokio::main] async fn main() { + dotenv().ok(); let mut root = Root::try_parse().unwrap_or_else(|e| { use clap::error::ErrorKind; match e.kind() { diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 10872662c..0c84a5a80 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -87,27 +87,26 @@ impl Cmd { let public_strkey = stellar_strkey::ed25519::PublicKey(key.public.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.clone(), sequence + 1, self.fee.fee, &key)?; - // Currently internal errors are not returned if the contract code is expired - if let ( + if let Ok(( TransactionResult { result: TransactionResultResult::TxInternalError, .. }, + meta, _, - _, - ) = client + )) = client .prepare_and_send_transaction( &tx_without_preflight, &key, &network.network_passphrase, None, ) - .await? + .await { + tracing::trace!("{meta:#?}"); // Now just need to restore it and don't have to install again restore::Cmd { contract_id: None, diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index daea261ed..49f75bf66 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -547,7 +547,7 @@ fn build_custom_cmd(name: &str, spec: &Spec) -> Result { 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.iter() { + 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); diff --git a/cmd/soroban-cli/src/commands/version.rs b/cmd/soroban-cli/src/commands/version.rs index 44848322c..21be0743a 100644 --- a/cmd/soroban-cli/src/commands/version.rs +++ b/cmd/soroban-cli/src/commands/version.rs @@ -22,7 +22,7 @@ pub fn short() -> String { pub fn long() -> String { let env = soroban_env_host::VERSION; let xdr = soroban_env_host::VERSION.xdr; - vec![ + [ short(), format!("soroban-env {} ({})", env.pkg, env.rev), format!("soroban-env interface version {}", meta::INTERFACE_VERSION), diff --git a/cmd/soroban-cli/src/log.rs b/cmd/soroban-cli/src/log.rs index 0bced71f5..9f7b1ebbf 100644 --- a/cmd/soroban-cli/src/log.rs +++ b/cmd/soroban-cli/src/log.rs @@ -2,8 +2,12 @@ pub mod auth; pub mod budget; pub mod event; pub mod footprint; +pub mod txn_error; +pub mod txn_response_error; pub use auth::*; pub use budget::*; pub use event::*; pub use footprint::*; +pub use txn_error::*; +pub use txn_response_error::*; diff --git a/cmd/soroban-cli/src/log/event.rs b/cmd/soroban-cli/src/log/event.rs index acd92fee5..33d150ca0 100644 --- a/cmd/soroban-cli/src/log/event.rs +++ b/cmd/soroban-cli/src/log/event.rs @@ -1,7 +1,7 @@ use soroban_env_host::events::HostEvent; pub fn events(events: &[HostEvent]) { - for event in events.iter() { + for event in events { tracing::info!(log = event.to_string()); } } diff --git a/cmd/soroban-cli/src/log/txn_error.rs b/cmd/soroban-cli/src/log/txn_error.rs new file mode 100644 index 000000000..a3357f5de --- /dev/null +++ b/cmd/soroban-cli/src/log/txn_error.rs @@ -0,0 +1,3 @@ +pub fn txn_error(error: &crate::rpc::Error) { + tracing::debug!(?error); +} diff --git a/cmd/soroban-cli/src/log/txn_response_error.rs b/cmd/soroban-cli/src/log/txn_response_error.rs new file mode 100644 index 000000000..c58d2af79 --- /dev/null +++ b/cmd/soroban-cli/src/log/txn_response_error.rs @@ -0,0 +1,5 @@ +use crate::rpc::GetTransactionResponse; + +pub fn txn_response_error(error: &GetTransactionResponse) { + tracing::debug!(?error); +} diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index 572cd59ca..ad70d733b 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -508,8 +508,11 @@ soroban config identity fund {address} --helper-url "# TransactionResult::read_xdr_base64(&mut x.as_bytes()) .map_err(|_| Error::InvalidResponse) }) - .map(|r| r.result); - tracing::error!(?error); + .map(|r| r.result) + .map_err(|e| { + crate::log::txn_error::txn_error(&e); + e + }); return Err(Error::TransactionSubmissionFailed(format!("{:#?}", error?))); } // even if status == "success" we need to query the transaction status in order to get the result @@ -535,7 +538,7 @@ soroban config identity fund {address} --helper-url "# return Ok((result, meta, events)); } "FAILED" => { - tracing::error!(?response); + crate::log::txn_response_error(&response); // TODO: provide a more elaborate error return Err(Error::TransactionSubmissionFailed(format!("{response:#?}"))); } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index d2b3705b1..fea858ba3 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -84,7 +84,7 @@ pub fn add_contract_code_to_ledger_entries( }), ext: LedgerEntryExt::V0, }; - for (k, e) in entries.iter_mut() { + for (k, e) in &mut *entries { if **k == code_key { **e = code_entry; return Ok(hash); @@ -125,7 +125,7 @@ pub fn add_contract_to_ledger_entries( }), ext: LedgerEntryExt::V0, }; - for (k, e) in entries.iter_mut() { + for (k, e) in &mut *entries { if **k == contract_key { **e = contract_entry; return; @@ -143,7 +143,7 @@ pub fn bump_ledger_entry_expirations( .iter() .map(|b| (b.key.as_ref().clone(), b.min_expiration)) .collect::>(); - for (k, e) in entries.iter_mut() { + for (k, e) in &mut *entries { if let Some(min_expiration) = lookup.get(k.as_ref()) { if let LedgerEntryData::ContractData(entry) = &mut e.data { entry.expiration_ledger_seq = *min_expiration;