From 239040c277c92aaa1f6483383a095bd28eac2034 Mon Sep 17 00:00:00 2001 From: Chad Ostrowski <221614+chadoh@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:43:28 -0400 Subject: [PATCH] feat(bindings-ts): support multi-auth workflows - add `atomic_swap` and `token` contracts from https://github.com/stellar/soroban-examples to `test-wasms` - switch from `ts-tests/package.json` to an `initialize.sh` script that uses a `.env` if available or defaults to environment variables. As before, this will build, deploy, and generate bindings for the new contracts, plus creating an `alice` identity and minting separate amounts of two separate tokens to the `root` user and `alice` - add eslint to catch missing `await`s - add tests for atomic swap functionality inspired by https://github.com/stellar/soroban-react-atomic-swap - let this logic guide needed updates to `bindings typescript`-generated libraries: - don't return flat values - instead, return a `new AssembledTransaction`, a class that has a `result` getter that can be used to get the result of the simulation right away, or can be used to `await tx.sign()` then `await tx.send()`. - `AssembledTransaction` also contains all possibly-relevant fields from the logic it performs, such as `sendTransactionAll` (these field names are a bit raw right now, and could probably be renamed to be more clear to new users) - `AssembledTransaction` also has a `needsNonInvokerSigningBy()` getter and a `signAuthEntriesFor(publicKey, signingFunction)` to facilitate multi-auth workflows. - Since assembling transactions will now take place across multiple users' browsers, you can also call `json = tx.toJSON()` on an AssembledTransaction and then `contract.fromJSON[method](json)` on the next machine to continue signing Co-authored-by: Aristides Staffieri --- Cargo.lock | 23 + Cargo.toml | 5 + .../dist/cjs/assembled-tx.d.ts | 140 + .../dist/cjs/assembled-tx.js | 399 ++ .../test_custom_types/dist/cjs/convert.d.ts | 7 + .../test_custom_types/dist/cjs/convert.js | 103 + .../test_custom_types/dist/cjs/index.d.ts | 355 ++ .../test_custom_types/dist/cjs/index.js | 503 ++ .../dist/cjs/method-options.d.ts | 41 + .../dist/cjs/method-options.js | 4 + .../dist/esm/assembled-tx.d.ts | 140 + .../dist/esm/assembled-tx.js | 388 ++ .../test_custom_types/dist/esm/convert.d.ts | 7 + .../test_custom_types/dist/esm/convert.js | 94 + .../test_custom_types/dist/esm/index.d.ts | 355 ++ .../test_custom_types/dist/esm/index.js | 483 ++ .../dist/esm/method-options.d.ts | 41 + .../dist/esm/method-options.js | 3 + .../test_custom_types/dist/esm/package.json | 1 + .../dist/types/assembled-tx.d.ts | 140 + .../test_custom_types/dist/types/convert.d.ts | 7 + .../test_custom_types/dist/types/index.d.ts | 355 ++ .../dist/types/method-options.d.ts | 41 + .../test_custom_types/package-lock.json | 94 +- .../fixtures/test_custom_types/package.json | 2 +- .../test_custom_types/src/assembled-tx.ts | 584 ++ .../fixtures/test_custom_types/src/index.ts | 724 +-- .../fixtures/test_custom_types/src/invoke.ts | 254 - .../test_custom_types/src/method-options.ts | 14 +- .../src/boilerplate.rs | 2 +- cmd/crates/soroban-spec-typescript/src/lib.rs | 160 +- .../src/project_template/src/assembled-tx.ts | 584 ++ .../src/project_template/src/index.ts | 8 +- .../src/project_template/src/invoke.ts | 259 - .../project_template/src/method-options.ts | 14 +- .../soroban-spec-typescript/ts-tests/.env | 2 + .../ts-tests/.eslintrc.cjs | 11 + .../ts-tests/contract-token-hash.txt | 1 + .../ts-tests/initialize.sh | 71 + .../ts-tests/package-lock.json | 5650 ++++++++++------- .../ts-tests/package.json | 59 +- .../ts-tests/src/test-custom-types.ts | 96 +- .../src/test-deserialized-result-type.ts | 16 + .../ts-tests/src/test-hello-world.ts | 27 +- .../ts-tests/src/test-methods-as-args.ts | 4 +- .../ts-tests/src/test-swap.ts | 115 + .../ts-tests/src/util.ts | 57 +- .../ts-tests/tsconfig.json | 4 +- .../tests/fixtures/test-wasms/swap/Cargo.toml | 18 + .../tests/fixtures/test-wasms/swap/src/lib.rs | 77 + .../fixtures/test-wasms/swap/src/test.rs | 112 + .../fixtures/test-wasms/token/Cargo.toml | 19 + .../fixtures/test-wasms/token/src/admin.rs | 18 + .../test-wasms/token/src/allowance.rs | 63 + .../fixtures/test-wasms/token/src/balance.rs | 35 + .../fixtures/test-wasms/token/src/contract.rs | 174 + .../fixtures/test-wasms/token/src/lib.rs | 11 + .../fixtures/test-wasms/token/src/metadata.rs | 22 + .../test-wasms/token/src/storage_types.rs | 31 + .../fixtures/test-wasms/token/src/test.rs | 256 + 60 files changed, 9758 insertions(+), 3525 deletions(-) create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.js create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/package.json create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/assembled-tx.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/convert.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/index.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/method-options.d.ts create mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/assembled-tx.ts delete mode 100644 cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts create mode 100644 cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts delete mode 100644 cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts create mode 100644 cmd/crates/soroban-spec-typescript/ts-tests/.eslintrc.cjs create mode 100644 cmd/crates/soroban-spec-typescript/ts-tests/contract-token-hash.txt create mode 100755 cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh create mode 100644 cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-result-type.ts create mode 100644 cmd/crates/soroban-spec-typescript/ts-tests/src/test-swap.ts create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/lib.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/admin.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/allowance.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/balance.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/contract.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/lib.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/metadata.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/storage_types.rs create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs diff --git a/Cargo.lock b/Cargo.lock index a1be72d738..7e01e87ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2743,6 +2743,14 @@ dependencies = [ "which", ] +[[package]] +name = "soroban-token-sdk" +version = "20.0.0-rc2" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=0992413f9b05e5bfb1f872bce99e89d9129b2e61#0992413f9b05e5bfb1f872bce99e89d9129b2e61" +dependencies = [ + "soroban-sdk", +] + [[package]] name = "soroban-wasmi" version = "0.31.0-soroban1" @@ -2932,6 +2940,21 @@ dependencies = [ "soroban-sdk", ] +[[package]] +name = "test_swap" +version = "20.0.0-rc.4.2" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "test_token" +version = "20.0.0-rc.4.2" +dependencies = [ + "soroban-sdk", + "soroban-token-sdk", +] + [[package]] name = "test_udt" version = "20.0.0-rc.4.2" diff --git a/Cargo.toml b/Cargo.toml index c7a92d31bd..8dad2eba3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,11 @@ version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" rev = "0992413f9b05e5bfb1f872bce99e89d9129b2e61" +[workspace.dependencies.soroban-token-sdk] +version = "20.0.0-rc2" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "0992413f9b05e5bfb1f872bce99e89d9129b2e61" + [workspace.dependencies.soroban-ledger-snapshot] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" 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 new file mode 100644 index 0000000000..4ef0898486 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.d.ts @@ -0,0 +1,140 @@ +import { Account, Operation, Server, SorobanRpc, xdr } from "soroban-client"; +import type { Memo, MemoType, Transaction } from "soroban-client"; +import { Buffer } from "buffer"; +import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; +export type Tx = Transaction, Operation[]>; +export declare class NotImplementedError extends Error { +} +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.SendTransactionResponse; +type GetTx = SorobanRpc.GetTransactionResponse; +type AssembledTransactionOptions = MethodOptions & ClassOptions & { + method: string; + args?: any[]; + parseResultXdr: (xdr: string | xdr.ScVal) => T; +}; +export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +export declare class AssembledTransaction { + options: AssembledTransactionOptions; + tx: Tx; + simulationResult: SorobanRpc.SimulateHostFunctionResult; + simulationTransactionData: xdr.SorobanTransactionData; + 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; + getWallet: () => 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 view call. + */ + force?: boolean | undefined; + }) => Promise>; + getStorageExpiration: (storageType: 'temporary' | 'persistent') => 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 signAuthEntriesFor}. 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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor: (publicKey: string, sign: (payload: Buffer) => Buffer | Promise, expiration?: number | Promise) => Promise; + get isViewCall(): boolean; + hasRealInvoker: () => Promise; + get result(): T; +} +/** + * 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; + tx: Tx; + server: Server; + sendTransactionResponse?: SendTx; + sendTransactionResponseAll?: SendTx[]; + getTransactionResponse?: GetTx; + getTransactionResponseAll?: GetTx[]; + constructor(options: AssembledTransactionOptions, tx: Tx); + send: (secondsToWait?: number) => Promise; + get result(): T; +} +export {}; 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 new file mode 100644 index 0000000000..b1c6d9b0fd --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/assembled-tx.js @@ -0,0 +1,399 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssembledTransaction = exports.NULL_ACCOUNT = exports.NoUnsignedNonInvokerAuthEntriesError = exports.SendFailedError = exports.SendResultOnlyError = exports.WalletDisconnectedError = exports.NeedsMoreSignaturesError = exports.ExpiredStateError = exports.NotImplementedError = void 0; +const soroban_client_1 = require("soroban-client"); +const buffer_1 = require("buffer"); +class NotImplementedError extends Error { +} +exports.NotImplementedError = NotImplementedError; +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; +exports.NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +class AssembledTransaction { + options; + tx; + simulationResult; + simulationTransactionData; + server; + toJSON() { + return JSON.stringify({ + method: this.options.method, + tx: this.tx?.toXDR(), + simulationResult: { + auth: this.simulationResult.auth.map(a => a.toXDR('base64')), + retval: this.simulationResult.retval.toXDR('base64'), + }, + simulationTransactionData: this.simulationTransactionData.toXDR('base64'), + }); + } + static fromJSON(options, { tx, simulationResult, simulationTransactionData }) { + const txn = new AssembledTransaction(options); + txn.tx = soroban_client_1.TransactionBuilder.fromXDR(tx, options.networkPassphrase); + txn.simulationResult = { + auth: simulationResult.auth.map(a => soroban_client_1.xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64')), + retval: soroban_client_1.xdr.ScVal.fromXDR(simulationResult.retval, 'base64'), + }; + txn.simulationTransactionData = soroban_client_1.xdr.SorobanTransactionData.fromXDR(simulationTransactionData, 'base64'); + return txn; + } + constructor(options) { + this.options = options; + this.server = new soroban_client_1.Server(this.options.rpcUrl, { + allowHttp: this.options.rpcUrl.startsWith("http://"), + }); + } + static async fromSimulation(options) { + const tx = new AssembledTransaction(options); + const contract = new soroban_client_1.Contract(options.contractId); + tx.tx = new soroban_client_1.TransactionBuilder(await tx.getAccount(), { + fee: options.fee?.toString(10) ?? '100', + networkPassphrase: options.networkPassphrase, + }) + .addOperation(contract.call(options.method, ...(options.args ?? []))) + .setTimeout(soroban_client_1.TimeoutInfinite) + .build(); + return await tx.simulate(); + } + simulate = async () => { + if (!this.tx) + throw new Error('Transaction has not yet been assembled'); + const simulation = await this.server.simulateTransaction(this.tx); + if (!simulation) { + throw new Error('Transaction has not yet been simulated'); + } + if (soroban_client_1.SorobanRpc.isSimulationError(simulation)) { + throw new Error(`Transaction simulation failed: "${simulation.error}"`); + } + if (soroban_client_1.SorobanRpc.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)}`); + } + this.simulationResult = simulation.result; + this.simulationTransactionData = simulation.transactionData.build(); + this.tx = (0, soroban_client_1.assembleTransaction)(this.tx, this.options.networkPassphrase, simulation).build(); + return this; + }; + getWallet = async () => { + return this.options.wallet ?? (await Promise.resolve().then(() => require("@stellar/freighter-api"))); + }; + /** + * 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 wallet = await this.getWallet(); + let publicKey; + if (await wallet.isConnected() && await wallet.isAllowed()) { + publicKey = (await wallet.getUserInfo()).publicKey; + } + return publicKey + ? await this.server.getAccount(publicKey) + : new soroban_client_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.tx) { + throw new Error('Transaction has not yet been simulated'); + } + if (!force && this.isViewCall) { + throw new Error('This is a view call. It requires no signature or sending. Use `force: true` to sign anyway.'); + } + if (!await this.hasRealInvoker()) { + throw new WalletDisconnectedError('Wallet is not connected'); + } + if ((await this.needsNonInvokerSigningBy()).length) { + throw new NeedsMoreSignaturesError('Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.'); + } + const wallet = await this.getWallet(); + const signed = await wallet.signTransaction(this.tx.toXDR(), { + networkPassphrase: this.options.networkPassphrase, + }); + const tx = new SentTransaction(this.options, soroban_client_1.TransactionBuilder.fromXDR(signed, this.options.networkPassphrase)); + await tx.send(); + return tx; + }; + getStorageExpiration = async (storageType) => { + const key = soroban_client_1.xdr.LedgerKey.contractData(new soroban_client_1.xdr.LedgerKeyContractData({ + contract: new soroban_client_1.Address(this.options.contractId).toScAddress(), + key: soroban_client_1.xdr.ScVal.scvLedgerKeyContractInstance(), + durability: soroban_client_1.xdr.ContractDataDurability[storageType](), + })); + const expirationKey = soroban_client_1.xdr.LedgerKey.expiration(new soroban_client_1.xdr.LedgerKeyExpiration({ keyHash: (0, soroban_client_1.hash)(key.toXDR()) })); + const entryRes = await this.server.getLedgerEntries(expirationKey); + if (!(entryRes.entries && entryRes.entries.length)) + throw new Error('failed to get ledger entry'); + return entryRes.entries[0].val.expiration().expirationLedgerSeq(); + }; + /** + * 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 signAuthEntriesFor}. Then re-serialize and send to the next account + * in this list. + */ + needsNonInvokerSigningBy = async ({ includeAlreadySigned = false, } = {}) => { + if (!this.tx) { + 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.tx)) { + throw new Error(`Unexpected Transaction type; no operations: ${JSON.stringify(this.tx)}`); + } + const rawInvokeHostFunctionOp = this.tx + .operations[0]; + return [...new Set((rawInvokeHostFunctionOp.auth ?? []).filter(entry => entry.credentials().switch() === + soroban_client_1.xdr.SorobanCredentialsType.sorobanCredentialsAddress() && + (includeAlreadySigned || + entry.credentials().address().signature().switch().name === 'scvVoid')).map(entry => soroban_client_1.StrKey.encodeEd25519PublicKey(entry.credentials().address().address().accountId().ed25519())))]; + }; + preImageFor(entry, signatureExpirationLedger) { + const addrAuth = entry.credentials().address(); + return soroban_client_1.xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(new soroban_client_1.xdr.HashIdPreimageSorobanAuthorization({ + networkId: (0, soroban_client_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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor = async ( + /** + * The public key of the account with auth entries that need to be signed. + * + * Must correspond to a key from `await this.needsNonInvokerSigningBy()`. + */ + publicKey, + /** + * Signing function to use to sign each auth entry that `publicKey` account + holder needs to sign. + */ + sign, + /** + * 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('persistent')) => { + if (!this.tx) + 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?'); + if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) + throw new Error('No auth entries for this public key'); + const rawInvokeHostFunctionOp = this.tx + .operations[0]; + const originalAuthEntries = rawInvokeHostFunctionOp.auth ?? []; + for (const entry of originalAuthEntries) { + if (entry.credentials().switch() !== + soroban_client_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 = soroban_client_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; + const preimage = this.preImageFor(entry, await expiration); + const payload = (0, soroban_client_1.hash)(preimage.toXDR()); + const signature = await sign(payload); + // TODO: do we need this check? + if (!soroban_client_1.Keypair.fromPublicKey(pk).verify(payload, signature)) { + throw new Error(`Signature doesn't match payload!\n\n` + + ` publicKey: ${pk}\n` + + ` signature: ${signature}`); + } + const sigScVal = (0, soroban_client_1.nativeToScVal)({ + public_key: soroban_client_1.StrKey.decodeEd25519PublicKey(pk), + signature, + }, { + // force the keys to be interpreted as symbols (expected for + // Soroban [contracttype]s) + // Pr open to fix this type in the gen'd xdr + type: { + public_key: ["symbol", null], + signature: ["symbol", null], + }, + }); + // get a reference to the auth entry, then mutate it + const addrAuth = entry.credentials().address(); + addrAuth.signature(soroban_client_1.xdr.ScVal.scvVec([sigScVal])); + addrAuth.signatureExpirationLedger(preimage.sorobanAuthorization().signatureExpirationLedger()); + } + }; + get isViewCall() { + const authsCount = this.simulationResult.auth.length; + const writeLength = this.simulationTransactionData.resources().footprint().readWrite().length; + return (authsCount === 0) && (writeLength === 0); + } + hasRealInvoker = async () => { + const account = await this.getAccount(); + return account.accountId() !== exports.NULL_ACCOUNT; + }; + get result() { + return this.options.parseResultXdr(this.simulationResult.retval); + } +} +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; + tx; + server; + sendTransactionResponse; + sendTransactionResponseAll; + getTransactionResponse; + getTransactionResponseAll; + constructor(options, tx) { + this.options = options; + this.tx = tx; + this.server = new soroban_client_1.Server(this.options.rpcUrl, { + allowHttp: this.options.rpcUrl.startsWith("http://"), + }); + this.tx = tx; + } + send = async (secondsToWait = 10) => { + this.sendTransactionResponseAll = await withExponentialBackoff(() => this.server.sendTransaction(this.tx), 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 === soroban_client_1.SorobanRpc.GetTransactionStatus.NOT_FOUND, secondsToWait); + this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1]; + if (this.getTransactionResponse.status === soroban_client_1.SorobanRpc.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.tx)}`); + } +} +/** + * 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.log(`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.log(`was gonna wait too long; new waitTime: ${waitTime}ms`); + } + } + totalWaitTime = waitTime + totalWaitTime; + // Try again + attempts.push(await fn()); + if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { + console.log(`${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/convert.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.d.ts new file mode 100644 index 0000000000..6db3b88d39 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.d.ts @@ -0,0 +1,7 @@ +import { xdr } from 'soroban-client'; +export declare function strToScVal(base64Xdr: string): xdr.ScVal; +export declare function scValStrToJs(base64Xdr: string): T; +export declare function scValToJs(val: xdr.ScVal): T; +export declare function addressToScVal(addr: string): xdr.ScVal; +export declare function i128ToScVal(i: bigint): xdr.ScVal; +export declare function u128ToScVal(i: bigint): xdr.ScVal; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.js new file mode 100644 index 0000000000..02157808d6 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/convert.js @@ -0,0 +1,103 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.u128ToScVal = exports.i128ToScVal = exports.addressToScVal = exports.scValToJs = exports.scValStrToJs = exports.strToScVal = void 0; +const soroban_client_1 = require("soroban-client"); +function strToScVal(base64Xdr) { + return soroban_client_1.xdr.ScVal.fromXDR(base64Xdr, 'base64'); +} +exports.strToScVal = strToScVal; +function scValStrToJs(base64Xdr) { + return scValToJs(strToScVal(base64Xdr)); +} +exports.scValStrToJs = scValStrToJs; +function scValToJs(val) { + switch (val?.switch()) { + case soroban_client_1.xdr.ScValType.scvBool(): { + return val.b(); + } + case soroban_client_1.xdr.ScValType.scvVoid(): + case undefined: { + return 0; + } + case soroban_client_1.xdr.ScValType.scvU32(): { + return val.u32(); + } + case soroban_client_1.xdr.ScValType.scvI32(): { + return val.i32(); + } + case soroban_client_1.xdr.ScValType.scvU64(): + case soroban_client_1.xdr.ScValType.scvI64(): + case soroban_client_1.xdr.ScValType.scvU128(): + case soroban_client_1.xdr.ScValType.scvI128(): + case soroban_client_1.xdr.ScValType.scvU256(): + case soroban_client_1.xdr.ScValType.scvI256(): { + return (0, soroban_client_1.scValToBigInt)(val); + } + case soroban_client_1.xdr.ScValType.scvAddress(): { + return soroban_client_1.Address.fromScVal(val).toString(); + } + case soroban_client_1.xdr.ScValType.scvString(): { + return val.str().toString(); + } + case soroban_client_1.xdr.ScValType.scvSymbol(): { + return val.sym().toString(); + } + case soroban_client_1.xdr.ScValType.scvBytes(): { + return val.bytes(); + } + case soroban_client_1.xdr.ScValType.scvVec(): { + return val.vec().map(v => scValToJs(v)); + } + case soroban_client_1.xdr.ScValType.scvMap(): { + let res = {}; + val.map().forEach((e) => { + let key = scValToJs(e.key()); + let value; + let v = e.val(); + // For now we assume second level maps are real maps. Not perfect but better. + switch (v?.switch()) { + case soroban_client_1.xdr.ScValType.scvMap(): { + let inner_map = new Map(); + v.map().forEach((e) => { + let key = scValToJs(e.key()); + let value = scValToJs(e.val()); + inner_map.set(key, value); + }); + value = inner_map; + break; + } + default: { + value = scValToJs(e.val()); + } + } + //@ts-ignore + res[key] = value; + }); + return res; + } + case soroban_client_1.xdr.ScValType.scvContractInstance(): + case soroban_client_1.xdr.ScValType.scvLedgerKeyNonce(): + case soroban_client_1.xdr.ScValType.scvTimepoint(): + case soroban_client_1.xdr.ScValType.scvDuration(): + return val.value(); + // TODO: Add this case when merged + // case xdr.ScValType.scvError(): + default: { + throw new Error(`type not implemented yet: ${val?.switch().name}`); + } + } + ; +} +exports.scValToJs = scValToJs; +function addressToScVal(addr) { + return (0, soroban_client_1.nativeToScVal)(addr, { type: 'address' } /* bug workaround */); +} +exports.addressToScVal = addressToScVal; +function i128ToScVal(i) { + return new soroban_client_1.ScInt(i).toI128(); +} +exports.i128ToScVal = i128ToScVal; +function u128ToScVal(i) { + return new soroban_client_1.ScInt(i).toU128(); +} +exports.u128ToScVal = u128ToScVal; 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 new file mode 100644 index 0000000000..0598452832 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.d.ts @@ -0,0 +1,355 @@ +import { ContractSpec, Address } from 'soroban-client'; +import { Buffer } from "buffer"; +import { AssembledTransaction } from './assembled-tx.js'; +import type { ClassOptions, XDR_BASE64 } from './method-options.js'; +export * from './assembled-tx.js'; +export * from './method-options.js'; +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 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 class Contract { + readonly options: ClassOptions; + spec: ContractSpec; + readonly parsers: { + hello: (xdr: XDR_BASE64) => string; + woid: () => void; + val: (xdr: XDR_BASE64) => any; + u32FailOnEven: (xdr: XDR_BASE64) => Ok | Err | undefined; + u32: (xdr: XDR_BASE64) => u32; + i32: (xdr: XDR_BASE64) => i32; + i64: (xdr: XDR_BASE64) => i64; + struktHel: (xdr: XDR_BASE64) => Array; + strukt: (xdr: XDR_BASE64) => Test; + simple: (xdr: XDR_BASE64) => SimpleEnum; + complex: (xdr: XDR_BASE64) => ComplexEnum; + addresse: (xdr: XDR_BASE64) => string; + bytes: (xdr: XDR_BASE64) => Buffer; + bytesN: (xdr: XDR_BASE64) => Buffer; + card: (xdr: XDR_BASE64) => RoyalCard; + boolean: (xdr: XDR_BASE64) => boolean; + not: (xdr: XDR_BASE64) => boolean; + i128: (xdr: XDR_BASE64) => i128; + u128: (xdr: XDR_BASE64) => u128; + multiArgs: (xdr: XDR_BASE64) => u32; + map: (xdr: XDR_BASE64) => Map; + vec: (xdr: XDR_BASE64) => Array; + tuple: (xdr: XDR_BASE64) => readonly [string, u32]; + option: (xdr: XDR_BASE64) => Option; + u256: (xdr: XDR_BASE64) => u256; + i256: (xdr: XDR_BASE64) => i256; + string: (xdr: XDR_BASE64) => string; + tupleStrukt: (xdr: XDR_BASE64) => TupleStruct; + }; + constructor(options: ClassOptions); + txFromJSON: (json: string) => AssembledTransaction; + hello: ({ hello }: { + hello: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + woid: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + val: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u32FailOnEven: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise | AssembledTransaction | Ok | undefined>>; + u32: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i32: ({ i32_ }: { + i32_: i32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i64: ({ i64_ }: { + i64_: i64; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example contract method which takes a struct + */ + struktHel: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + strukt: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + simple: ({ simple }: { + simple: SimpleEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + complex: ({ complex }: { + complex: ComplexEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + addresse: ({ addresse }: { + addresse: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytes: ({ bytes }: { + bytes: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytesN: ({ bytes_n }: { + bytes_n: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + card: ({ card }: { + card: RoyalCard; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + boolean: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Negates a boolean value + */ + not: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i128: ({ i128 }: { + i128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u128: ({ u128 }: { + u128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + multiArgs: ({ a, b }: { + a: u32; + b: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + map: ({ map }: { + map: Map; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + vec: ({ vec }: { + vec: Array; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + tuple: ({ tuple }: { + tuple: readonly [string, u32]; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example of an optional argument + */ + option: ({ option }: { + option: Option; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + u256: ({ u256 }: { + u256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i256: ({ i256 }: { + i256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + string: ({ string }: { + string: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + 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 new file mode 100644 index 0000000000..c82bb74abf --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/index.js @@ -0,0 +1,503 @@ +"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.RoyalCard = exports.networks = exports.Err = exports.Ok = exports.Address = void 0; +const soroban_client_1 = require("soroban-client"); +Object.defineProperty(exports, "Address", { enumerable: true, get: function () { return soroban_client_1.Address; } }); +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); +; +; +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; +if (typeof window !== 'undefined') { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || buffer_1.Buffer; +} +const regex = /Error\(Contract, #(\d+)\)/; +function parseError(message) { + const match = message.match(regex); + if (!match) { + return undefined; + } + if (Errors === undefined) { + return undefined; + } + let i = parseInt(match[1], 10); + let err = Errors[i]; + if (err) { + return new Err(err); + } + return undefined; +} +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 = {})); +const Errors = { + 1: { message: "Please provide an odd number" } +}; +class Contract { + options; + spec; + parsers = { + hello: (xdr) => { + return this.spec.funcResToNative("hello", xdr); + }, + woid: () => { }, + val: (xdr) => { + return this.spec.funcResToNative("val", xdr); + }, + u32FailOnEven: (xdr) => { + return new Ok(this.spec.funcResToNative("u32_fail_on_even", xdr)); + }, + u32: (xdr) => { + return this.spec.funcResToNative("u32_", xdr); + }, + i32: (xdr) => { + return this.spec.funcResToNative("i32_", xdr); + }, + i64: (xdr) => { + return this.spec.funcResToNative("i64_", xdr); + }, + struktHel: (xdr) => { + return this.spec.funcResToNative("strukt_hel", xdr); + }, + strukt: (xdr) => { + return this.spec.funcResToNative("strukt", xdr); + }, + simple: (xdr) => { + return this.spec.funcResToNative("simple", xdr); + }, + complex: (xdr) => { + return this.spec.funcResToNative("complex", xdr); + }, + addresse: (xdr) => { + return this.spec.funcResToNative("addresse", xdr); + }, + bytes: (xdr) => { + return this.spec.funcResToNative("bytes", xdr); + }, + bytesN: (xdr) => { + return this.spec.funcResToNative("bytes_n", xdr); + }, + card: (xdr) => { + return this.spec.funcResToNative("card", xdr); + }, + boolean: (xdr) => { + return this.spec.funcResToNative("boolean", xdr); + }, + not: (xdr) => { + return this.spec.funcResToNative("not", xdr); + }, + i128: (xdr) => { + return this.spec.funcResToNative("i128", xdr); + }, + u128: (xdr) => { + return this.spec.funcResToNative("u128", xdr); + }, + multiArgs: (xdr) => { + return this.spec.funcResToNative("multi_args", xdr); + }, + map: (xdr) => { + return this.spec.funcResToNative("map", xdr); + }, + vec: (xdr) => { + return this.spec.funcResToNative("vec", xdr); + }, + tuple: (xdr) => { + return this.spec.funcResToNative("tuple", xdr); + }, + option: (xdr) => { + return this.spec.funcResToNative("option", xdr); + }, + u256: (xdr) => { + return this.spec.funcResToNative("u256", xdr); + }, + i256: (xdr) => { + return this.spec.funcResToNative("i256", xdr); + }, + string: (xdr) => { + return this.spec.funcResToNative("string", xdr); + }, + tupleStrukt: (xdr) => { + return this.spec.funcResToNative("tuple_strukt", xdr); + } + }; + constructor(options) { + this.options = options; + this.spec = new soroban_client_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=" + ]); + } + txFromJSON = (json) => { + const { method, ...tx } = JSON.parse(json); + return assembled_tx_js_1.AssembledTransaction.fromJSON({ + ...this.options, + method, + parseResultXdr: this.parsers[method], + }, tx); + }; + hello = async ({ hello }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'hello', + args: this.spec.funcArgsToScVals("hello", { hello }), + ...options, + ...this.options, + parseResultXdr: this.parsers['hello'], + }); + }; + woid = async (options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'woid', + args: this.spec.funcArgsToScVals("woid", {}), + ...options, + ...this.options, + parseResultXdr: this.parsers['woid'], + }); + }; + val = async (options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'val', + args: this.spec.funcArgsToScVals("val", {}), + ...options, + ...this.options, + parseResultXdr: this.parsers['val'], + }); + }; + u32FailOnEven = async ({ u32_ }, options = {}) => { + try { + 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, + parseResultXdr: this.parsers['u32FailOnEven'], + }); + } + catch (e) { + let err = parseError(e.toString()); + if (err) + return err; + throw e; + } + }; + u32 = async ({ u32_ }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'u32_', + args: this.spec.funcArgsToScVals("u32_", { u32_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u32'], + }); + }; + i32 = async ({ i32_ }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'i32_', + args: this.spec.funcArgsToScVals("i32_", { i32_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i32'], + }); + }; + i64 = async ({ i64_ }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'i64_', + args: this.spec.funcArgsToScVals("i64_", { i64_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i64'], + }); + }; + /** + * 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, + parseResultXdr: this.parsers['struktHel'], + }); + }; + strukt = async ({ strukt }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'strukt', + args: this.spec.funcArgsToScVals("strukt", { strukt }), + ...options, + ...this.options, + parseResultXdr: this.parsers['strukt'], + }); + }; + simple = async ({ simple }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'simple', + args: this.spec.funcArgsToScVals("simple", { simple }), + ...options, + ...this.options, + parseResultXdr: this.parsers['simple'], + }); + }; + complex = async ({ complex }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'complex', + args: this.spec.funcArgsToScVals("complex", { complex }), + ...options, + ...this.options, + parseResultXdr: this.parsers['complex'], + }); + }; + addresse = async ({ addresse }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'addresse', + args: this.spec.funcArgsToScVals("addresse", { addresse: new soroban_client_1.Address(addresse) }), + ...options, + ...this.options, + parseResultXdr: this.parsers['addresse'], + }); + }; + bytes = async ({ bytes }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'bytes', + args: this.spec.funcArgsToScVals("bytes", { bytes }), + ...options, + ...this.options, + parseResultXdr: this.parsers['bytes'], + }); + }; + 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, + parseResultXdr: this.parsers['bytesN'], + }); + }; + card = async ({ card }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'card', + args: this.spec.funcArgsToScVals("card", { card }), + ...options, + ...this.options, + parseResultXdr: this.parsers['card'], + }); + }; + boolean = async ({ boolean }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'boolean', + args: this.spec.funcArgsToScVals("boolean", { boolean }), + ...options, + ...this.options, + parseResultXdr: this.parsers['boolean'], + }); + }; + /** + * 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, + parseResultXdr: this.parsers['not'], + }); + }; + i128 = async ({ i128 }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'i128', + args: this.spec.funcArgsToScVals("i128", { i128 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i128'], + }); + }; + u128 = async ({ u128 }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'u128', + args: this.spec.funcArgsToScVals("u128", { u128 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u128'], + }); + }; + 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, + parseResultXdr: this.parsers['multiArgs'], + }); + }; + map = async ({ map }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'map', + args: this.spec.funcArgsToScVals("map", { map }), + ...options, + ...this.options, + parseResultXdr: this.parsers['map'], + }); + }; + vec = async ({ vec }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'vec', + args: this.spec.funcArgsToScVals("vec", { vec }), + ...options, + ...this.options, + parseResultXdr: this.parsers['vec'], + }); + }; + tuple = async ({ tuple }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'tuple', + args: this.spec.funcArgsToScVals("tuple", { tuple }), + ...options, + ...this.options, + parseResultXdr: this.parsers['tuple'], + }); + }; + /** + * 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, + parseResultXdr: this.parsers['option'], + }); + }; + u256 = async ({ u256 }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'u256', + args: this.spec.funcArgsToScVals("u256", { u256 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u256'], + }); + }; + i256 = async ({ i256 }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'i256', + args: this.spec.funcArgsToScVals("i256", { i256 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i256'], + }); + }; + string = async ({ string }, options = {}) => { + return await assembled_tx_js_1.AssembledTransaction.fromSimulation({ + method: 'string', + args: this.spec.funcArgsToScVals("string", { string }), + ...options, + ...this.options, + parseResultXdr: this.parsers['string'], + }); + }; + 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, + 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 new file mode 100644 index 0000000000..a547f917dd --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.d.ts @@ -0,0 +1,41 @@ +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; +} +export type ClassOptions = { + contractId: string; + networkPassphrase: string; + rpcUrl: string; + /** + * 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: 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 new file mode 100644 index 0000000000..6d483ac325 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/cjs/method-options.js @@ -0,0 +1,4 @@ +"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 new file mode 100644 index 0000000000..4ef0898486 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.d.ts @@ -0,0 +1,140 @@ +import { Account, Operation, Server, SorobanRpc, xdr } from "soroban-client"; +import type { Memo, MemoType, Transaction } from "soroban-client"; +import { Buffer } from "buffer"; +import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; +export type Tx = Transaction, Operation[]>; +export declare class NotImplementedError extends Error { +} +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.SendTransactionResponse; +type GetTx = SorobanRpc.GetTransactionResponse; +type AssembledTransactionOptions = MethodOptions & ClassOptions & { + method: string; + args?: any[]; + parseResultXdr: (xdr: string | xdr.ScVal) => T; +}; +export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +export declare class AssembledTransaction { + options: AssembledTransactionOptions; + tx: Tx; + simulationResult: SorobanRpc.SimulateHostFunctionResult; + simulationTransactionData: xdr.SorobanTransactionData; + 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; + getWallet: () => 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 view call. + */ + force?: boolean | undefined; + }) => Promise>; + getStorageExpiration: (storageType: 'temporary' | 'persistent') => 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 signAuthEntriesFor}. 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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor: (publicKey: string, sign: (payload: Buffer) => Buffer | Promise, expiration?: number | Promise) => Promise; + get isViewCall(): boolean; + hasRealInvoker: () => Promise; + get result(): T; +} +/** + * 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; + tx: Tx; + server: Server; + sendTransactionResponse?: SendTx; + sendTransactionResponseAll?: SendTx[]; + getTransactionResponse?: GetTx; + getTransactionResponseAll?: GetTx[]; + constructor(options: AssembledTransactionOptions, tx: Tx); + send: (secondsToWait?: number) => Promise; + get result(): T; +} +export {}; 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 new file mode 100644 index 0000000000..9378e35e13 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/assembled-tx.js @@ -0,0 +1,388 @@ +import { Account, Address, Contract, Keypair, Server, SorobanRpc, StrKey, TimeoutInfinite, TransactionBuilder, assembleTransaction, hash, nativeToScVal, xdr, } from "soroban-client"; +import { Buffer } from "buffer"; +export class NotImplementedError extends Error { +} +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 const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +export class AssembledTransaction { + options; + tx; + simulationResult; + simulationTransactionData; + server; + toJSON() { + return JSON.stringify({ + method: this.options.method, + tx: this.tx?.toXDR(), + simulationResult: { + auth: this.simulationResult.auth.map(a => a.toXDR('base64')), + retval: this.simulationResult.retval.toXDR('base64'), + }, + simulationTransactionData: this.simulationTransactionData.toXDR('base64'), + }); + } + static fromJSON(options, { tx, simulationResult, simulationTransactionData }) { + const txn = new AssembledTransaction(options); + txn.tx = 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 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.tx = new TransactionBuilder(await tx.getAccount(), { + fee: options.fee?.toString(10) ?? '100', + networkPassphrase: options.networkPassphrase, + }) + .addOperation(contract.call(options.method, ...(options.args ?? []))) + .setTimeout(TimeoutInfinite) + .build(); + return await tx.simulate(); + } + simulate = async () => { + if (!this.tx) + throw new Error('Transaction has not yet been assembled'); + const simulation = await this.server.simulateTransaction(this.tx); + if (!simulation) { + throw new Error('Transaction has not yet been simulated'); + } + if (SorobanRpc.isSimulationError(simulation)) { + throw new Error(`Transaction simulation failed: "${simulation.error}"`); + } + if (SorobanRpc.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)}`); + } + this.simulationResult = simulation.result; + this.simulationTransactionData = simulation.transactionData.build(); + this.tx = assembleTransaction(this.tx, this.options.networkPassphrase, simulation).build(); + return this; + }; + getWallet = async () => { + return this.options.wallet ?? (await import("@stellar/freighter-api")); + }; + /** + * 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 wallet = await this.getWallet(); + let publicKey; + if (await wallet.isConnected() && await wallet.isAllowed()) { + publicKey = (await wallet.getUserInfo()).publicKey; + } + 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.tx) { + throw new Error('Transaction has not yet been simulated'); + } + if (!force && this.isViewCall) { + throw new Error('This is a view call. It requires no signature or sending. Use `force: true` to sign anyway.'); + } + if (!await this.hasRealInvoker()) { + throw new WalletDisconnectedError('Wallet is not connected'); + } + if ((await this.needsNonInvokerSigningBy()).length) { + throw new NeedsMoreSignaturesError('Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.'); + } + const wallet = await this.getWallet(); + const signed = await wallet.signTransaction(this.tx.toXDR(), { + networkPassphrase: this.options.networkPassphrase, + }); + const tx = new SentTransaction(this.options, TransactionBuilder.fromXDR(signed, this.options.networkPassphrase)); + await tx.send(); + return tx; + }; + getStorageExpiration = async (storageType) => { + const key = xdr.LedgerKey.contractData(new xdr.LedgerKeyContractData({ + contract: new Address(this.options.contractId).toScAddress(), + key: xdr.ScVal.scvLedgerKeyContractInstance(), + durability: xdr.ContractDataDurability[storageType](), + })); + const expirationKey = xdr.LedgerKey.expiration(new xdr.LedgerKeyExpiration({ keyHash: hash(key.toXDR()) })); + const entryRes = await this.server.getLedgerEntries(expirationKey); + if (!(entryRes.entries && entryRes.entries.length)) + throw new Error('failed to get ledger entry'); + return entryRes.entries[0].val.expiration().expirationLedgerSeq(); + }; + /** + * 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 signAuthEntriesFor}. Then re-serialize and send to the next account + * in this list. + */ + needsNonInvokerSigningBy = async ({ includeAlreadySigned = false, } = {}) => { + if (!this.tx) { + 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.tx)) { + throw new Error(`Unexpected Transaction type; no operations: ${JSON.stringify(this.tx)}`); + } + const rawInvokeHostFunctionOp = this.tx + .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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor = async ( + /** + * The public key of the account with auth entries that need to be signed. + * + * Must correspond to a key from `await this.needsNonInvokerSigningBy()`. + */ + publicKey, + /** + * Signing function to use to sign each auth entry that `publicKey` account + holder needs to sign. + */ + sign, + /** + * 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('persistent')) => { + if (!this.tx) + 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?'); + if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) + throw new Error('No auth entries for this public key'); + const rawInvokeHostFunctionOp = this.tx + .operations[0]; + const originalAuthEntries = rawInvokeHostFunctionOp.auth ?? []; + for (const entry of originalAuthEntries) { + 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; + const preimage = this.preImageFor(entry, await expiration); + const payload = hash(preimage.toXDR()); + const signature = await sign(payload); + // TODO: do we need this check? + if (!Keypair.fromPublicKey(pk).verify(payload, signature)) { + throw new Error(`Signature doesn't match payload!\n\n` + + ` publicKey: ${pk}\n` + + ` signature: ${signature}`); + } + const sigScVal = nativeToScVal({ + public_key: StrKey.decodeEd25519PublicKey(pk), + signature, + }, { + // force the keys to be interpreted as symbols (expected for + // Soroban [contracttype]s) + // Pr open to fix this type in the gen'd xdr + type: { + public_key: ["symbol", null], + signature: ["symbol", null], + }, + }); + // get a reference to the auth entry, then mutate it + const addrAuth = entry.credentials().address(); + addrAuth.signature(xdr.ScVal.scvVec([sigScVal])); + addrAuth.signatureExpirationLedger(preimage.sorobanAuthorization().signatureExpirationLedger()); + } + }; + get isViewCall() { + const authsCount = this.simulationResult.auth.length; + const writeLength = this.simulationTransactionData.resources().footprint().readWrite().length; + return (authsCount === 0) && (writeLength === 0); + } + hasRealInvoker = async () => { + const account = await this.getAccount(); + return account.accountId() !== NULL_ACCOUNT; + }; + get result() { + return this.options.parseResultXdr(this.simulationResult.retval); + } +} +/** + * 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; + tx; + server; + sendTransactionResponse; + sendTransactionResponseAll; + getTransactionResponse; + getTransactionResponseAll; + constructor(options, tx) { + this.options = options; + this.tx = tx; + this.server = new Server(this.options.rpcUrl, { + allowHttp: this.options.rpcUrl.startsWith("http://"), + }); + this.tx = tx; + } + send = async (secondsToWait = 10) => { + this.sendTransactionResponseAll = await withExponentialBackoff(() => this.server.sendTransaction(this.tx), 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.GetTransactionStatus.NOT_FOUND, secondsToWait); + this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1]; + if (this.getTransactionResponse.status === SorobanRpc.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.tx)}`); + } +} +/** + * 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.log(`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.log(`was gonna wait too long; new waitTime: ${waitTime}ms`); + } + } + totalWaitTime = waitTime + totalWaitTime; + // Try again + attempts.push(await fn()); + if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { + console.log(`${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/convert.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.d.ts new file mode 100644 index 0000000000..6db3b88d39 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.d.ts @@ -0,0 +1,7 @@ +import { xdr } from 'soroban-client'; +export declare function strToScVal(base64Xdr: string): xdr.ScVal; +export declare function scValStrToJs(base64Xdr: string): T; +export declare function scValToJs(val: xdr.ScVal): T; +export declare function addressToScVal(addr: string): xdr.ScVal; +export declare function i128ToScVal(i: bigint): xdr.ScVal; +export declare function u128ToScVal(i: bigint): xdr.ScVal; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.js b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.js new file mode 100644 index 0000000000..4faf03e153 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/convert.js @@ -0,0 +1,94 @@ +import { xdr, Address, nativeToScVal, scValToBigInt, ScInt } from 'soroban-client'; +export function strToScVal(base64Xdr) { + return xdr.ScVal.fromXDR(base64Xdr, 'base64'); +} +export function scValStrToJs(base64Xdr) { + return scValToJs(strToScVal(base64Xdr)); +} +export function scValToJs(val) { + switch (val?.switch()) { + case xdr.ScValType.scvBool(): { + return val.b(); + } + case xdr.ScValType.scvVoid(): + case undefined: { + return 0; + } + case xdr.ScValType.scvU32(): { + return val.u32(); + } + case xdr.ScValType.scvI32(): { + return val.i32(); + } + case xdr.ScValType.scvU64(): + case xdr.ScValType.scvI64(): + case xdr.ScValType.scvU128(): + case xdr.ScValType.scvI128(): + case xdr.ScValType.scvU256(): + case xdr.ScValType.scvI256(): { + return scValToBigInt(val); + } + case xdr.ScValType.scvAddress(): { + return Address.fromScVal(val).toString(); + } + case xdr.ScValType.scvString(): { + return val.str().toString(); + } + case xdr.ScValType.scvSymbol(): { + return val.sym().toString(); + } + case xdr.ScValType.scvBytes(): { + return val.bytes(); + } + case xdr.ScValType.scvVec(): { + return val.vec().map(v => scValToJs(v)); + } + case xdr.ScValType.scvMap(): { + let res = {}; + val.map().forEach((e) => { + let key = scValToJs(e.key()); + let value; + let v = e.val(); + // For now we assume second level maps are real maps. Not perfect but better. + switch (v?.switch()) { + case xdr.ScValType.scvMap(): { + let inner_map = new Map(); + v.map().forEach((e) => { + let key = scValToJs(e.key()); + let value = scValToJs(e.val()); + inner_map.set(key, value); + }); + value = inner_map; + break; + } + default: { + value = scValToJs(e.val()); + } + } + //@ts-ignore + res[key] = value; + }); + return res; + } + case xdr.ScValType.scvContractInstance(): + case xdr.ScValType.scvLedgerKeyNonce(): + case xdr.ScValType.scvTimepoint(): + case xdr.ScValType.scvDuration(): + return val.value(); + // TODO: Add this case when merged + // case xdr.ScValType.scvError(): + default: { + throw new Error(`type not implemented yet: ${val?.switch().name}`); + } + } + ; +} +export function addressToScVal(addr) { + return nativeToScVal(addr, { type: 'address' } /* bug workaround */); +} +export function i128ToScVal(i) { + return new ScInt(i).toI128(); +} +export function u128ToScVal(i) { + return new ScInt(i).toU128(); +} 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 new file mode 100644 index 0000000000..0598452832 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.d.ts @@ -0,0 +1,355 @@ +import { ContractSpec, Address } from 'soroban-client'; +import { Buffer } from "buffer"; +import { AssembledTransaction } from './assembled-tx.js'; +import type { ClassOptions, XDR_BASE64 } from './method-options.js'; +export * from './assembled-tx.js'; +export * from './method-options.js'; +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 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 class Contract { + readonly options: ClassOptions; + spec: ContractSpec; + readonly parsers: { + hello: (xdr: XDR_BASE64) => string; + woid: () => void; + val: (xdr: XDR_BASE64) => any; + u32FailOnEven: (xdr: XDR_BASE64) => Ok | Err | undefined; + u32: (xdr: XDR_BASE64) => u32; + i32: (xdr: XDR_BASE64) => i32; + i64: (xdr: XDR_BASE64) => i64; + struktHel: (xdr: XDR_BASE64) => Array; + strukt: (xdr: XDR_BASE64) => Test; + simple: (xdr: XDR_BASE64) => SimpleEnum; + complex: (xdr: XDR_BASE64) => ComplexEnum; + addresse: (xdr: XDR_BASE64) => string; + bytes: (xdr: XDR_BASE64) => Buffer; + bytesN: (xdr: XDR_BASE64) => Buffer; + card: (xdr: XDR_BASE64) => RoyalCard; + boolean: (xdr: XDR_BASE64) => boolean; + not: (xdr: XDR_BASE64) => boolean; + i128: (xdr: XDR_BASE64) => i128; + u128: (xdr: XDR_BASE64) => u128; + multiArgs: (xdr: XDR_BASE64) => u32; + map: (xdr: XDR_BASE64) => Map; + vec: (xdr: XDR_BASE64) => Array; + tuple: (xdr: XDR_BASE64) => readonly [string, u32]; + option: (xdr: XDR_BASE64) => Option; + u256: (xdr: XDR_BASE64) => u256; + i256: (xdr: XDR_BASE64) => i256; + string: (xdr: XDR_BASE64) => string; + tupleStrukt: (xdr: XDR_BASE64) => TupleStruct; + }; + constructor(options: ClassOptions); + txFromJSON: (json: string) => AssembledTransaction; + hello: ({ hello }: { + hello: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + woid: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + val: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u32FailOnEven: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise | AssembledTransaction | Ok | undefined>>; + u32: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i32: ({ i32_ }: { + i32_: i32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i64: ({ i64_ }: { + i64_: i64; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example contract method which takes a struct + */ + struktHel: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + strukt: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + simple: ({ simple }: { + simple: SimpleEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + complex: ({ complex }: { + complex: ComplexEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + addresse: ({ addresse }: { + addresse: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytes: ({ bytes }: { + bytes: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytesN: ({ bytes_n }: { + bytes_n: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + card: ({ card }: { + card: RoyalCard; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + boolean: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Negates a boolean value + */ + not: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i128: ({ i128 }: { + i128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u128: ({ u128 }: { + u128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + multiArgs: ({ a, b }: { + a: u32; + b: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + map: ({ map }: { + map: Map; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + vec: ({ vec }: { + vec: Array; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + tuple: ({ tuple }: { + tuple: readonly [string, u32]; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example of an optional argument + */ + option: ({ option }: { + option: Option; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + u256: ({ u256 }: { + u256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i256: ({ i256 }: { + i256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + string: ({ string }: { + string: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + 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 new file mode 100644 index 0000000000..b42048e36e --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/index.js @@ -0,0 +1,483 @@ +import { ContractSpec, Address } from 'soroban-client'; +import { Buffer } from "buffer"; +import { AssembledTransaction } from './assembled-tx.js'; +export * from './assembled-tx.js'; +export * from './method-options.js'; +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(); + } +} +if (typeof window !== 'undefined') { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || Buffer; +} +const regex = /Error\(Contract, #(\d+)\)/; +function parseError(message) { + const match = message.match(regex); + if (!match) { + return undefined; + } + if (Errors === undefined) { + return undefined; + } + let i = parseInt(match[1], 10); + let err = Errors[i]; + if (err) { + return new Err(err); + } + return undefined; +} +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 = {})); +const Errors = { + 1: { message: "Please provide an odd number" } +}; +export class Contract { + options; + spec; + parsers = { + hello: (xdr) => { + return this.spec.funcResToNative("hello", xdr); + }, + woid: () => { }, + val: (xdr) => { + return this.spec.funcResToNative("val", xdr); + }, + u32FailOnEven: (xdr) => { + return new Ok(this.spec.funcResToNative("u32_fail_on_even", xdr)); + }, + u32: (xdr) => { + return this.spec.funcResToNative("u32_", xdr); + }, + i32: (xdr) => { + return this.spec.funcResToNative("i32_", xdr); + }, + i64: (xdr) => { + return this.spec.funcResToNative("i64_", xdr); + }, + struktHel: (xdr) => { + return this.spec.funcResToNative("strukt_hel", xdr); + }, + strukt: (xdr) => { + return this.spec.funcResToNative("strukt", xdr); + }, + simple: (xdr) => { + return this.spec.funcResToNative("simple", xdr); + }, + complex: (xdr) => { + return this.spec.funcResToNative("complex", xdr); + }, + addresse: (xdr) => { + return this.spec.funcResToNative("addresse", xdr); + }, + bytes: (xdr) => { + return this.spec.funcResToNative("bytes", xdr); + }, + bytesN: (xdr) => { + return this.spec.funcResToNative("bytes_n", xdr); + }, + card: (xdr) => { + return this.spec.funcResToNative("card", xdr); + }, + boolean: (xdr) => { + return this.spec.funcResToNative("boolean", xdr); + }, + not: (xdr) => { + return this.spec.funcResToNative("not", xdr); + }, + i128: (xdr) => { + return this.spec.funcResToNative("i128", xdr); + }, + u128: (xdr) => { + return this.spec.funcResToNative("u128", xdr); + }, + multiArgs: (xdr) => { + return this.spec.funcResToNative("multi_args", xdr); + }, + map: (xdr) => { + return this.spec.funcResToNative("map", xdr); + }, + vec: (xdr) => { + return this.spec.funcResToNative("vec", xdr); + }, + tuple: (xdr) => { + return this.spec.funcResToNative("tuple", xdr); + }, + option: (xdr) => { + return this.spec.funcResToNative("option", xdr); + }, + u256: (xdr) => { + return this.spec.funcResToNative("u256", xdr); + }, + i256: (xdr) => { + return this.spec.funcResToNative("i256", xdr); + }, + string: (xdr) => { + return this.spec.funcResToNative("string", xdr); + }, + tupleStrukt: (xdr) => { + return this.spec.funcResToNative("tuple_strukt", xdr); + } + }; + 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=" + ]); + } + txFromJSON = (json) => { + const { method, ...tx } = JSON.parse(json); + return AssembledTransaction.fromJSON({ + ...this.options, + method, + parseResultXdr: this.parsers[method], + }, tx); + }; + hello = async ({ hello }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'hello', + args: this.spec.funcArgsToScVals("hello", { hello }), + ...options, + ...this.options, + parseResultXdr: this.parsers['hello'], + }); + }; + woid = async (options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'woid', + args: this.spec.funcArgsToScVals("woid", {}), + ...options, + ...this.options, + parseResultXdr: this.parsers['woid'], + }); + }; + val = async (options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'val', + args: this.spec.funcArgsToScVals("val", {}), + ...options, + ...this.options, + parseResultXdr: this.parsers['val'], + }); + }; + u32FailOnEven = async ({ u32_ }, options = {}) => { + try { + return await AssembledTransaction.fromSimulation({ + method: 'u32_fail_on_even', + args: this.spec.funcArgsToScVals("u32_fail_on_even", { u32_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u32FailOnEven'], + }); + } + catch (e) { + let err = parseError(e.toString()); + if (err) + return err; + throw e; + } + }; + u32 = async ({ u32_ }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'u32_', + args: this.spec.funcArgsToScVals("u32_", { u32_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u32'], + }); + }; + i32 = async ({ i32_ }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'i32_', + args: this.spec.funcArgsToScVals("i32_", { i32_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i32'], + }); + }; + i64 = async ({ i64_ }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'i64_', + args: this.spec.funcArgsToScVals("i64_", { i64_ }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i64'], + }); + }; + /** + * 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, + parseResultXdr: this.parsers['struktHel'], + }); + }; + strukt = async ({ strukt }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'strukt', + args: this.spec.funcArgsToScVals("strukt", { strukt }), + ...options, + ...this.options, + parseResultXdr: this.parsers['strukt'], + }); + }; + simple = async ({ simple }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'simple', + args: this.spec.funcArgsToScVals("simple", { simple }), + ...options, + ...this.options, + parseResultXdr: this.parsers['simple'], + }); + }; + complex = async ({ complex }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'complex', + args: this.spec.funcArgsToScVals("complex", { complex }), + ...options, + ...this.options, + parseResultXdr: this.parsers['complex'], + }); + }; + addresse = async ({ addresse }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'addresse', + args: this.spec.funcArgsToScVals("addresse", { addresse: new Address(addresse) }), + ...options, + ...this.options, + parseResultXdr: this.parsers['addresse'], + }); + }; + bytes = async ({ bytes }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'bytes', + args: this.spec.funcArgsToScVals("bytes", { bytes }), + ...options, + ...this.options, + parseResultXdr: this.parsers['bytes'], + }); + }; + bytesN = async ({ bytes_n }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'bytes_n', + args: this.spec.funcArgsToScVals("bytes_n", { bytes_n }), + ...options, + ...this.options, + parseResultXdr: this.parsers['bytesN'], + }); + }; + card = async ({ card }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'card', + args: this.spec.funcArgsToScVals("card", { card }), + ...options, + ...this.options, + parseResultXdr: this.parsers['card'], + }); + }; + boolean = async ({ boolean }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'boolean', + args: this.spec.funcArgsToScVals("boolean", { boolean }), + ...options, + ...this.options, + parseResultXdr: this.parsers['boolean'], + }); + }; + /** + * Negates a boolean value + */ + not = async ({ boolean }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'not', + args: this.spec.funcArgsToScVals("not", { boolean }), + ...options, + ...this.options, + parseResultXdr: this.parsers['not'], + }); + }; + i128 = async ({ i128 }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'i128', + args: this.spec.funcArgsToScVals("i128", { i128 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i128'], + }); + }; + u128 = async ({ u128 }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'u128', + args: this.spec.funcArgsToScVals("u128", { u128 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u128'], + }); + }; + multiArgs = async ({ a, b }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'multi_args', + args: this.spec.funcArgsToScVals("multi_args", { a, b }), + ...options, + ...this.options, + parseResultXdr: this.parsers['multiArgs'], + }); + }; + map = async ({ map }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'map', + args: this.spec.funcArgsToScVals("map", { map }), + ...options, + ...this.options, + parseResultXdr: this.parsers['map'], + }); + }; + vec = async ({ vec }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'vec', + args: this.spec.funcArgsToScVals("vec", { vec }), + ...options, + ...this.options, + parseResultXdr: this.parsers['vec'], + }); + }; + tuple = async ({ tuple }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'tuple', + args: this.spec.funcArgsToScVals("tuple", { tuple }), + ...options, + ...this.options, + parseResultXdr: this.parsers['tuple'], + }); + }; + /** + * Example of an optional argument + */ + option = async ({ option }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'option', + args: this.spec.funcArgsToScVals("option", { option }), + ...options, + ...this.options, + parseResultXdr: this.parsers['option'], + }); + }; + u256 = async ({ u256 }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'u256', + args: this.spec.funcArgsToScVals("u256", { u256 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['u256'], + }); + }; + i256 = async ({ i256 }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'i256', + args: this.spec.funcArgsToScVals("i256", { i256 }), + ...options, + ...this.options, + parseResultXdr: this.parsers['i256'], + }); + }; + string = async ({ string }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'string', + args: this.spec.funcArgsToScVals("string", { string }), + ...options, + ...this.options, + parseResultXdr: this.parsers['string'], + }); + }; + tupleStrukt = async ({ tuple_strukt }, options = {}) => { + return await AssembledTransaction.fromSimulation({ + method: 'tuple_strukt', + args: this.spec.funcArgsToScVals("tuple_strukt", { tuple_strukt }), + ...options, + ...this.options, + 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 new file mode 100644 index 0000000000..a547f917dd --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.d.ts @@ -0,0 +1,41 @@ +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; +} +export type ClassOptions = { + contractId: string; + networkPassphrase: string; + rpcUrl: string; + /** + * 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: 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 new file mode 100644 index 0000000000..00ad9d3cf4 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/method-options.js @@ -0,0 +1,3 @@ +// 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 new file mode 100644 index 0000000000..1632c2c4df --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/esm/package.json @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000000..4ef0898486 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/assembled-tx.d.ts @@ -0,0 +1,140 @@ +import { Account, Operation, Server, SorobanRpc, xdr } from "soroban-client"; +import type { Memo, MemoType, Transaction } from "soroban-client"; +import { Buffer } from "buffer"; +import type { ClassOptions, MethodOptions, Wallet, XDR_BASE64 } from "./method-options.js"; +export type Tx = Transaction, Operation[]>; +export declare class NotImplementedError extends Error { +} +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.SendTransactionResponse; +type GetTx = SorobanRpc.GetTransactionResponse; +type AssembledTransactionOptions = MethodOptions & ClassOptions & { + method: string; + args?: any[]; + parseResultXdr: (xdr: string | xdr.ScVal) => T; +}; +export declare const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +export declare class AssembledTransaction { + options: AssembledTransactionOptions; + tx: Tx; + simulationResult: SorobanRpc.SimulateHostFunctionResult; + simulationTransactionData: xdr.SorobanTransactionData; + 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; + getWallet: () => 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 view call. + */ + force?: boolean | undefined; + }) => Promise>; + getStorageExpiration: (storageType: 'temporary' | 'persistent') => 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 signAuthEntriesFor}. 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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor: (publicKey: string, sign: (payload: Buffer) => Buffer | Promise, expiration?: number | Promise) => Promise; + get isViewCall(): boolean; + hasRealInvoker: () => Promise; + get result(): T; +} +/** + * 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; + tx: Tx; + server: Server; + sendTransactionResponse?: SendTx; + sendTransactionResponseAll?: SendTx[]; + getTransactionResponse?: GetTx; + getTransactionResponseAll?: GetTx[]; + constructor(options: AssembledTransactionOptions, tx: Tx); + send: (secondsToWait?: number) => Promise; + get result(): T; +} +export {}; diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/convert.d.ts b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/convert.d.ts new file mode 100644 index 0000000000..6db3b88d39 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/convert.d.ts @@ -0,0 +1,7 @@ +import { xdr } from 'soroban-client'; +export declare function strToScVal(base64Xdr: string): xdr.ScVal; +export declare function scValStrToJs(base64Xdr: string): T; +export declare function scValToJs(val: xdr.ScVal): T; +export declare function addressToScVal(addr: string): xdr.ScVal; +export declare function i128ToScVal(i: bigint): xdr.ScVal; +export declare function u128ToScVal(i: bigint): xdr.ScVal; 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 new file mode 100644 index 0000000000..0598452832 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/index.d.ts @@ -0,0 +1,355 @@ +import { ContractSpec, Address } from 'soroban-client'; +import { Buffer } from "buffer"; +import { AssembledTransaction } from './assembled-tx.js'; +import type { ClassOptions, XDR_BASE64 } from './method-options.js'; +export * from './assembled-tx.js'; +export * from './method-options.js'; +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 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 class Contract { + readonly options: ClassOptions; + spec: ContractSpec; + readonly parsers: { + hello: (xdr: XDR_BASE64) => string; + woid: () => void; + val: (xdr: XDR_BASE64) => any; + u32FailOnEven: (xdr: XDR_BASE64) => Ok | Err | undefined; + u32: (xdr: XDR_BASE64) => u32; + i32: (xdr: XDR_BASE64) => i32; + i64: (xdr: XDR_BASE64) => i64; + struktHel: (xdr: XDR_BASE64) => Array; + strukt: (xdr: XDR_BASE64) => Test; + simple: (xdr: XDR_BASE64) => SimpleEnum; + complex: (xdr: XDR_BASE64) => ComplexEnum; + addresse: (xdr: XDR_BASE64) => string; + bytes: (xdr: XDR_BASE64) => Buffer; + bytesN: (xdr: XDR_BASE64) => Buffer; + card: (xdr: XDR_BASE64) => RoyalCard; + boolean: (xdr: XDR_BASE64) => boolean; + not: (xdr: XDR_BASE64) => boolean; + i128: (xdr: XDR_BASE64) => i128; + u128: (xdr: XDR_BASE64) => u128; + multiArgs: (xdr: XDR_BASE64) => u32; + map: (xdr: XDR_BASE64) => Map; + vec: (xdr: XDR_BASE64) => Array; + tuple: (xdr: XDR_BASE64) => readonly [string, u32]; + option: (xdr: XDR_BASE64) => Option; + u256: (xdr: XDR_BASE64) => u256; + i256: (xdr: XDR_BASE64) => i256; + string: (xdr: XDR_BASE64) => string; + tupleStrukt: (xdr: XDR_BASE64) => TupleStruct; + }; + constructor(options: ClassOptions); + txFromJSON: (json: string) => AssembledTransaction; + hello: ({ hello }: { + hello: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + woid: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + val: (options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u32FailOnEven: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise | AssembledTransaction | Ok | undefined>>; + u32: ({ u32_ }: { + u32_: u32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i32: ({ i32_ }: { + i32_: i32; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i64: ({ i64_ }: { + i64_: i64; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example contract method which takes a struct + */ + struktHel: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + strukt: ({ strukt }: { + strukt: Test; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + simple: ({ simple }: { + simple: SimpleEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + complex: ({ complex }: { + complex: ComplexEnum; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + addresse: ({ addresse }: { + addresse: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytes: ({ bytes }: { + bytes: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + bytesN: ({ bytes_n }: { + bytes_n: Buffer; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + card: ({ card }: { + card: RoyalCard; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + boolean: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Negates a boolean value + */ + not: ({ boolean }: { + boolean: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i128: ({ i128 }: { + i128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + u128: ({ u128 }: { + u128: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + multiArgs: ({ a, b }: { + a: u32; + b: boolean; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + map: ({ map }: { + map: Map; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + vec: ({ vec }: { + vec: Array; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + tuple: ({ tuple }: { + tuple: readonly [string, u32]; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + /** + * Example of an optional argument + */ + option: ({ option }: { + option: Option; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>>; + u256: ({ u256 }: { + u256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + i256: ({ i256 }: { + i256: bigint; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + string: ({ string }: { + string: string; + }, options?: { + /** + * The fee to pay for the transaction. Default: 100. + */ + fee?: number; + }) => Promise>; + 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 new file mode 100644 index 0000000000..a547f917dd --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/dist/types/method-options.d.ts @@ -0,0 +1,41 @@ +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; +} +export type ClassOptions = { + contractId: string; + networkPassphrase: string; + rpcUrl: string; + /** + * 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: 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 index ea6452d894..f806a45d18 100644 --- 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 @@ -10,7 +10,7 @@ "dependencies": { "@stellar/freighter-api": "1.5.1", "buffer": "6.0.3", - "soroban-client": "1.0.0-beta.2" + "soroban-client": "1.0.0-beta.3" }, "devDependencies": { "typescript": "5.1.6" @@ -18,15 +18,18 @@ }, "node_modules/@stellar/freighter-api": { "version": "1.5.1", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/@stellar/freighter-api/-/freighter-api-1.5.1.tgz", + "integrity": "sha512-WEnKEqd+xVLnOq6bJv+fLXod8JQyPjzpOKTpH4g7tG9MM1fmXzD3y2SXJlpCIw8kVqtiC4ynWOlSWX+TKO7KiQ==" }, "node_modules/asynckit": { "version": "0.4.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.5.1", - "license": "MIT", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -35,13 +38,16 @@ }, "node_modules/base32.js": { "version": "0.1.0", - "license": "MIT", + "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", @@ -55,18 +61,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/bignumber.js": { "version": "9.1.2", - "license": "MIT", + "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", @@ -81,7 +89,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -89,7 +96,8 @@ }, "node_modules/combined-stream": { "version": "1.0.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -99,20 +107,22 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/follow-redirects": { "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -124,7 +134,8 @@ }, "node_modules/form-data": { "version": "4.0.0", - "license": "MIT", + "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", @@ -136,6 +147,8 @@ }, "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", @@ -149,27 +162,30 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/inherits": { "version": "2.0.4", - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/js-xdr": { "version": "3.0.0", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/js-xdr/-/js-xdr-3.0.0.tgz", + "integrity": "sha512-tSt6UKJ2L7t+yaQURGkHo9kop9qnVbChTlCu62zNiDbDZQoZb/YjUj2iFJ3lgelhfg9p5bhO2o/QX+g36TPsSQ==" }, "node_modules/mime-db": { "version": "1.52.0", - "license": "MIT", + "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", - "license": "MIT", + "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" }, @@ -179,7 +195,8 @@ }, "node_modules/node-gyp-build": { "version": "4.6.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -189,10 +206,13 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "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", @@ -206,12 +226,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/sha.js": { "version": "2.4.11", - "license": "(MIT AND BSD-3-Clause)", + "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" @@ -222,27 +242,30 @@ }, "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==", "hasInstallScript": true, - "license": "MIT", "optional": true, "dependencies": { "node-gyp-build": "^4.6.0" } }, "node_modules/soroban-client": { - "version": "1.0.0-beta.2", - "license": "Apache-2.0", + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-1.0.0-beta.3.tgz", + "integrity": "sha512-BnySnAKmSaDTMn+QV50jpJ24+sxwsUyi78Et120o4lzxyya0MXTXyx2toqVBGrTdEubjO0Fy4+bc+1jkbvGSOg==", "dependencies": { "axios": "^1.4.0", "bignumber.js": "^9.1.1", "buffer": "^6.0.3", - "stellar-base": "v10.0.0-beta.1", + "stellar-base": "v10.0.0-beta.3", "urijs": "^1.19.1" } }, "node_modules/stellar-base": { - "version": "10.0.0-beta.1", - "license": "Apache-2.0", + "version": "10.0.0-beta.3", + "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-beta.3.tgz", + "integrity": "sha512-+B1fOdsDWJEnYSYkKSAVHVngzaqDtD8wdDMT/FC+11MrohP3uGY1OmrEeVn34jiBmUlpYZVudDnpDMSXD4RqDA==", "dependencies": { "base32.js": "^0.1.0", "bignumber.js": "^9.1.2", @@ -257,12 +280,14 @@ }, "node_modules/tweetnacl": { "version": "1.0.3", - "license": "Unlicense" + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "node_modules/typescript": { "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -273,7 +298,8 @@ }, "node_modules/urijs": { "version": "1.19.11", - "license": "MIT" + "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 index efb3acfe25..3959ba67c8 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json @@ -4,7 +4,7 @@ "dependencies": { "@stellar/freighter-api": "1.5.1", "buffer": "6.0.3", - "soroban-client": "1.0.0-beta.2" + "soroban-client": "1.0.0-beta.3" }, "scripts": { "build": "node ./scripts/build.mjs" 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 new file mode 100644 index 0000000000..914cd56495 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/assembled-tx.ts @@ -0,0 +1,584 @@ +import { + Account, + Address, + Contract, + Keypair, + Operation, + Server, + SorobanRpc, + StrKey, + TimeoutInfinite, + TransactionBuilder, + assembleTransaction, + hash, + nativeToScVal, + xdr, +} from "soroban-client"; +import type { Memo, MemoType, Transaction } from "soroban-client"; +import { Buffer } from "buffer"; +import type { + ClassOptions, + MethodOptions, + Wallet, + XDR_BASE64, +} from "./method-options.js"; + +export type Tx = Transaction, Operation[]> + +export class NotImplementedError extends Error { } +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.SendTransactionResponse; +type GetTx = SorobanRpc.GetTransactionResponse; + +type AssembledTransactionOptions = MethodOptions & + ClassOptions & { + method: string; + args?: any[]; + parseResultXdr: (xdr: string | xdr.ScVal) => T; + }; + +export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF" + +export class AssembledTransaction { + public tx: Tx + public simulationResult: SorobanRpc.SimulateHostFunctionResult + public simulationTransactionData: xdr.SorobanTransactionData + private server: Server + + toJSON() { + return JSON.stringify({ + method: this.options.method, + tx: this.tx?.toXDR(), + simulationResult: { + auth: this.simulationResult.auth.map(a => a.toXDR('base64')), + retval: this.simulationResult.retval.toXDR('base64'), + }, + simulationTransactionData: this.simulationTransactionData.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.tx = 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 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.tx = new TransactionBuilder(await tx.getAccount(), { + fee: options.fee?.toString(10) ?? '100', + networkPassphrase: options.networkPassphrase, + }) + .addOperation(contract.call(options.method, ...(options.args ?? []))) + .setTimeout(TimeoutInfinite) + .build(); + + return await tx.simulate() + } + + simulate = async (): Promise => { + if (!this.tx) throw new Error('Transaction has not yet been assembled') + const simulation = await this.server.simulateTransaction(this.tx); + + if (!simulation) { + throw new Error('Transaction has not yet been simulated') + } + + if (SorobanRpc.isSimulationError(simulation)) { + throw new Error(`Transaction simulation failed: "${simulation.error}"`) + } + + if (SorobanRpc.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)}`) + } + + this.simulationResult = simulation.result + this.simulationTransactionData = simulation.transactionData.build() + + this.tx = assembleTransaction( + this.tx, + this.options.networkPassphrase, + simulation + ).build() + + return this + } + + getWallet = async (): Promise => { + return this.options.wallet ?? (await import("@stellar/freighter-api")) + } + + /** + * 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 wallet = await this.getWallet() + + let publicKey: string | undefined + if (await wallet.isConnected() && await wallet.isAllowed()) { + publicKey = (await wallet.getUserInfo()).publicKey + } + + 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 view call. + */ + force?: boolean + }= {}): Promise> => { + if (!this.tx) { + throw new Error('Transaction has not yet been simulated') + } + + if (!force && this.isViewCall) { + throw new Error('This is a view call. It requires no signature or sending. Use `force: true` to sign anyway.') + } + + if (!await this.hasRealInvoker()) { + throw new WalletDisconnectedError('Wallet is not connected') + } + + if ((await this.needsNonInvokerSigningBy()).length) { + throw new NeedsMoreSignaturesError( + 'Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.' + ) + } + + const wallet = await this.getWallet() + const signed = await wallet.signTransaction(this.tx.toXDR(), { + networkPassphrase: this.options.networkPassphrase, + }); + + const tx = new SentTransaction(this.options, TransactionBuilder.fromXDR( + signed, + this.options.networkPassphrase + ) as Tx); + await tx.send() + return tx + } + + getStorageExpiration = async (storageType: 'temporary' | 'persistent') => { + const key = xdr.LedgerKey.contractData( + new xdr.LedgerKeyContractData({ + contract: new Address(this.options.contractId).toScAddress(), + key: xdr.ScVal.scvLedgerKeyContractInstance(), + durability: xdr.ContractDataDurability[storageType](), + }), + ) + + const expirationKey = xdr.LedgerKey.expiration( + new xdr.LedgerKeyExpiration({ keyHash: hash(key.toXDR()) }), + ) + + const entryRes = await this.server.getLedgerEntries(expirationKey) + if (!(entryRes.entries && entryRes.entries.length)) throw new Error('failed to get ledger entry') + + return entryRes.entries[0].val.expiration().expirationLedgerSeq() + } + + /** + * 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 signAuthEntriesFor}. 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.tx) { + 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.tx)) { + throw new Error( + `Unexpected Transaction type; no operations: ${ + JSON.stringify(this.tx) + }` + ) + } + const rawInvokeHostFunctionOp = this.tx + .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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor = async ( + /** + * The public key of the account with auth entries that need to be signed. + * + * Must correspond to a key from `await this.needsNonInvokerSigningBy()`. + */ + publicKey: string, + /** + * Signing function to use to sign each auth entry that `publicKey` account + holder needs to sign. + */ + sign: (payload: Buffer) => Buffer | Promise, + /** + * 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('persistent') + ): Promise => { + if (!this.tx) 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?') + if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) throw new Error('No auth entries for this public key') + + const rawInvokeHostFunctionOp = this.tx + .operations[0] as Operation.InvokeHostFunction + + const originalAuthEntries = rawInvokeHostFunctionOp.auth ?? [] + + for (const entry of originalAuthEntries) { + 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 + + + const preimage = this.preImageFor(entry, await expiration) + const payload = hash(preimage.toXDR()) + const signature = await sign(payload) + + // TODO: do we need this check? + if (!Keypair.fromPublicKey(pk).verify(payload, signature)) { + throw new Error( + `Signature doesn't match payload!\n\n` + + ` publicKey: ${pk}\n` + + ` signature: ${signature}` + ) + } + + const sigScVal = nativeToScVal( + { + public_key: StrKey.decodeEd25519PublicKey(pk), + signature, + }, + { + // force the keys to be interpreted as symbols (expected for + // Soroban [contracttype]s) + // Pr open to fix this type in the gen'd xdr + type: { + public_key: ["symbol", null], + signature: ["symbol", null], + }, + }, + ) + + // get a reference to the auth entry, then mutate it + const addrAuth = entry.credentials().address() + addrAuth.signature(xdr.ScVal.scvVec([sigScVal])) + + addrAuth.signatureExpirationLedger( + preimage.sorobanAuthorization().signatureExpirationLedger() + ) + } + } + + get isViewCall(): boolean { + const authsCount = this.simulationResult.auth.length; + const writeLength = this.simulationTransactionData.resources().footprint().readWrite().length + return (authsCount === 0) && (writeLength === 0); + } + + hasRealInvoker = async (): Promise => { + const account = await this.getAccount() + return account.accountId() !== NULL_ACCOUNT + } + + get result(): T { + return this.options.parseResultXdr(this.simulationResult.retval) + } +} + +/** + * 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: Server + public sendTransactionResponse?: SendTx + public sendTransactionResponseAll?: SendTx[] + public getTransactionResponse?: GetTx + public getTransactionResponseAll?: GetTx[] + + constructor(public options: AssembledTransactionOptions, public tx: Tx) { + this.server = new Server(this.options.rpcUrl, { + allowHttp: this.options.rpcUrl.startsWith("http://"), + }); + this.tx = tx + } + + send = async (secondsToWait: number = 10): Promise => { + this.sendTransactionResponseAll = await withExponentialBackoff( + () => this.server.sendTransaction(this.tx), + 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.GetTransactionStatus.NOT_FOUND, + secondsToWait + ) + + this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1] + if (this.getTransactionResponse.status === SorobanRpc.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.tx)}`) + } +} + +/** + * 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: () => 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.log(`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.log(`was gonna wait too long; new waitTime: ${waitTime}ms`) + } + } + totalWaitTime = waitTime + totalWaitTime + // Try again + attempts.push(await fn()) + if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { + console.log( + `${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 index d0e4e0349f..ed586120e4 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 @@ -1,11 +1,11 @@ import * as SorobanClient from 'soroban-client'; import { ContractSpec, Address } from 'soroban-client'; import { Buffer } from "buffer"; -import { invoke } from './invoke.js'; -import type { ResponseTypes, Wallet, ClassOptions } from './method-options.js' +import { AssembledTransaction } from './assembled-tx.js'; +import type { ResponseTypes, Wallet, ClassOptions, XDR_BASE64 } from './method-options.js'; -export * from './invoke.js' -export * from './method-options.js' +export * from './assembled-tx.js'; +export * from './method-options.js'; export type u32 = number; export type i32 = number; @@ -89,6 +89,7 @@ function parseError(message: string): Err | undefined { return undefined; } + export const networks = { futurenet: { networkPassphrase: "Test SDF Future Network ; October 2022", @@ -121,7 +122,91 @@ const Errors = { } export class Contract { - spec: ContractSpec; + spec: ContractSpec; + public readonly parsers = { + hello: (xdr: XDR_BASE64): string => { + return this.spec.funcResToNative("hello", xdr); + }, + woid: () => {}, + val: (xdr: XDR_BASE64): any => { + return this.spec.funcResToNative("val", xdr); + }, + u32FailOnEven: (xdr: XDR_BASE64): Ok | Err | undefined => { + return new Ok(this.spec.funcResToNative("u32_fail_on_even", xdr)); + }, + u32: (xdr: XDR_BASE64): u32 => { + return this.spec.funcResToNative("u32_", xdr); + }, + i32: (xdr: XDR_BASE64): i32 => { + return this.spec.funcResToNative("i32_", xdr); + }, + i64: (xdr: XDR_BASE64): i64 => { + return this.spec.funcResToNative("i64_", xdr); + }, + struktHel: (xdr: XDR_BASE64): Array => { + return this.spec.funcResToNative("strukt_hel", xdr); + }, + strukt: (xdr: XDR_BASE64): Test => { + return this.spec.funcResToNative("strukt", xdr); + }, + simple: (xdr: XDR_BASE64): SimpleEnum => { + return this.spec.funcResToNative("simple", xdr); + }, + complex: (xdr: XDR_BASE64): ComplexEnum => { + return this.spec.funcResToNative("complex", xdr); + }, + addresse: (xdr: XDR_BASE64): string => { + return this.spec.funcResToNative("addresse", xdr); + }, + bytes: (xdr: XDR_BASE64): Buffer => { + return this.spec.funcResToNative("bytes", xdr); + }, + bytesN: (xdr: XDR_BASE64): Buffer => { + return this.spec.funcResToNative("bytes_n", xdr); + }, + card: (xdr: XDR_BASE64): RoyalCard => { + return this.spec.funcResToNative("card", xdr); + }, + boolean: (xdr: XDR_BASE64): boolean => { + return this.spec.funcResToNative("boolean", xdr); + }, + not: (xdr: XDR_BASE64): boolean => { + return this.spec.funcResToNative("not", xdr); + }, + i128: (xdr: XDR_BASE64): i128 => { + return this.spec.funcResToNative("i128", xdr); + }, + u128: (xdr: XDR_BASE64): u128 => { + return this.spec.funcResToNative("u128", xdr); + }, + multiArgs: (xdr: XDR_BASE64): u32 => { + return this.spec.funcResToNative("multi_args", xdr); + }, + map: (xdr: XDR_BASE64): Map => { + return this.spec.funcResToNative("map", xdr); + }, + vec: (xdr: XDR_BASE64): Array => { + return this.spec.funcResToNative("vec", xdr); + }, + tuple: (xdr: XDR_BASE64): readonly [string, u32] => { + return this.spec.funcResToNative("tuple", xdr); + }, + option: (xdr: XDR_BASE64): Option => { + return this.spec.funcResToNative("option", xdr); + }, + u256: (xdr: XDR_BASE64): u256 => { + return this.spec.funcResToNative("u256", xdr); + }, + i256: (xdr: XDR_BASE64): i256 => { + return this.spec.funcResToNative("i256", xdr); + }, + string: (xdr: XDR_BASE64): string => { + return this.spec.funcResToNative("string", xdr); + }, + tupleStrukt: (xdr: XDR_BASE64): TupleStruct => { + return this.spec.funcResToNative("tuple_strukt", xdr); + } + }; constructor(public readonly options: ClassOptions) { this.spec = new ContractSpec([ "AAAAAQAAAC9UaGlzIGlzIGZyb20gdGhlIHJ1c3QgZG9jIGFib3ZlIHRoZSBzdHJ1Y3QgVGVzdAAAAAAAAAAABFRlc3QAAAADAAAAAAAAAAFhAAAAAAAABAAAAAAAAAABYgAAAAAAAAEAAAAAAAAAAWMAAAAAAAAR", @@ -158,123 +243,80 @@ export class Contract { "AAAAAAAAAAAAAAAEaTI1NgAAAAEAAAAAAAAABGkyNTYAAAANAAAAAQAAAA0=", "AAAAAAAAAAAAAAAGc3RyaW5nAAAAAAABAAAAAAAAAAZzdHJpbmcAAAAAABAAAAABAAAAEA==", "AAAAAAAAAAAAAAAMdHVwbGVfc3RydWt0AAAAAQAAAAAAAAAMdHVwbGVfc3RydWt0AAAH0AAAAAtUdXBsZVN0cnVjdAAAAAABAAAH0AAAAAtUdXBsZVN0cnVjdAA=" - ]); + ]); + } + txFromJSON = (json: string): AssembledTransaction => { + const { method, ...tx } = JSON.parse(json) + return AssembledTransaction.fromJSON( + { + ...this.options, + method, + parseResultXdr: this.parsers[method], + }, + tx, + ); } - hello = async ({hello}: {hello: string}, options: { + hello = async ({hello}: {hello: string}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `string`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'hello', args: this.spec.funcArgsToScVals("hello", {hello}), ...options, ...this.options, - parseResultXdr: (xdr): string => { - return this.spec.funcResToNative("hello", xdr); - }, + parseResultXdr: this.parsers['hello'], }); } - woid = async (options: { + woid = async (options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `void`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'woid', args: this.spec.funcArgsToScVals("woid", {}), ...options, ...this.options, - parseResultXdr: () => {}, + parseResultXdr: this.parsers['woid'], }); } - val = async (options: { + val = async (options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `any`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'val', args: this.spec.funcArgsToScVals("val", {}), ...options, ...this.options, - parseResultXdr: (xdr): any => { - return this.spec.funcResToNative("val", xdr); - }, + parseResultXdr: this.parsers['val'], }); } - u32FailOnEven = async ({u32_}: {u32_: u32}, options: { + u32FailOnEven = async ({u32_}: {u32_: u32}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Ok | Err | undefined`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - try { - return await invoke({ + try { + return await AssembledTransaction.fromSimulation({ method: 'u32_fail_on_even', args: this.spec.funcArgsToScVals("u32_fail_on_even", {u32_}), ...options, ...this.options, - parseResultXdr: (xdr): Ok | Err | undefined => { - return new Ok(this.spec.funcResToNative("u32_fail_on_even", xdr)); - }, + parseResultXdr: this.parsers['u32FailOnEven'], }); } catch (e) { let err = parseError(e.toString()); @@ -284,92 +326,50 @@ export class Contract { } - u32 = async ({u32_}: {u32_: u32}, options: { + u32 = async ({u32_}: {u32_: u32}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `u32`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'u32_', args: this.spec.funcArgsToScVals("u32_", {u32_}), ...options, ...this.options, - parseResultXdr: (xdr): u32 => { - return this.spec.funcResToNative("u32_", xdr); - }, + parseResultXdr: this.parsers['u32'], }); } - i32 = async ({i32_}: {i32_: i32}, options: { + i32 = async ({i32_}: {i32_: i32}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `i32`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'i32_', args: this.spec.funcArgsToScVals("i32_", {i32_}), ...options, ...this.options, - parseResultXdr: (xdr): i32 => { - return this.spec.funcResToNative("i32_", xdr); - }, + parseResultXdr: this.parsers['i32'], }); } - i64 = async ({i64_}: {i64_: i64}, options: { + i64 = async ({i64_}: {i64_: i64}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `i64`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'i64_', args: this.spec.funcArgsToScVals("i64_", {i64_}), ...options, ...this.options, - parseResultXdr: (xdr): i64 => { - return this.spec.funcResToNative("i64_", xdr); - }, + parseResultXdr: this.parsers['i64'], }); } @@ -377,272 +377,146 @@ export class Contract { /** * Example contract method which takes a struct */ -struktHel = async ({strukt}: {strukt: Test}, options: { +struktHel = async ({strukt}: {strukt: Test}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Array`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'strukt_hel', args: this.spec.funcArgsToScVals("strukt_hel", {strukt}), ...options, ...this.options, - parseResultXdr: (xdr): Array => { - return this.spec.funcResToNative("strukt_hel", xdr); - }, + parseResultXdr: this.parsers['struktHel'], }); } - strukt = async ({strukt}: {strukt: Test}, options: { + strukt = async ({strukt}: {strukt: Test}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Test`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'strukt', args: this.spec.funcArgsToScVals("strukt", {strukt}), ...options, ...this.options, - parseResultXdr: (xdr): Test => { - return this.spec.funcResToNative("strukt", xdr); - }, + parseResultXdr: this.parsers['strukt'], }); } - simple = async ({simple}: {simple: SimpleEnum}, options: { + simple = async ({simple}: {simple: SimpleEnum}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `SimpleEnum`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'simple', args: this.spec.funcArgsToScVals("simple", {simple}), ...options, ...this.options, - parseResultXdr: (xdr): SimpleEnum => { - return this.spec.funcResToNative("simple", xdr); - }, + parseResultXdr: this.parsers['simple'], }); } - complex = async ({complex}: {complex: ComplexEnum}, options: { + complex = async ({complex}: {complex: ComplexEnum}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `ComplexEnum`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'complex', args: this.spec.funcArgsToScVals("complex", {complex}), ...options, ...this.options, - parseResultXdr: (xdr): ComplexEnum => { - return this.spec.funcResToNative("complex", xdr); - }, + parseResultXdr: this.parsers['complex'], }); } - addresse = async ({addresse}: {addresse: string}, options: { + addresse = async ({addresse}: {addresse: string}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `string`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'addresse', args: this.spec.funcArgsToScVals("addresse", {addresse: new Address(addresse)}), ...options, ...this.options, - parseResultXdr: (xdr): string => { - return this.spec.funcResToNative("addresse", xdr); - }, + parseResultXdr: this.parsers['addresse'], }); } - bytes = async ({bytes}: {bytes: Buffer}, options: { + bytes = async ({bytes}: {bytes: Buffer}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Buffer`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'bytes', args: this.spec.funcArgsToScVals("bytes", {bytes}), ...options, ...this.options, - parseResultXdr: (xdr): Buffer => { - return this.spec.funcResToNative("bytes", xdr); - }, + parseResultXdr: this.parsers['bytes'], }); } - bytesN = async ({bytes_n}: {bytes_n: Buffer}, options: { + bytesN = async ({bytes_n}: {bytes_n: Buffer}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Buffer`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'bytes_n', args: this.spec.funcArgsToScVals("bytes_n", {bytes_n}), ...options, ...this.options, - parseResultXdr: (xdr): Buffer => { - return this.spec.funcResToNative("bytes_n", xdr); - }, + parseResultXdr: this.parsers['bytesN'], }); } - card = async ({card}: {card: RoyalCard}, options: { + card = async ({card}: {card: RoyalCard}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `RoyalCard`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'card', args: this.spec.funcArgsToScVals("card", {card}), ...options, ...this.options, - parseResultXdr: (xdr): RoyalCard => { - return this.spec.funcResToNative("card", xdr); - }, + parseResultXdr: this.parsers['card'], }); } - boolean = async ({boolean}: {boolean: boolean}, options: { + boolean = async ({boolean}: {boolean: boolean}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `boolean`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'boolean', args: this.spec.funcArgsToScVals("boolean", {boolean}), ...options, ...this.options, - parseResultXdr: (xdr): boolean => { - return this.spec.funcResToNative("boolean", xdr); - }, + parseResultXdr: this.parsers['boolean'], }); } @@ -650,212 +524,114 @@ struktHel = async ({strukt}: {strukt: Test} /** * Negates a boolean value */ -not = async ({boolean}: {boolean: boolean}, options: { +not = async ({boolean}: {boolean: boolean}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `boolean`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'not', args: this.spec.funcArgsToScVals("not", {boolean}), ...options, ...this.options, - parseResultXdr: (xdr): boolean => { - return this.spec.funcResToNative("not", xdr); - }, + parseResultXdr: this.parsers['not'], }); } - i128 = async ({i128}: {i128: i128}, options: { + i128 = async ({i128}: {i128: i128}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `i128`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'i128', args: this.spec.funcArgsToScVals("i128", {i128}), ...options, ...this.options, - parseResultXdr: (xdr): i128 => { - return this.spec.funcResToNative("i128", xdr); - }, + parseResultXdr: this.parsers['i128'], }); } - u128 = async ({u128}: {u128: u128}, options: { + u128 = async ({u128}: {u128: u128}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `u128`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'u128', args: this.spec.funcArgsToScVals("u128", {u128}), ...options, ...this.options, - parseResultXdr: (xdr): u128 => { - return this.spec.funcResToNative("u128", xdr); - }, + parseResultXdr: this.parsers['u128'], }); } - multiArgs = async ({a, b}: {a: u32, b: boolean}, options: { + multiArgs = async ({a, b}: {a: u32, b: boolean}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `u32`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'multi_args', args: this.spec.funcArgsToScVals("multi_args", {a, b}), ...options, ...this.options, - parseResultXdr: (xdr): u32 => { - return this.spec.funcResToNative("multi_args", xdr); - }, + parseResultXdr: this.parsers['multiArgs'], }); } - map = async ({map}: {map: Map}, options: { + map = async ({map}: {map: Map}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Map`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'map', args: this.spec.funcArgsToScVals("map", {map}), ...options, ...this.options, - parseResultXdr: (xdr): Map => { - return this.spec.funcResToNative("map", xdr); - }, + parseResultXdr: this.parsers['map'], }); } - vec = async ({vec}: {vec: Array}, options: { + vec = async ({vec}: {vec: Array}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Array`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'vec', args: this.spec.funcArgsToScVals("vec", {vec}), ...options, ...this.options, - parseResultXdr: (xdr): Array => { - return this.spec.funcResToNative("vec", xdr); - }, + parseResultXdr: this.parsers['vec'], }); } - tuple = async ({tuple}: {tuple: readonly [string, u32]}, options: { + tuple = async ({tuple}: {tuple: readonly [string, u32]}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `readonly [string, u32]`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'tuple', args: this.spec.funcArgsToScVals("tuple", {tuple}), ...options, ...this.options, - parseResultXdr: (xdr): readonly [string, u32] => { - return this.spec.funcResToNative("tuple", xdr); - }, + parseResultXdr: this.parsers['tuple'], }); } @@ -863,152 +639,82 @@ not = async ({boolean}: {boolean: boolean}, /** * Example of an optional argument */ -option = async ({option}: {option: Option}, options: { +option = async ({option}: {option: Option}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `Option`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'option', args: this.spec.funcArgsToScVals("option", {option}), ...options, ...this.options, - parseResultXdr: (xdr): Option => { - return this.spec.funcResToNative("option", xdr); - }, + parseResultXdr: this.parsers['option'], }); } - u256 = async ({u256}: {u256: u256}, options: { + u256 = async ({u256}: {u256: u256}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `u256`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'u256', args: this.spec.funcArgsToScVals("u256", {u256}), ...options, ...this.options, - parseResultXdr: (xdr): u256 => { - return this.spec.funcResToNative("u256", xdr); - }, + parseResultXdr: this.parsers['u256'], }); } - i256 = async ({i256}: {i256: i256}, options: { + i256 = async ({i256}: {i256: i256}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `i256`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'i256', args: this.spec.funcArgsToScVals("i256", {i256}), ...options, ...this.options, - parseResultXdr: (xdr): i256 => { - return this.spec.funcResToNative("i256", xdr); - }, + parseResultXdr: this.parsers['i256'], }); } - string = async ({string}: {string: string}, options: { + string = async ({string}: {string: string}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `string`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'string', args: this.spec.funcArgsToScVals("string", {string}), ...options, ...this.options, - parseResultXdr: (xdr): string => { - return this.spec.funcResToNative("string", xdr); - }, + parseResultXdr: this.parsers['string'], }); } - tupleStrukt = async ({tuple_strukt}: {tuple_strukt: TupleStruct}, options: { + tupleStrukt = async ({tuple_strukt}: {tuple_strukt: TupleStruct}, options: { /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `TupleStruct`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number + fee?: number, } = {}) => { - return await invoke({ + return await AssembledTransaction.fromSimulation({ method: 'tuple_strukt', args: this.spec.funcArgsToScVals("tuple_strukt", {tuple_strukt}), ...options, ...this.options, - parseResultXdr: (xdr): TupleStruct => { - return this.spec.funcResToNative("tuple_strukt", xdr); - }, + parseResultXdr: this.parsers['tupleStrukt'], }); } 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 deleted file mode 100644 index d69166ce12..0000000000 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/src/invoke.ts +++ /dev/null @@ -1,254 +0,0 @@ -import * as SorobanClient from "soroban-client"; -import { SorobanRpc } from "soroban-client"; -import type { - Account, - Memo, - MemoType, - Operation, - Transaction, - xdr, -} from "soroban-client"; -import type { - ClassOptions, - MethodOptions, - ResponseTypes, - Wallet, -} from "./method-options.js"; - -export type Tx = Transaction, Operation[]>; - -/** - * Get account details from the Soroban network for the publicKey currently - * selected in Freighter. If not connected to Freighter, return null. - */ -async function getAccount( - wallet: Wallet, - server: SorobanClient.Server -): Promise { - if (!(await wallet.isConnected()) || !(await wallet.isAllowed())) { - return null; - } - const { publicKey } = await wallet.getUserInfo(); - if (!publicKey) { - return null; - } - return await server.getAccount(publicKey); -} - -export class NotImplementedError extends Error {} - -type Simulation = SorobanRpc.SimulateTransactionResponse; -type SendTx = SorobanRpc.SendTransactionResponse; -type GetTx = SorobanRpc.GetTransactionResponse; - -// defined this way so typeahead shows full union, not named alias -let someRpcResponse: Simulation | SendTx | GetTx; -type SomeRpcResponse = typeof someRpcResponse; - -type InvokeArgs = MethodOptions & - ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal) => T; - }; - -/** - * Invoke a method on the test_custom_types contract. - * - * Uses Freighter to determine the current user and if necessary sign the transaction. - * - * @returns {T}, by default, the parsed XDR from either the simulation or the full transaction. If `simulateOnly` or `fullRpcResponse` are true, returns either the full simulation or the result of sending/getting the transaction to/from the ledger. - */ -export async function invoke( - args: InvokeArgs -): Promise< - R extends undefined - ? T - : R extends "simulated" - ? Simulation - : R extends "full" - ? SomeRpcResponse - : T ->; -export async function invoke({ - method, - args = [], - fee = 100, - responseType, - parseResultXdr, - secondsToWait = 10, - rpcUrl, - networkPassphrase, - contractId, - wallet, -}: InvokeArgs): Promise { - wallet = wallet ?? (await import("@stellar/freighter-api")); - let parse = parseResultXdr; - const server = new SorobanClient.Server(rpcUrl, { - allowHttp: rpcUrl.startsWith("http://"), - }); - const walletAccount = await getAccount(wallet, server); - - // use a placeholder null account if not yet connected to Freighter so that view calls can still work - const account = - walletAccount ?? - new SorobanClient.Account( - "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", - "0" - ); - - const contract = new SorobanClient.Contract(contractId); - - let tx = new SorobanClient.TransactionBuilder(account, { - fee: fee.toString(10), - networkPassphrase, - }) - .addOperation(contract.call(method, ...args)) - .setTimeout(SorobanClient.TimeoutInfinite) - .build(); - const simulated = await server.simulateTransaction(tx); - - if (SorobanRpc.isSimulationError(simulated)) { - throw new Error(simulated.error); - } else if (responseType === "simulated") { - return simulated; - } else if (!simulated.result) { - throw new Error(`invalid simulation: no result in ${simulated}`); - } - - let authsCount = simulated.result.auth.length; - const writeLength = simulated.transactionData.getReadWrite().length; - const isViewCall = (authsCount === 0) && (writeLength === 0); - - if (isViewCall) { - if (responseType === "full") { - return simulated; - } - - return parseResultXdr(simulated.result.retval); - } - - if (authsCount > 1) { - throw new NotImplementedError("Multiple auths not yet supported"); - } - if (authsCount === 1) { - // TODO: figure out how to fix with new SorobanClient - // const auth = SorobanClient.xdr.SorobanAuthorizationEntry.fromXDR(auths![0]!, 'base64') - // if (auth.addressWithNonce() !== undefined) { - // throw new NotImplementedError( - // `This transaction needs to be signed by ${auth.addressWithNonce() - // }; Not yet supported` - // ) - // } - } - - if (!walletAccount) { - throw new Error("Not connected to Freighter"); - } - - tx = await signTx( - wallet, - SorobanClient.assembleTransaction(tx, networkPassphrase, simulated).build(), - networkPassphrase - ); - - const raw = await sendTx(tx, secondsToWait, server); - if (responseType === "full") { - return raw; - } - - // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `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!); - - // if neither of these are present, something went wrong - console.error("Don't know how to parse result! Returning full RPC response."); - return raw; -} - -/** - * Sign a transaction with Freighter and return the fully-reconstructed - * transaction ready to send with {@link sendTx}. - * - * If you need to construct a transaction yourself rather than using `invoke` - * or one of the exported contract methods, you may want to use this function - * to sign the transaction with Freighter. - */ -export async function signTx( - wallet: Wallet, - tx: Tx, - networkPassphrase: string -): Promise { - const signed = await wallet.signTransaction(tx.toXDR(), { - networkPassphrase, - }); - - return SorobanClient.TransactionBuilder.fromXDR( - signed, - networkPassphrase - ) as Tx; -} - -/** - * Send a transaction to the Soroban network. - * - * Wait `secondsToWait` seconds for the transaction to complete (default: 10). - * - * If you need to construct or sign a transaction yourself rather than using - * `invoke` or one of the exported contract methods, you may want to use this - * function for its timeout/`secondsToWait` logic, rather than implementing - * your own. - */ -export async function sendTx( - tx: Tx, - secondsToWait: number, - server: SorobanClient.Server -): Promise { - const sendTransactionResponse = await server.sendTransaction(tx); - - if (sendTransactionResponse.status !== "PENDING" || secondsToWait === 0) { - return sendTransactionResponse; - } - - let getTransactionResponse = await server.getTransaction( - sendTransactionResponse.hash - ); - - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf(); - - let waitTime = 1000; - let exponentialFactor = 1.5; - - while ( - Date.now() < waitUntil && - getTransactionResponse.status === SorobanRpc.GetTransactionStatus.NOT_FOUND - ) { - // Wait a beat - await new Promise((resolve) => setTimeout(resolve, waitTime)); - /// Exponential backoff - waitTime = waitTime * exponentialFactor; - // See if the transaction is complete - getTransactionResponse = await server.getTransaction( - sendTransactionResponse.hash - ); - } - - if (getTransactionResponse.status === SorobanRpc.GetTransactionStatus.NOT_FOUND) { - console.error( - `Waited ${ - secondsToWait - } seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Info: ${JSON.stringify( - sendTransactionResponse, - null, - 2 - )}` - ); - } - - return getTransactionResponse; -} 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 index 05c4c46ca4..030b82c5be 100644 --- 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 @@ -35,21 +35,9 @@ export type ClassOptions = { wallet?: Wallet } -export type MethodOptions = { +export type MethodOptions = { /** * The fee to pay for the transaction. Default: 100. */ fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `{RETURN_TYPE}`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number } diff --git a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs b/cmd/crates/soroban-spec-typescript/src/boilerplate.rs index ae14ae4760..d0541e3e0f 100644 --- a/cmd/crates/soroban-spec-typescript/src/boilerplate.rs +++ b/cmd/crates/soroban-spec-typescript/src/boilerplate.rs @@ -81,8 +81,8 @@ impl Project { [ "package.json", "README.md", + "src/assembled-tx.ts", "src/index.ts", - "src/invoke.ts", "src/method-options.ts", ] .into_iter() diff --git a/cmd/crates/soroban-spec-typescript/src/lib.rs b/cmd/crates/soroban-spec-typescript/src/lib.rs index 5e8f8f2101..028387a346 100644 --- a/cmd/crates/soroban-spec-typescript/src/lib.rs +++ b/cmd/crates/soroban-spec-typescript/src/lib.rs @@ -61,18 +61,43 @@ pub fn generate_from_wasm(wasm: &[u8]) -> Result { } fn generate_class(fns: &[Entry], spec: &[ScSpecEntry]) -> String { - let methods = fns.iter().map(entry_to_ts).join("\n\n "); + 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().unwrap())) .join(",\n "); format!( r#"export class Contract {{ - spec: ContractSpec; + 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} }}"#, @@ -91,7 +116,7 @@ pub fn generate(spec: &[ScSpecEntry]) -> String { let (fns, other): (Vec<_>, Vec<_>) = collected .into_iter() .partition(|entry| matches!(entry, Entry::Function { .. })); - let top = other.iter().map(entry_to_ts).join("\n"); + let top = other.iter().map(entry_to_method).join("\n"); let bottom = generate_class(&fns, spec); format!("{top}\n\n{bottom}") } @@ -114,41 +139,75 @@ fn is_error_enum(entry: &ScSpecEntry) -> bool { matches!(entry, ScSpecEntry::UdtErrorEnumV0(_)) } -fn method_options(return_type: &String) -> String { - format!( - r#"{{ +const METHOD_OPTIONS: &str = r#"{ /** * The fee to pay for the transaction. Default: 100. */ - fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `{return_type}`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {{@link SorobanClient.SorobanRpc.GetTransactionResponse}} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {{@link SorobanClient.SorobanRpc.SendTransactionResponse}} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number - }}"# - ) -} + fee?: number, + }"#; fn jsify_name(name: &String) -> String { name.to_lower_camel_case() } +pub fn entry_to_parser(entry: &Entry) -> Option<(String, String, bool)> { + 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 | undefined"); + } + + let mut output = outputs + .get(0) + .map(|_| format!("this.spec.funcResToNative(\"{name}\", xdr)")) + .unwrap_or_default(); + if is_result { + output = format!("new Ok({output})"); + } + if return_type != "void" { + output = format!(r#"return {output};"#); + }; + let parse_result_xdr = if return_type == "void" { + r#"() => {}"#.to_owned() + } else { + format!( + r#"(xdr: XDR_BASE64): {return_type} => {{ + {output} + }}"# + ) + }; + let js_name = jsify_name(name); + Some((js_name, parse_result_xdr, is_result)) + } else { + None + } +} + + #[allow(clippy::too_many_lines)] -pub fn entry_to_ts(entry: &Entry) -> String { +pub fn entry_to_method(entry: &Entry) -> String { match entry { Entry::Function { doc, name, inputs, - outputs, + .. } => { let input_vals = inputs.iter().map(func_input_to_arg_name).join(", "); let input = (!inputs.is_empty()) @@ -159,59 +218,18 @@ pub fn entry_to_ts(entry: &Entry) -> String { ) }) .unwrap_or_default(); - 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(", ")); - }; let ts_doc = doc_to_ts_doc(doc); - - if is_result { - return_type = return_type - .strip_prefix("Result<") - .unwrap() - .strip_suffix('>') - .unwrap() - .to_owned(); - return_type = format!("Ok<{return_type}> | Err | undefined"); - } - - let mut output = outputs - .get(0) - .map(|_| format!("this.spec.funcResToNative(\"{name}\", xdr)")) - .unwrap_or_default(); - if is_result { - output = format!("new Ok({output})"); - } - if return_type != "void" { - output = format!(r#"return {output};"#); - }; - let parse_result_xdr = if return_type == "void" { - r#"parseResultXdr: () => {}"#.to_owned() - } else { - format!( - r#"parseResultXdr: (xdr): {return_type} => {{ - {output} - }}"# - ) - }; - let js_name = jsify_name(name); - let options = method_options(&return_type); + let (js_name, _, is_result) = 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 mut body = format!( - r#"return await invoke({{ + r#"return await AssembledTransaction.fromSimulation({{ method: '{name}', {args} ...options, ...this.options, - {parse_result_xdr}, + parseResultXdr: this.parsers['{js_name}'], }});"# ); if is_result { @@ -226,8 +244,8 @@ pub fn entry_to_ts(entry: &Entry) -> String { ); } format!( - r#"{ts_doc}{js_name} = async ({input}options: {options} = {{}}) => {{ - {body} + r#"{ts_doc}{js_name} = async ({input}options: {METHOD_OPTIONS} = {{}}) => {{ + {body} }} "# ) 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 new file mode 100644 index 0000000000..914cd56495 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts @@ -0,0 +1,584 @@ +import { + Account, + Address, + Contract, + Keypair, + Operation, + Server, + SorobanRpc, + StrKey, + TimeoutInfinite, + TransactionBuilder, + assembleTransaction, + hash, + nativeToScVal, + xdr, +} from "soroban-client"; +import type { Memo, MemoType, Transaction } from "soroban-client"; +import { Buffer } from "buffer"; +import type { + ClassOptions, + MethodOptions, + Wallet, + XDR_BASE64, +} from "./method-options.js"; + +export type Tx = Transaction, Operation[]> + +export class NotImplementedError extends Error { } +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.SendTransactionResponse; +type GetTx = SorobanRpc.GetTransactionResponse; + +type AssembledTransactionOptions = MethodOptions & + ClassOptions & { + method: string; + args?: any[]; + parseResultXdr: (xdr: string | xdr.ScVal) => T; + }; + +export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF" + +export class AssembledTransaction { + public tx: Tx + public simulationResult: SorobanRpc.SimulateHostFunctionResult + public simulationTransactionData: xdr.SorobanTransactionData + private server: Server + + toJSON() { + return JSON.stringify({ + method: this.options.method, + tx: this.tx?.toXDR(), + simulationResult: { + auth: this.simulationResult.auth.map(a => a.toXDR('base64')), + retval: this.simulationResult.retval.toXDR('base64'), + }, + simulationTransactionData: this.simulationTransactionData.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.tx = 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 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.tx = new TransactionBuilder(await tx.getAccount(), { + fee: options.fee?.toString(10) ?? '100', + networkPassphrase: options.networkPassphrase, + }) + .addOperation(contract.call(options.method, ...(options.args ?? []))) + .setTimeout(TimeoutInfinite) + .build(); + + return await tx.simulate() + } + + simulate = async (): Promise => { + if (!this.tx) throw new Error('Transaction has not yet been assembled') + const simulation = await this.server.simulateTransaction(this.tx); + + if (!simulation) { + throw new Error('Transaction has not yet been simulated') + } + + if (SorobanRpc.isSimulationError(simulation)) { + throw new Error(`Transaction simulation failed: "${simulation.error}"`) + } + + if (SorobanRpc.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)}`) + } + + this.simulationResult = simulation.result + this.simulationTransactionData = simulation.transactionData.build() + + this.tx = assembleTransaction( + this.tx, + this.options.networkPassphrase, + simulation + ).build() + + return this + } + + getWallet = async (): Promise => { + return this.options.wallet ?? (await import("@stellar/freighter-api")) + } + + /** + * 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 wallet = await this.getWallet() + + let publicKey: string | undefined + if (await wallet.isConnected() && await wallet.isAllowed()) { + publicKey = (await wallet.getUserInfo()).publicKey + } + + 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 view call. + */ + force?: boolean + }= {}): Promise> => { + if (!this.tx) { + throw new Error('Transaction has not yet been simulated') + } + + if (!force && this.isViewCall) { + throw new Error('This is a view call. It requires no signature or sending. Use `force: true` to sign anyway.') + } + + if (!await this.hasRealInvoker()) { + throw new WalletDisconnectedError('Wallet is not connected') + } + + if ((await this.needsNonInvokerSigningBy()).length) { + throw new NeedsMoreSignaturesError( + 'Transaction requires more signatures. See `needsNonInvokerSigningBy` for details.' + ) + } + + const wallet = await this.getWallet() + const signed = await wallet.signTransaction(this.tx.toXDR(), { + networkPassphrase: this.options.networkPassphrase, + }); + + const tx = new SentTransaction(this.options, TransactionBuilder.fromXDR( + signed, + this.options.networkPassphrase + ) as Tx); + await tx.send() + return tx + } + + getStorageExpiration = async (storageType: 'temporary' | 'persistent') => { + const key = xdr.LedgerKey.contractData( + new xdr.LedgerKeyContractData({ + contract: new Address(this.options.contractId).toScAddress(), + key: xdr.ScVal.scvLedgerKeyContractInstance(), + durability: xdr.ContractDataDurability[storageType](), + }), + ) + + const expirationKey = xdr.LedgerKey.expiration( + new xdr.LedgerKeyExpiration({ keyHash: hash(key.toXDR()) }), + ) + + const entryRes = await this.server.getLedgerEntries(expirationKey) + if (!(entryRes.entries && entryRes.entries.length)) throw new Error('failed to get ledger entry') + + return entryRes.entries[0].val.expiration().expirationLedgerSeq() + } + + /** + * 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 signAuthEntriesFor}. 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.tx) { + 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.tx)) { + throw new Error( + `Unexpected Transaction type; no operations: ${ + JSON.stringify(this.tx) + }` + ) + } + const rawInvokeHostFunctionOp = this.tx + .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, passing in the function to use to sign each of their auth entries. + * + * 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! + */ + signAuthEntriesFor = async ( + /** + * The public key of the account with auth entries that need to be signed. + * + * Must correspond to a key from `await this.needsNonInvokerSigningBy()`. + */ + publicKey: string, + /** + * Signing function to use to sign each auth entry that `publicKey` account + holder needs to sign. + */ + sign: (payload: Buffer) => Buffer | Promise, + /** + * 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('persistent') + ): Promise => { + if (!this.tx) 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?') + if (needsNonInvokerSigningBy.indexOf(publicKey) === -1) throw new Error('No auth entries for this public key') + + const rawInvokeHostFunctionOp = this.tx + .operations[0] as Operation.InvokeHostFunction + + const originalAuthEntries = rawInvokeHostFunctionOp.auth ?? [] + + for (const entry of originalAuthEntries) { + 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 + + + const preimage = this.preImageFor(entry, await expiration) + const payload = hash(preimage.toXDR()) + const signature = await sign(payload) + + // TODO: do we need this check? + if (!Keypair.fromPublicKey(pk).verify(payload, signature)) { + throw new Error( + `Signature doesn't match payload!\n\n` + + ` publicKey: ${pk}\n` + + ` signature: ${signature}` + ) + } + + const sigScVal = nativeToScVal( + { + public_key: StrKey.decodeEd25519PublicKey(pk), + signature, + }, + { + // force the keys to be interpreted as symbols (expected for + // Soroban [contracttype]s) + // Pr open to fix this type in the gen'd xdr + type: { + public_key: ["symbol", null], + signature: ["symbol", null], + }, + }, + ) + + // get a reference to the auth entry, then mutate it + const addrAuth = entry.credentials().address() + addrAuth.signature(xdr.ScVal.scvVec([sigScVal])) + + addrAuth.signatureExpirationLedger( + preimage.sorobanAuthorization().signatureExpirationLedger() + ) + } + } + + get isViewCall(): boolean { + const authsCount = this.simulationResult.auth.length; + const writeLength = this.simulationTransactionData.resources().footprint().readWrite().length + return (authsCount === 0) && (writeLength === 0); + } + + hasRealInvoker = async (): Promise => { + const account = await this.getAccount() + return account.accountId() !== NULL_ACCOUNT + } + + get result(): T { + return this.options.parseResultXdr(this.simulationResult.retval) + } +} + +/** + * 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: Server + public sendTransactionResponse?: SendTx + public sendTransactionResponseAll?: SendTx[] + public getTransactionResponse?: GetTx + public getTransactionResponseAll?: GetTx[] + + constructor(public options: AssembledTransactionOptions, public tx: Tx) { + this.server = new Server(this.options.rpcUrl, { + allowHttp: this.options.rpcUrl.startsWith("http://"), + }); + this.tx = tx + } + + send = async (secondsToWait: number = 10): Promise => { + this.sendTransactionResponseAll = await withExponentialBackoff( + () => this.server.sendTransaction(this.tx), + 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.GetTransactionStatus.NOT_FOUND, + secondsToWait + ) + + this.getTransactionResponse = this.getTransactionResponseAll[this.getTransactionResponseAll.length - 1] + if (this.getTransactionResponse.status === SorobanRpc.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.tx)}`) + } +} + +/** + * 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: () => 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.log(`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.log(`was gonna wait too long; new waitTime: ${waitTime}ms`) + } + } + totalWaitTime = waitTime + totalWaitTime + // Try again + attempts.push(await fn()) + if (verbose && keepWaitingIf(attempts[attempts.length - 1])) { + console.log( + `${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 index 2b7c15d242..cc11d9252b 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/index.ts +++ b/cmd/crates/soroban-spec-typescript/src/project_template/src/index.ts @@ -1,11 +1,11 @@ import * as SorobanClient from 'soroban-client'; import { ContractSpec, Address } from 'soroban-client'; import { Buffer } from "buffer"; -import { invoke } from './invoke.js'; -import type { ResponseTypes, Wallet, ClassOptions } from './method-options.js' +import { AssembledTransaction } from './assembled-tx.js'; +import type { ResponseTypes, Wallet, ClassOptions, XDR_BASE64 } from './method-options.js'; -export * from './invoke.js' -export * from './method-options.js' +export * from './assembled-tx.js'; +export * from './method-options.js'; export type u32 = number; export type i32 = number; 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 deleted file mode 100644 index 27a85c0ac5..0000000000 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts +++ /dev/null @@ -1,259 +0,0 @@ -import * as SorobanClient from "soroban-client"; -import { SorobanRpc } from "soroban-client"; -import type { - Account, - Memo, - MemoType, - Operation, - Transaction, - xdr, -} from "soroban-client"; -import type { - ClassOptions, - MethodOptions, - ResponseTypes, - Wallet, -} from "./method-options.js"; - -export type Tx = Transaction, Operation[]>; - -export class SendFailedError extends Error { } -/** - * Get account details from the Soroban network for the publicKey currently - * selected in Freighter. If not connected to Freighter, return null. - */ -async function getAccount( - wallet: Wallet, - server: SorobanClient.Server -): Promise { - if (!(await wallet.isConnected()) || !(await wallet.isAllowed())) { - return null; - } - const { publicKey } = await wallet.getUserInfo(); - if (!publicKey) { - return null; - } - return await server.getAccount(publicKey); -} - -export class NotImplementedError extends Error {} - -type Simulation = SorobanRpc.SimulateTransactionResponse; -type SendTx = SorobanRpc.SendTransactionResponse; -type GetTx = SorobanRpc.GetTransactionResponse; - -// defined this way so typeahead shows full union, not named alias -let someRpcResponse: Simulation | SendTx | GetTx; -type SomeRpcResponse = typeof someRpcResponse; - -type InvokeArgs = MethodOptions & - ClassOptions & { - method: string; - args?: any[]; - parseResultXdr: (xdr: string | xdr.ScVal) => T; - }; - -/** - * Invoke a method on the test_custom_types contract. - * - * Uses Freighter to determine the current user and if necessary sign the transaction. - * - * @returns {T}, by default, the parsed XDR from either the simulation or the full transaction. If `simulateOnly` or `fullRpcResponse` are true, returns either the full simulation or the result of sending/getting the transaction to/from the ledger. - */ -export async function invoke( - args: InvokeArgs -): Promise< - R extends undefined - ? T - : R extends "simulated" - ? Simulation - : R extends "full" - ? SomeRpcResponse - : T ->; -export async function invoke({ - method, - args = [], - fee = 100, - responseType, - parseResultXdr, - secondsToWait = 10, - rpcUrl, - networkPassphrase, - contractId, - wallet, -}: InvokeArgs): Promise { - wallet = wallet ?? (await import("@stellar/freighter-api")).default; - let parse = parseResultXdr; - const server = new SorobanClient.Server(rpcUrl, { - allowHttp: rpcUrl.startsWith("http://"), - }); - const walletAccount = await getAccount(wallet, server); - - // use a placeholder null account if not yet connected to Freighter so that view calls can still work - const account = - walletAccount ?? - new SorobanClient.Account( - "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", - "0" - ); - - const contract = new SorobanClient.Contract(contractId); - - let tx = new SorobanClient.TransactionBuilder(account, { - fee: fee.toString(10), - networkPassphrase, - }) - .addOperation(contract.call(method, ...args)) - .setTimeout(SorobanClient.TimeoutInfinite) - .build(); - const simulated = await server.simulateTransaction(tx); - - if (SorobanRpc.isSimulationError(simulated)) { - throw new Error(simulated.error); - } else if (responseType === "simulated") { - return simulated; - } else if (!simulated.result) { - throw new Error(`invalid simulation: no result in ${simulated}`); - } - - let authsCount = simulated.result.auth.length; - const writeLength = simulated.transactionData.getReadWrite().length; - const isViewCall = (authsCount === 0) && (writeLength === 0); - - if (isViewCall) { - if (responseType === "full") { - return simulated; - } - - return parseResultXdr(simulated.result.retval); - } - - if (authsCount > 1) { - throw new NotImplementedError("Multiple auths not yet supported"); - } - if (authsCount === 1) { - // TODO: figure out how to fix with new SorobanClient - // const auth = SorobanClient.xdr.SorobanAuthorizationEntry.fromXDR(auths![0]!, 'base64') - // if (auth.addressWithNonce() !== undefined) { - // throw new NotImplementedError( - // `This transaction needs to be signed by ${auth.addressWithNonce() - // }; Not yet supported` - // ) - // } - } - - if (!walletAccount) { - throw new Error("Not connected to Freighter"); - } - - tx = await signTx( - wallet, - SorobanClient.assembleTransaction(tx, networkPassphrase, simulated).build(), - networkPassphrase - ); - - const raw = await sendTx(tx, secondsToWait, server); - if (responseType === "full") { - return raw; - } - - // if `sendTx` awaited the inclusion of the tx in the ledger, it used - // `getTransaction`, which has a `returnValue` field - if ("returnValue" in raw) return parse(raw.returnValue!); - - // otherwise, it returned the result of `sendTransaction` - if ("errorResult" in raw) { - throw new SendFailedError( - `errorResult.result(): ${JSON.stringify(raw.errorResult?.result())}` - ) - } - - // if neither of these are present, something went wrong - console.error("Don't know how to parse result! Returning full RPC response."); - return raw; -} - -/** - * Sign a transaction with Freighter and return the fully-reconstructed - * transaction ready to send with {@link sendTx}. - * - * If you need to construct a transaction yourself rather than using `invoke` - * or one of the exported contract methods, you may want to use this function - * to sign the transaction with Freighter. - */ -export async function signTx( - wallet: Wallet, - tx: Tx, - networkPassphrase: string -): Promise { - const signed = await wallet.signTransaction(tx.toXDR(), { - networkPassphrase, - }); - - return SorobanClient.TransactionBuilder.fromXDR( - signed, - networkPassphrase - ) as Tx; -} - -/** - * Send a transaction to the Soroban network. - * - * Wait `secondsToWait` seconds for the transaction to complete (default: 10). - * - * If you need to construct or sign a transaction yourself rather than using - * `invoke` or one of the exported contract methods, you may want to use this - * function for its timeout/`secondsToWait` logic, rather than implementing - * your own. - */ -export async function sendTx( - tx: Tx, - secondsToWait: number, - server: SorobanClient.Server -): Promise { - const sendTransactionResponse = await server.sendTransaction(tx); - - if (sendTransactionResponse.status !== "PENDING" || secondsToWait === 0) { - return sendTransactionResponse; - } - - let getTransactionResponse = await server.getTransaction( - sendTransactionResponse.hash - ); - - const waitUntil = new Date(Date.now() + secondsToWait * 1000).valueOf(); - - let waitTime = 1000; - let exponentialFactor = 1.5; - - while ( - Date.now() < waitUntil && - getTransactionResponse.status === SorobanRpc.GetTransactionStatus.NOT_FOUND - ) { - // Wait a beat - await new Promise((resolve) => setTimeout(resolve, waitTime)); - /// Exponential backoff - waitTime = waitTime * exponentialFactor; - // See if the transaction is complete - getTransactionResponse = await server.getTransaction( - sendTransactionResponse.hash - ); - } - - if (getTransactionResponse.status === SorobanRpc.GetTransactionStatus.NOT_FOUND) { - console.error( - `Waited ${ - secondsToWait - } seconds for transaction to complete, but it did not. ` + - `Returning anyway. Check the transaction status manually. ` + - `Info: ${JSON.stringify( - sendTransactionResponse, - null, - 2 - )}` - ); - } - - return getTransactionResponse; -} 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 index d55991bf8f..32d92c00dd 100644 --- 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 @@ -35,21 +35,9 @@ export type ClassOptions = { wallet?: Wallet } -export type MethodOptions = { +export type MethodOptions = { /** * The fee to pay for the transaction. Default: 100. */ fee?: number - /** - * What type of response to return. - * - * - `undefined`, the default, parses the returned XDR as `{RETURN_TYPE}`. Runs preflight, checks to see if auth/signing is required, and sends the transaction if so. If there's no error and `secondsToWait` is positive, awaits the finalized transaction. - * - `'simulated'` will only simulate/preflight the transaction, even if it's a change/set method that requires auth/signing. Returns full preflight info. - * - `'full'` return the full RPC response, meaning either 1. the preflight info, if it's a view/read method that doesn't require auth/signing, or 2. the `sendTransaction` response, if there's a problem with sending the transaction or if you set `secondsToWait` to 0, or 3. the `getTransaction` response, if it's a change method with no `sendTransaction` errors and a positive `secondsToWait`. - */ - responseType?: R - /** - * If the simulation shows that this invocation requires auth/signing, `invoke` will wait `secondsToWait` seconds for the transaction to complete before giving up and returning the incomplete {@link SorobanClient.SorobanRpc.GetTransactionResponse} results (or attempting to parse their probably-missing XDR with `parseResultXdr`, depending on `responseType`). Set this to `0` to skip waiting altogether, which will return you {@link SorobanClient.SorobanRpc.SendTransactionResponse} more quickly, before the transaction has time to be included in the ledger. Default: 10. - */ - secondsToWait?: number } diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.env b/cmd/crates/soroban-spec-typescript/ts-tests/.env index e69de29bb2..39b2454824 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.env +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.env @@ -0,0 +1,2 @@ +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 new file mode 100644 index 0000000000..90af706bb7 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.eslintrc.cjs @@ -0,0 +1,11 @@ +/* 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/contract-token-hash.txt b/cmd/crates/soroban-spec-typescript/ts-tests/contract-token-hash.txt new file mode 100644 index 0000000000..93d06aa5f5 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/contract-token-hash.txt @@ -0,0 +1 @@ +07ec9b8333159ac477239ff1a54c6fc45c5817e03cf0f45b6c9e51727c0e3dc7 diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh new file mode 100755 index 0000000000..84716f8d84 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh @@ -0,0 +1,71 @@ +#!/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\"" + +# 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 config identity fund" + exe eval "./soroban config identity generate alice" + exe eval "./soroban config identity fund alice" + exe eval "./soroban config identity generate bob" + exe eval "./soroban config identity fund bob" +} +function upload() { + exe eval "(./soroban contract $1 --wasm $2 --ignore-checks) > $3" +} +function deploy() { + exe eval "(./soroban contract deploy --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 --id $(cat $1) -- initialize --admin $(./soroban config identity address) --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 --id $(cat $1) -- mint --amount 2000000 --to $(./soroban config identity address $2)" +} +function mint_all() { + mint contract-id-token-a.txt alice + mint contract-id-token-b.txt bob +} + +curl -X POST "http://localhost:8000/soroban/rpc" || { echo "Make sure you're running standalone RPC network on localhost:8000" && exit 1; } +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 index 20622f5c99..fee7c919e3 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json @@ -1,2257 +1,3397 @@ { - "name": "ts-tests", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "hasInstallScript": true, - "devDependencies": { - "@ava/typescript": "^4.1.0", - "@types/node": "^20.4.9", - "ava": "^5.3.1", - "soroban-client": "1.0.0-beta.2", - "typescript": "^5.1.6" - }, - "engines": { - "node": ">=20.0.0", - "npm": ">=9.8.1" - } - }, - "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/@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/@types/node": { - "version": "20.5.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", - "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "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/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": "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/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/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/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", - "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.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/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.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "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/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/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/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/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/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/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-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.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "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/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/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": "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/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.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-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/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/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.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "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/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/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-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-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", - "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/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": "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/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "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/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/node-gyp-build": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", - "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", - "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/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/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": "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/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/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/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": "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/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/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/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/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/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-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/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/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/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": "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/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/soroban-client": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-1.0.0-beta.2.tgz", - "integrity": "sha512-v5h3yvef7HkUD3H26w33NUEgRXcPiOSDWEsVzMloaxsprs3N002tXJHvFF+Uw1eYt50Uk6bvqBgvkLwX10VENw==", - "dev": true, - "dependencies": { - "axios": "^1.4.0", - "bignumber.js": "^9.1.1", - "buffer": "^6.0.3", - "stellar-base": "v10.0.0-beta.1", - "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", - "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/stellar-base": { - "version": "10.0.0-beta.1", - "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-beta.1.tgz", - "integrity": "sha512-zXC5AsbUsLi57JruyeIMv23s3iUxq/P2ZFrSJ+FerLIZjSAjY8EDs4zwY4LCuu7swUu46Lm8GK6sqxUZCPekHw==", - "dev": true, - "dependencies": { - "base32.js": "^0.1.0", - "bignumber.js": "^9.1.2", - "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", - "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/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/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/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/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", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "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==", - "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/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": "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" - } - } - } + "name": "ts-tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "hasInstallScript": true, + "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", + "soroban-client": "1.0.0-beta.2", + "typescript": "^5.1.6" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "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/eslintrc/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/@eslint/eslintrc/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/@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "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/@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.8.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.5.tgz", + "integrity": "sha512-SPlobFgbidfIeOYlzXiEjSYeIJiOCthv+9tSQVpvk4PAdIIc+2SmjNVzWXk9t0Y7dl73Zdf+OgXKHX9XtkqUpw==", + "dev": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.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/typescript-estree/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/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "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.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.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.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "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.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "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": "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/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/axios": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "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.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.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/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/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/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/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/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/eslint/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/eslint/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/eslint/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/eslint/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/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/eslint/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" + } + }, + "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/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.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "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": "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/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=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.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "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.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/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/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/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.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "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/import-fresh/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/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-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", + "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/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": "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/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.6.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "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": "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/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/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": "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/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/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/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-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/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/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": "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/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/soroban-client": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-1.0.0-beta.2.tgz", + "integrity": "sha512-v5h3yvef7HkUD3H26w33NUEgRXcPiOSDWEsVzMloaxsprs3N002tXJHvFF+Uw1eYt50Uk6bvqBgvkLwX10VENw==", + "dev": true, + "dependencies": { + "axios": "^1.4.0", + "bignumber.js": "^9.1.1", + "buffer": "^6.0.3", + "stellar-base": "v10.0.0-beta.1", + "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", + "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/stellar-base": { + "version": "10.0.0-beta.1", + "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-beta.1.tgz", + "integrity": "sha512-zXC5AsbUsLi57JruyeIMv23s3iUxq/P2ZFrSJ+FerLIZjSAjY8EDs4zwY4LCuu7swUu46Lm8GK6sqxUZCPekHw==", + "dev": true, + "dependencies": { + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "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", + "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/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/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.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/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "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": "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" + } + } + } } diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package.json b/cmd/crates/soroban-spec-typescript/ts-tests/package.json index 7ad7212ae4..159bef9cdc 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package.json @@ -1,34 +1,31 @@ { - "private": true, - "type": "module", - "scripts": { - "postinstall": "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": "1.0.0-beta.2", - "typescript": "^5.1.6" - }, - "ava": { - "typescript": { - "rewritePaths": { - "src/": "build/" - }, - "compile": "tsc" - } - }, - "engines": { - "node": ">=20.0.0", - "npm": ">=9.8.1" + "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", + "soroban-client": "1.0.0-beta.2", + "typescript": "^5.1.6" + }, + "ava": { + "typescript": { + "rewritePaths": { + "src/": "build/" + }, + "compile": "tsc" + }, + "require": [ + "dotenv/config" + ] } } 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 index 1c5d047061..007945f693 100644 --- 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 @@ -1,126 +1,130 @@ import test from 'ava' -import { publicKey, rpcUrl, wallet } from './util.js' -import { Contract, Ok, Err, networks, Address } from 'test-custom-types' +import { root, rpcUrl, wallet } from './util.js' +import { Contract, networks } from 'test-custom-types' -const contract = new Contract({ ...networks.standalone, rpcUrl, wallet}); +const addr = root.address; +const publicKey = root.keypair.publicKey(); + +const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }); test('hello', async t => { - t.is(await contract.hello({ hello: 'tests' }), 'tests') + const tx = await contract.hello({ hello: 'tests' }) + t.is(tx.result, 'tests') }) test('woid', async t => { - t.is(await contract.woid(), undefined) + t.is((await contract.woid()).result, undefined) }) -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" })) -}) +// 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 }), 1) + t.is((await contract.u32({ u32_: 1 })).result, 1) }) test('i32', async t => { - t.is(await contract.i32({ i32_: 1 }), 1) + t.is((await contract.i32({ i32_: 1 })).result, 1) }) test('i64', async t => { - t.is(await contract.i64({ i64_: 1n }), 1n) + t.is((await contract.i64({ i64_: 1n })).result, 1n) }) test("strukt_hel", async (t) => { - let test = { a: 0, b: true, c: "world" } - t.deepEqual(await contract.struktHel({ strukt: test }), ["Hello", "world"]) + const test = { a: 0, b: true, c: "world" } + t.deepEqual((await contract.struktHel({ strukt: test })).result, ["Hello", "world"]) }) test("strukt", async (t) => { - let test = { a: 0, b: true, c: "hello" } - t.deepEqual(await contract.strukt({ strukt: test }), test) + const test = { a: 0, b: true, c: "hello" } + t.deepEqual((await contract.strukt({ strukt: test })).result, test) }) test('simple first', async t => { const simple = { tag: 'First', values: undefined } as const - t.deepEqual(await contract.simple({ simple }), simple) + t.deepEqual((await contract.simple({ simple })).result, simple) }) test('simple second', async t => { const simple = { tag: 'Second', values: undefined } as const - t.deepEqual(await contract.simple({ simple }), simple) + t.deepEqual((await contract.simple({ simple })).result, simple) }) test('simple third', async t => { const simple = { tag: 'Third', values: undefined } as const - t.deepEqual(await contract.simple({ simple }), simple) + t.deepEqual((await contract.simple({ simple })).result, simple) }) 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 }), ret) + 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', values: undefined }]] } - t.deepEqual(await contract.complex({ complex: arg }), ret) + 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', values: undefined }] } - t.deepEqual(await contract.complex({ complex: arg }), ret) + 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: [new Address(publicKey), 1n] } - t.deepEqual(await contract.complex({ complex: arg }), ret) + const ret = { tag: 'Asset', values: [addr, 1n] } + t.deepEqual((await contract.complex({ complex: arg })).result, ret) }) test('complex with void', async t => { const complex = { tag: 'Void', values: undefined } as const - t.deepEqual(await contract.complex({ complex }), complex) + t.deepEqual((await contract.complex({ complex })).result, complex) }) test('addresse', async t => { - t.deepEqual(await contract.addresse({ addresse: publicKey }), Address.fromString(publicKey)) + t.deepEqual((await contract.addresse({ addresse: publicKey })).result, addr) }) test('bytes', async t => { const bytes = Buffer.from('hello') - t.deepEqual(await contract.bytes({ bytes }), bytes) + 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 }), 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 }), card) + t.is((await contract.card({ card })).result, card) }) test('boolean', async t => { - t.is(await contract.boolean({ boolean: true }), true) + t.is((await contract.boolean({ boolean: true })).result, true) }) test('not', async t => { - t.is(await contract.not({ boolean: true }), false) + t.is((await contract.not({ boolean: true })).result, false) }) test('i128', async t => { - t.is(await contract.i128({ i128: -1n }), -1n) + t.is((await contract.i128({ i128: -1n })).result, -1n) }) test('u128', async t => { - t.is(await contract.u128({ u128: 1n }), 1n) + t.is((await contract.u128({ u128: 1n })).result, 1n) }) test('multi_args', async t => { - t.is(await contract.multiArgs({ a: 1, b: true }), 1) - t.is(await contract.multiArgs({ a: 1, b: false }), 0) + 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 => { @@ -128,46 +132,46 @@ test('map', async t => { map.set(1, true) map.set(2, false) // map.set(3, 'hahaha') // should throw an error - t.deepEqual(await contract.map({ map }), map) + t.deepEqual((await contract.map({ map })).result, map) }) test('vec', async t => { const vec = [1, 2, 3] - t.deepEqual(await contract.vec({ vec }), vec) + t.deepEqual((await contract.vec({ vec })).result, vec) }) test('tuple', async t => { const tuple = ['hello', 1] as const - t.deepEqual(await contract.tuple({ tuple }), tuple) + t.deepEqual((await contract.tuple({ tuple })).result, tuple) }) test('option', async t => { // this makes sense - t.deepEqual(await contract.option({ option: 1 }), 1) + t.deepEqual((await contract.option({ option: 1 })).result, 1) // this passes but shouldn't - t.deepEqual(await contract.option({ option: undefined }), undefined) + 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({}), undefined) // typing requires argument; implementation would be fine with this - // t.deepEqual(await contract.option({ option: undefined }), undefined) + // 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 }), 1n) + t.is((await contract.u256({ u256: 1n })).result, 1n) }) test('i256', async t => { - t.is(await contract.i256({ i256: -1n }), -1n) + t.is((await contract.i256({ i256: -1n })).result, -1n) }) test('string', async t => { - t.is(await contract.string({ string: 'hello' }), 'hello') + 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', values: undefined }] - t.deepEqual(await contract.tupleStrukt({ tuple_strukt: arg }), res) + t.deepEqual((await contract.tupleStrukt({ tuple_strukt: arg })).result, res) }) diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-result-type.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-result-type.ts new file mode 100644 index 0000000000..5ef6127c19 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-deserialized-result-type.ts @@ -0,0 +1,16 @@ +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("deserialized transaction 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 index dc83679317..bf370c2662 100644 --- 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 @@ -1,20 +1,27 @@ import test from "ava"; -import { wallet, publicKey, rpcUrl } from "./util.js"; -import { Address, Contract, networks } from "test-hello-world"; +import { root, wallet, rpcUrl } from "./util.js"; +import { Contract, networks } from "test-hello-world"; -const contract = new Contract({...networks.standalone, rpcUrl, wallet}); +const contract = new Contract({ ...networks.standalone, rpcUrl, wallet }); test("hello", async (t) => { - t.deepEqual(await contract.hello({ world: "tests" }), ["Hello", "tests"]); + t.deepEqual((await contract.hello({ world: "tests" })).result, ["Hello", "tests"]); }); // Currently must run tests in serial because nonce logic not smart enough to handle concurrent calls. -test.serial.failing("auth", async (t) => { - t.deepEqual(await contract.auth({ addr: publicKey, world: 'lol' }), Address.fromString(publicKey)) +test.serial("auth", async (t) => { + t.deepEqual( + (await contract.auth({ + addr: root.keypair.publicKey(), + world: 'lol' + })).result, + root.address + ) }); -test.serial.failing("inc", async (t) => { - t.is(await contract.getCount(), 0); - t.is(await contract.inc(), 1) - t.is(await contract.getCount(), 1); +test.serial("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 index dfa7c47240..afa3b65124 100644 --- 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 @@ -2,11 +2,11 @@ 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}); +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" }), ["Hello", "tests"]); + 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 new file mode 100644 index 0000000000..fe372ce756 --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-swap.ts @@ -0,0 +1,115 @@ +import test from "ava" +import { wallet, rpcUrl, alice, bob, networkPassphrase } 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, +}) +const swap = new Swap({ ...networks.standalone, rpcUrl, wallet }) + +const amountAToSwap = 2n +const amountBToSwap = 1n + +test('attempting to call `swap.signAndSend()` before signing other auth entries throws a descriptive error', async t => { + const tx = await swap.swap({ + a: alice.keypair.publicKey(), + b: bob.keypair.publicKey(), + 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('root swaps alice 10 A for 1 B', async t => { + const [ + { result: aliceStartingABalance }, + { result: aliceStartingBBalance }, + { result: bobStartingABalance }, + { result: bobStartingBBalance }, + ] = await Promise.all([ + tokenA.balance({ id: alice.keypair.publicKey() }), + tokenB.balance({ id: alice.keypair.publicKey() }), + tokenA.balance({ id: bob.keypair.publicKey() }), + tokenB.balance({ id: bob.keypair.publicKey() }), + ]) + 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 swap.swap({ + a: alice.keypair.publicKey(), + b: bob.keypair.publicKey(), + 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) + + const alicePk = alice.keypair.publicKey() + const bobPk = bob.keypair.publicKey() + 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 = swap.fromJSON.swap(jsonFromRoot) + await txAlice.signAuthEntriesFor(alicePk, payload => alice.keypair.sign(payload)) + + // alice serializes & sends to bob + const jsonFromAlice = txAlice.toJSON() + const txBob = swap.fromJSON.swap(jsonFromAlice) + await txBob.signAuthEntriesFor(bobPk, payload => bob.keypair.sign(payload)) + + // bob serializes & sends back to root + const jsonFromBob = txBob.toJSON() + const txRoot = swap.fromJSON.swap(jsonFromBob) + const result = await txRoot.signAndSend() + + t.truthy(result.sendTransactionResponse, `tx failed: ${JSON.stringify(result)}`) + t.true(result.sendTransactionResponse!.status === 'PENDING', `tx failed: ${JSON.stringify(result)}`) + t.truthy(result.getTransactionResponseAll?.length, `tx failed: ${JSON.stringify(result)}`) + t.truthy(result.getTransactionResponse, `tx failed: ${JSON.stringify(result)}`) + + 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 index 81517944f3..35196acda8 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts @@ -1,27 +1,44 @@ +import { spawnSync } from "node:child_process"; import { Keypair, TransactionBuilder } from "soroban-client"; +import { Address } from 'test-custom-types' -export const rpcUrl = process.env.SOROBAN_RPC_URL ?? "http://localhost:8000/"; -export const secretKey = - "SC36BWNUOCZAO7DMEJNNKFV6BOTPJP7IG5PSHLUOLT6DZFRU3D3XGIXW"; +const rootKeypair = Keypair.fromSecret(spawnSync("./soroban", ["config", "identity", "show"], { shell: true, encoding: "utf8" }).stdout.trim()); +const aliceKeypair = Keypair.fromSecret(spawnSync("./soroban", ["config", "identity", "show", "alice"], { shell: true, encoding: "utf8" }).stdout.trim()); +const bobKeypair = Keypair.fromSecret(spawnSync("./soroban", ["config", "identity", "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()), +} -const keypair = Keypair.fromSecret(secretKey); -export const publicKey = keypair.publicKey(); -const networkPassphrase = "Standalone Network ; February 2017"; +export const bob = { + keypair: bobKeypair, + address: Address.fromString(bobKeypair.publicKey()), +} -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; - } - ) => { +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); + const accountToSign = { + [root.keypair.publicKey()]: root.keypair.secret(), + [alice.keypair.publicKey()]: alice.keypair.secret(), + }[(await this.getUserInfo()).publicKey]; + const keypair = Keypair.fromSecret(accountToSign); t.sign(keypair); return t.toXDR(); - }, -}; + } +} + +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 index b90870f4e5..119437bc59 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/tsconfig.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/tsconfig.json @@ -22,9 +22,9 @@ // "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. */ + "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. */ + "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. */ diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml new file mode 100644 index 0000000000..27aede50d7 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test_swap" +version.workspace = true +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false +rust-version = "1.70" + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev_dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/lib.rs new file mode 100644 index 0000000000..528b84227f --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/lib.rs @@ -0,0 +1,77 @@ +//! This contract performs an atomic token swap between two parties. +//! Parties don't need to know each other and their signatures may be matched +//! off-chain. +//! This example demonstrates how multi-party authorization can be implemented. +#![no_std] + +use soroban_sdk::{contract, contractimpl, token, Address, Env, IntoVal}; + +#[contract] +pub struct AtomicSwapContract; + +#[contractimpl] +impl AtomicSwapContract { + // Swap token A for token B atomically. Settle for the minimum requested price + // for each party (this is an arbitrary choice; both parties could have + // received the full amount as well). + pub fn swap( + env: Env, + a: Address, + b: Address, + token_a: Address, + token_b: Address, + amount_a: i128, + min_b_for_a: i128, + amount_b: i128, + min_a_for_b: i128, + ) { + // Verify preconditions on the minimum price for both parties. + if amount_b < min_b_for_a { + panic!("not enough token B for token A"); + } + if amount_a < min_a_for_b { + panic!("not enough token A for token B"); + } + // Require authorization for a subset of arguments specific to a party. + // Notice, that arguments are symmetric - there is no difference between + // `a` and `b` in the call and hence their signatures can be used + // either for `a` or for `b` role. + a.require_auth_for_args( + (token_a.clone(), token_b.clone(), amount_a, min_b_for_a).into_val(&env), + ); + b.require_auth_for_args( + (token_b.clone(), token_a.clone(), amount_b, min_a_for_b).into_val(&env), + ); + + // Perform the swap by moving tokens from a to b and from b to a. + move_token(&env, &token_a, &a, &b, amount_a, min_a_for_b); + move_token(&env, &token_b, &b, &a, amount_b, min_b_for_a); + } +} + +fn move_token( + env: &Env, + token: &Address, + from: &Address, + to: &Address, + max_spend_amount: i128, + transfer_amount: i128, +) { + let token = token::Client::new(env, token); + let contract_address = env.current_contract_address(); + // This call needs to be authorized by `from` address. It transfers the + // maximum spend amount to the swap contract's address in order to decouple + // the signature from `to` address (so that parties don't need to know each + // other). + token.transfer(from, &contract_address, &max_spend_amount); + // Transfer the necessary amount to `to`. + token.transfer(&contract_address, to, &transfer_amount); + // Refund the remaining balance to `from`. + token.transfer( + &contract_address, + from, + &(&max_spend_amount - &transfer_amount), + ); +} + +mod test; diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs new file mode 100644 index 0000000000..4f38453311 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs @@ -0,0 +1,112 @@ +#![cfg(test)] +extern crate std; + +use super::*; +use soroban_sdk::{ + symbol_short, + testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, + token, Address, Env, IntoVal, +}; +use token::Client as TokenClient; +use token::StellarAssetClient as TokenAdminClient; + +fn create_token_contract<'a>(e: &Env, admin: &Address) -> (TokenClient<'a>, TokenAdminClient<'a>) { + let contract_address = e.register_stellar_asset_contract(admin.clone()); + ( + TokenClient::new(e, &contract_address), + TokenAdminClient::new(e, &contract_address), + ) +} + +fn create_atomic_swap_contract(e: &Env) -> AtomicSwapContractClient { + AtomicSwapContractClient::new(e, &e.register_contract(None, AtomicSwapContract {})) +} + +#[test] +fn test_atomic_swap() { + let env = Env::default(); + env.mock_all_auths(); + + let a = Address::random(&env); + let b = Address::random(&env); + + let token_admin = Address::random(&env); + + let (token_a, token_a_admin) = create_token_contract(&env, &token_admin); + let (token_b, token_b_admin) = create_token_contract(&env, &token_admin); + token_a_admin.mint(&a, &1000); + token_b_admin.mint(&b, &5000); + + let contract = create_atomic_swap_contract(&env); + + contract.swap( + &a, + &b, + &token_a.address, + &token_b.address, + &1000, + &4500, + &5000, + &950, + ); + + assert_eq!( + env.auths(), + std::vec![ + ( + a.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + contract.address.clone(), + symbol_short!("swap"), + ( + token_a.address.clone(), + token_b.address.clone(), + 1000_i128, + 4500_i128 + ) + .into_val(&env), + )), + sub_invocations: std::vec![AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token_a.address.clone(), + symbol_short!("transfer"), + (a.clone(), contract.address.clone(), 1000_i128,).into_val(&env), + )), + sub_invocations: std::vec![] + }] + } + ), + ( + b.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + contract.address.clone(), + symbol_short!("swap"), + ( + token_b.address.clone(), + token_a.address.clone(), + 5000_i128, + 950_i128 + ) + .into_val(&env), + )), + sub_invocations: std::vec![AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token_b.address.clone(), + symbol_short!("transfer"), + (b.clone(), contract.address.clone(), 5000_i128,).into_val(&env), + )), + sub_invocations: std::vec![] + }] + } + ), + ] + ); + + assert_eq!(token_a.balance(&a), 50); + assert_eq!(token_a.balance(&b), 950); + + assert_eq!(token_b.balance(&a), 4500); + assert_eq!(token_b.balance(&b), 500); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml new file mode 100644 index 0000000000..7a81bee777 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "test_token" +version.workspace = true +description = "Soroban standard token contract" +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false +rust-version = "1.70" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = { workspace = true } +soroban-token-sdk = { workspace = true } + +[dev_dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/admin.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/admin.rs new file mode 100644 index 0000000000..a820bf040b --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/admin.rs @@ -0,0 +1,18 @@ +use soroban_sdk::{Address, Env}; + +use crate::storage_types::DataKey; + +pub fn has_administrator(e: &Env) -> bool { + let key = DataKey::Admin; + e.storage().instance().has(&key) +} + +pub fn read_administrator(e: &Env) -> Address { + let key = DataKey::Admin; + e.storage().instance().get(&key).unwrap() +} + +pub fn write_administrator(e: &Env, id: &Address) { + let key = DataKey::Admin; + e.storage().instance().set(&key, id); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/allowance.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/allowance.rs new file mode 100644 index 0000000000..550665ea8d --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/allowance.rs @@ -0,0 +1,63 @@ +use crate::storage_types::{AllowanceDataKey, AllowanceValue, DataKey}; +use soroban_sdk::{Address, Env}; + +pub fn read_allowance(e: &Env, from: Address, spender: Address) -> AllowanceValue { + let key = DataKey::Allowance(AllowanceDataKey { from, spender }); + if let Some(allowance) = e.storage().temporary().get::<_, AllowanceValue>(&key) { + if allowance.expiration_ledger < e.ledger().sequence() { + AllowanceValue { + amount: 0, + expiration_ledger: allowance.expiration_ledger, + } + } else { + allowance + } + } else { + AllowanceValue { + amount: 0, + expiration_ledger: 0, + } + } +} + +pub fn write_allowance( + e: &Env, + from: Address, + spender: Address, + amount: i128, + expiration_ledger: u32, +) { + let allowance = AllowanceValue { + amount, + expiration_ledger, + }; + + if amount > 0 && expiration_ledger < e.ledger().sequence() { + panic!("expiration_ledger is less than ledger seq when amount > 0") + } + + let key = DataKey::Allowance(AllowanceDataKey { from, spender }); + e.storage().temporary().set(&key.clone(), &allowance); + + if amount > 0 { + let live_for = expiration_ledger + .checked_sub(e.ledger().sequence()) + .unwrap(); + + e.storage().temporary().bump(&key, live_for, live_for) + } +} + +pub fn spend_allowance(e: &Env, from: Address, spender: Address, amount: i128) { + let allowance = read_allowance(e, from.clone(), spender.clone()); + if allowance.amount < amount { + panic!("insufficient allowance"); + } + write_allowance( + e, + from, + spender, + allowance.amount - amount, + allowance.expiration_ledger, + ); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/balance.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/balance.rs new file mode 100644 index 0000000000..40a730402a --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/balance.rs @@ -0,0 +1,35 @@ +use crate::storage_types::{DataKey, BALANCE_BUMP_AMOUNT, BALANCE_LIFETIME_THRESHOLD}; +use soroban_sdk::{Address, Env}; + +pub fn read_balance(e: &Env, addr: Address) -> i128 { + let key = DataKey::Balance(addr); + if let Some(balance) = e.storage().persistent().get::(&key) { + e.storage() + .persistent() + .bump(&key, BALANCE_LIFETIME_THRESHOLD, BALANCE_BUMP_AMOUNT); + balance + } else { + 0 + } +} + +fn write_balance(e: &Env, addr: Address, amount: i128) { + let key = DataKey::Balance(addr); + e.storage().persistent().set(&key, &amount); + e.storage() + .persistent() + .bump(&key, BALANCE_LIFETIME_THRESHOLD, BALANCE_BUMP_AMOUNT); +} + +pub fn receive_balance(e: &Env, addr: Address, amount: i128) { + let balance = read_balance(e, addr.clone()); + write_balance(e, addr, balance + amount); +} + +pub fn spend_balance(e: &Env, addr: Address, amount: i128) { + let balance = read_balance(e, addr.clone()); + if balance < amount { + panic!("insufficient balance"); + } + write_balance(e, addr, balance - amount); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/contract.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/contract.rs new file mode 100644 index 0000000000..35d2a3e44a --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/contract.rs @@ -0,0 +1,174 @@ +//! This contract demonstrates a sample implementation of the Soroban token +//! interface. +use crate::admin::{has_administrator, read_administrator, write_administrator}; +use crate::allowance::{read_allowance, spend_allowance, write_allowance}; +use crate::balance::{read_balance, receive_balance, spend_balance}; +use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata}; +use crate::storage_types::{INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD}; +use soroban_sdk::token::{self, Interface as _}; +use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use soroban_token_sdk::metadata::TokenMetadata; +use soroban_token_sdk::TokenUtils; + +fn check_nonnegative_amount(amount: i128) { + if amount < 0 { + panic!("negative amount is not allowed: {}", amount) + } +} + +#[contract] +pub struct Token; + +#[contractimpl] +impl Token { + pub fn initialize(e: Env, admin: Address, decimal: u32, name: String, symbol: String) { + if has_administrator(&e) { + panic!("already initialized") + } + write_administrator(&e, &admin); + if decimal > u8::MAX.into() { + panic!("Decimal must fit in a u8"); + } + + write_metadata( + &e, + TokenMetadata { + decimal, + name, + symbol, + }, + ) + } + + pub fn mint(e: Env, to: Address, amount: i128) { + check_nonnegative_amount(amount); + let admin = read_administrator(&e); + admin.require_auth(); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + receive_balance(&e, to.clone(), amount); + TokenUtils::new(&e).events().mint(admin, to, amount); + } + + pub fn set_admin(e: Env, new_admin: Address) { + let admin = read_administrator(&e); + admin.require_auth(); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + write_administrator(&e, &new_admin); + TokenUtils::new(&e).events().set_admin(admin, new_admin); + } +} + +#[contractimpl] +impl token::Interface for Token { + fn allowance(e: Env, from: Address, spender: Address) -> i128 { + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + read_allowance(&e, from, spender).amount + } + + fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { + from.require_auth(); + + check_nonnegative_amount(amount); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); + TokenUtils::new(&e) + .events() + .approve(from, spender, amount, expiration_ledger); + } + + fn balance(e: Env, id: Address) -> i128 { + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + read_balance(&e, id) + } + + fn spendable_balance(e: Env, id: Address) -> i128 { + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + read_balance(&e, id) + } + + fn transfer(e: Env, from: Address, to: Address, amount: i128) { + from.require_auth(); + + check_nonnegative_amount(amount); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + spend_balance(&e, from.clone(), amount); + receive_balance(&e, to.clone(), amount); + TokenUtils::new(&e).events().transfer(from, to, amount); + } + + fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { + spender.require_auth(); + + check_nonnegative_amount(amount); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + spend_allowance(&e, from.clone(), spender, amount); + spend_balance(&e, from.clone(), amount); + receive_balance(&e, to.clone(), amount); + TokenUtils::new(&e).events().transfer(from, to, amount) + } + + fn burn(e: Env, from: Address, amount: i128) { + from.require_auth(); + + check_nonnegative_amount(amount); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + spend_balance(&e, from.clone(), amount); + TokenUtils::new(&e).events().burn(from, amount); + } + + fn burn_from(e: Env, spender: Address, from: Address, amount: i128) { + spender.require_auth(); + + check_nonnegative_amount(amount); + + e.storage() + .instance() + .bump(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + + spend_allowance(&e, from.clone(), spender, amount); + spend_balance(&e, from.clone(), amount); + TokenUtils::new(&e).events().burn(from, amount) + } + + fn decimals(e: Env) -> u32 { + read_decimal(&e) + } + + fn name(e: Env) -> String { + read_name(&e) + } + + fn symbol(e: Env) -> String { + read_symbol(&e) + } +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/lib.rs new file mode 100644 index 0000000000..b5f04e4dcf --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/lib.rs @@ -0,0 +1,11 @@ +#![no_std] + +mod admin; +mod allowance; +mod balance; +mod contract; +mod metadata; +mod storage_types; +mod test; + +pub use crate::contract::TokenClient; diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/metadata.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/metadata.rs new file mode 100644 index 0000000000..715feeeaa1 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/metadata.rs @@ -0,0 +1,22 @@ +use soroban_sdk::{Env, String}; +use soroban_token_sdk::{metadata::TokenMetadata, TokenUtils}; + +pub fn read_decimal(e: &Env) -> u32 { + let util = TokenUtils::new(e); + util.metadata().get_metadata().decimal +} + +pub fn read_name(e: &Env) -> String { + let util = TokenUtils::new(e); + util.metadata().get_metadata().name +} + +pub fn read_symbol(e: &Env) -> String { + let util = TokenUtils::new(e); + util.metadata().get_metadata().symbol +} + +pub fn write_metadata(e: &Env, metadata: TokenMetadata) { + let util = TokenUtils::new(e); + util.metadata().set_metadata(&metadata); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/storage_types.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/storage_types.rs new file mode 100644 index 0000000000..5710c05749 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/storage_types.rs @@ -0,0 +1,31 @@ +use soroban_sdk::{contracttype, Address}; + +pub(crate) const DAY_IN_LEDGERS: u32 = 17280; +pub(crate) const INSTANCE_BUMP_AMOUNT: u32 = 7 * DAY_IN_LEDGERS; +pub(crate) const INSTANCE_LIFETIME_THRESHOLD: u32 = INSTANCE_BUMP_AMOUNT - DAY_IN_LEDGERS; + +pub(crate) const BALANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; +pub(crate) const BALANCE_LIFETIME_THRESHOLD: u32 = BALANCE_BUMP_AMOUNT - DAY_IN_LEDGERS; + +#[derive(Clone)] +#[contracttype] +pub struct AllowanceDataKey { + pub from: Address, + pub spender: Address, +} + +#[contracttype] +pub struct AllowanceValue { + pub amount: i128, + pub expiration_ledger: u32, +} + +#[derive(Clone)] +#[contracttype] +pub enum DataKey { + Allowance(AllowanceDataKey), + Balance(Address), + Nonce(Address), + State(Address), + Admin, +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs new file mode 100644 index 0000000000..9aae46eba3 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs @@ -0,0 +1,256 @@ +#![cfg(test)] +extern crate std; + +use crate::{contract::Token, TokenClient}; +use soroban_sdk::{ + symbol_short, + testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, + Address, Env, IntoVal, Symbol, +}; + +fn create_token<'a>(e: &Env, admin: &Address) -> TokenClient<'a> { + let token = TokenClient::new(e, &e.register_contract(None, Token {})); + token.initialize(admin, &7, &"name".into_val(e), &"symbol".into_val(e)); + token +} + +#[test] +fn test() { + let e = Env::default(); + e.mock_all_auths(); + + let admin1 = Address::random(&e); + let admin2 = Address::random(&e); + let user1 = Address::random(&e); + let user2 = Address::random(&e); + let user3 = Address::random(&e); + let token = create_token(&e, &admin1); + + token.mint(&user1, &1000); + assert_eq!( + e.auths(), + std::vec![( + admin1.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("mint"), + (&user1, 1000_i128).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + assert_eq!(token.balance(&user1), 1000); + + token.approve(&user2, &user3, &500, &200); + assert_eq!( + e.auths(), + std::vec![( + user2.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("approve"), + (&user2, &user3, 500_i128, 200_u32).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + assert_eq!(token.allowance(&user2, &user3), 500); + + token.transfer(&user1, &user2, &600); + assert_eq!( + e.auths(), + std::vec![( + user1.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("transfer"), + (&user1, &user2, 600_i128).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + assert_eq!(token.balance(&user1), 400); + assert_eq!(token.balance(&user2), 600); + + token.transfer_from(&user3, &user2, &user1, &400); + assert_eq!( + e.auths(), + std::vec![( + user3.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + Symbol::new(&e, "transfer_from"), + (&user3, &user2, &user1, 400_i128).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + assert_eq!(token.balance(&user1), 800); + assert_eq!(token.balance(&user2), 200); + + token.transfer(&user1, &user3, &300); + assert_eq!(token.balance(&user1), 500); + assert_eq!(token.balance(&user3), 300); + + token.set_admin(&admin2); + assert_eq!( + e.auths(), + std::vec![( + admin1.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("set_admin"), + (&admin2,).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + + // Increase to 500 + token.approve(&user2, &user3, &500, &200); + assert_eq!(token.allowance(&user2, &user3), 500); + token.approve(&user2, &user3, &0, &200); + assert_eq!( + e.auths(), + std::vec![( + user2.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("approve"), + (&user2, &user3, 0_i128, 200_u32).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + assert_eq!(token.allowance(&user2, &user3), 0); +} + +#[test] +fn test_burn() { + let e = Env::default(); + e.mock_all_auths(); + + let admin = Address::random(&e); + let user1 = Address::random(&e); + let user2 = Address::random(&e); + let token = create_token(&e, &admin); + + token.mint(&user1, &1000); + assert_eq!(token.balance(&user1), 1000); + + token.approve(&user1, &user2, &500, &200); + assert_eq!(token.allowance(&user1, &user2), 500); + + token.burn_from(&user2, &user1, &500); + assert_eq!( + e.auths(), + std::vec![( + user2.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("burn_from"), + (&user2, &user1, 500_i128).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + + assert_eq!(token.allowance(&user1, &user2), 0); + assert_eq!(token.balance(&user1), 500); + assert_eq!(token.balance(&user2), 0); + + token.burn(&user1, &500); + assert_eq!( + e.auths(), + std::vec![( + user1.clone(), + AuthorizedInvocation { + function: AuthorizedFunction::Contract(( + token.address.clone(), + symbol_short!("burn"), + (&user1, 500_i128).into_val(&e), + )), + sub_invocations: std::vec![] + } + )] + ); + + assert_eq!(token.balance(&user1), 0); + assert_eq!(token.balance(&user2), 0); +} + +#[test] +#[should_panic(expected = "insufficient balance")] +fn transfer_insufficient_balance() { + let e = Env::default(); + e.mock_all_auths(); + + let admin = Address::random(&e); + let user1 = Address::random(&e); + let user2 = Address::random(&e); + let token = create_token(&e, &admin); + + token.mint(&user1, &1000); + assert_eq!(token.balance(&user1), 1000); + + token.transfer(&user1, &user2, &1001); +} + +#[test] +#[should_panic(expected = "insufficient allowance")] +fn transfer_from_insufficient_allowance() { + let e = Env::default(); + e.mock_all_auths(); + + let admin = Address::random(&e); + let user1 = Address::random(&e); + let user2 = Address::random(&e); + let user3 = Address::random(&e); + let token = create_token(&e, &admin); + + token.mint(&user1, &1000); + assert_eq!(token.balance(&user1), 1000); + + token.approve(&user1, &user3, &100, &200); + assert_eq!(token.allowance(&user1, &user3), 100); + + token.transfer_from(&user3, &user1, &user2, &101); +} + +#[test] +#[should_panic(expected = "already initialized")] +fn initialize_already_initialized() { + let e = Env::default(); + let admin = Address::random(&e); + let token = create_token(&e, &admin); + + token.initialize(&admin, &10, &"name".into_val(&e), &"symbol".into_val(&e)); +} + +#[test] +#[should_panic(expected = "Decimal must fit in a u8")] +fn decimal_is_over_max() { + let e = Env::default(); + let admin = Address::random(&e); + let token = TokenClient::new(&e, &e.register_contract(None, Token {})); + token.initialize( + &admin, + &(u32::from(u8::MAX) + 1), + &"name".into_val(&e), + &"symbol".into_val(&e), + ); +}