diff --git a/src/contract/assembled_transaction.ts b/src/contract/assembled_transaction.ts index d038051aa..aca36910e 100644 --- a/src/contract/assembled_transaction.ts +++ b/src/contract/assembled_transaction.ts @@ -23,11 +23,7 @@ import { Api } from "../rpc/api"; import { assembleTransaction } from "../rpc/transaction"; import type { Client } from "./client"; import { Err } from "./rust_result"; -import { - contractErrorPattern, - implementsToString, - getAccount -} from "./utils"; +import { contractErrorPattern, implementsToString, getAccount } from "./utils"; import { DEFAULT_TIMEOUT } from "./types"; import { SentTransaction } from "./sent_transaction"; import { Spec } from "./spec"; @@ -311,14 +307,14 @@ export class AssembledTransaction { * logic. */ static Errors = { - ExpiredState: class ExpiredStateError extends Error { }, - RestorationFailure: class RestoreFailureError extends Error { }, - NeedsMoreSignatures: class NeedsMoreSignaturesError extends Error { }, - NoSignatureNeeded: class NoSignatureNeededError extends Error { }, - NoUnsignedNonInvokerAuthEntries: class NoUnsignedNonInvokerAuthEntriesError extends Error { }, - NoSigner: class NoSignerError extends Error { }, - NotYetSimulated: class NotYetSimulatedError extends Error { }, - FakeAccount: class FakeAccountError extends Error { }, + ExpiredState: class ExpiredStateError extends Error {}, + RestorationFailure: class RestoreFailureError extends Error {}, + NeedsMoreSignatures: class NeedsMoreSignaturesError extends Error {}, + NoSignatureNeeded: class NoSignatureNeededError extends Error {}, + NoUnsignedNonInvokerAuthEntries: class NoUnsignedNonInvokerAuthEntriesError extends Error {}, + NoSigner: class NoSignerError extends Error {}, + NotYetSimulated: class NotYetSimulatedError extends Error {}, + FakeAccount: class FakeAccountError extends Error {}, }; /** @@ -353,19 +349,19 @@ export class AssembledTransaction { retval: XDR_BASE64; }; simulationTransactionData: XDR_BASE64; - }, + } ): AssembledTransaction { const txn = new AssembledTransaction(options); txn.built = TransactionBuilder.fromXDR(tx, options.networkPassphrase) as Tx; txn.simulationResult = { auth: simulationResult.auth.map((a) => - xdr.SorobanAuthorizationEntry.fromXDR(a, "base64"), + xdr.SorobanAuthorizationEntry.fromXDR(a, "base64") ), retval: xdr.ScVal.fromXDR(simulationResult.retval, "base64"), }; txn.simulationTransactionData = xdr.SorobanTransactionData.fromXDR( simulationTransactionData, - "base64", + "base64" ); return txn; } @@ -374,39 +370,49 @@ export class AssembledTransaction { * Serialize the AssembledTransaction to a base64-encoded XDR string. */ toXDR(): string { - if(!this.built) throw new Error( + if (!this.built) + throw new Error( "Transaction has not yet been simulated; " + - "call `AssembledTransaction.simulate` first.", + "call `AssembledTransaction.simulate` first." ); - return this.built?.toEnvelope().toXDR('base64'); + return this.built?.toEnvelope().toXDR("base64"); } /** * Deserialize the AssembledTransaction from a base64-encoded XDR string. */ static fromXDR( - options: Omit, "args" | "method" | "parseResultXdr">, + options: Omit< + AssembledTransactionOptions, + "args" | "method" | "parseResultXdr" + >, encodedXDR: string, spec: Spec ): AssembledTransaction { const envelope = xdr.TransactionEnvelope.fromXDR(encodedXDR, "base64"); - const built = TransactionBuilder.fromXDR(envelope, options.networkPassphrase) as Tx; + const built = TransactionBuilder.fromXDR( + envelope, + options.networkPassphrase + ) as Tx; const operation = built.operations[0] as Operation.InvokeHostFunction; - if (!operation?.func?.value || typeof operation.func.value !== 'function') { - throw new Error("Could not extract the method from the transaction envelope."); + if (!operation?.func?.value || typeof operation.func.value !== "function") { + throw new Error( + "Could not extract the method from the transaction envelope." + ); } const invokeContractArgs = operation.func.value() as xdr.InvokeContractArgs; if (!invokeContractArgs?.functionName) { - throw new Error("Could not extract the method name from the transaction envelope."); + throw new Error( + "Could not extract the method name from the transaction envelope." + ); } - const method = invokeContractArgs.functionName().toString('utf-8'); - const txn = new AssembledTransaction( - { ...options, - method, - parseResultXdr: (result: xdr.ScVal) => - spec.funcResToNative(method, result), - } - ); + const method = invokeContractArgs.functionName().toString("utf-8"); + const txn = new AssembledTransaction({ + ...options, + method, + parseResultXdr: (result: xdr.ScVal) => + spec.funcResToNative(method, result), + }); txn.built = built; return txn; } @@ -437,15 +443,12 @@ export class AssembledTransaction { * }) */ static async build( - options: AssembledTransactionOptions, + options: AssembledTransactionOptions ): Promise> { const tx = new AssembledTransaction(options); const contract = new Contract(options.contractId); - const account = await getAccount( - options, - tx.server - ); + const account = await getAccount(options, tx.server); tx.raw = new TransactionBuilder(account, { fee: options.fee ?? BASE_FEE, @@ -470,19 +473,23 @@ export class AssembledTransaction { fee, networkPassphrase: options.networkPassphrase, }) - .setSorobanData(sorobanData instanceof SorobanDataBuilder ? sorobanData.build() : sorobanData) + .setSorobanData( + sorobanData instanceof SorobanDataBuilder + ? sorobanData.build() + : sorobanData + ) .addOperation(Operation.restoreFootprint({})) .setTimeout(options.timeoutInSeconds ?? DEFAULT_TIMEOUT); await tx.simulate({ restore: false }); return tx; } - simulate = async ({ restore }: {restore?: boolean} = {}): Promise => { - if (!this.built){ - if(!this.raw) { + simulate = async ({ restore }: { restore?: boolean } = {}): Promise => { + if (!this.built) { + if (!this.raw) { throw new Error( "Transaction has not yet been assembled; " + - "call `AssembledTransaction.build` first." + "call `AssembledTransaction.build` first." ); } this.built = this.raw.build(); @@ -508,14 +515,9 @@ export class AssembledTransaction { networkPassphrase: this.options.networkPassphrase, }) .addOperation( - contract.call( - this.options.method, - ...(this.options.args ?? []) - ) + contract.call(this.options.method, ...(this.options.args ?? [])) ) - .setTimeout( - this.options.timeoutInSeconds ?? DEFAULT_TIMEOUT - ); + .setTimeout(this.options.timeoutInSeconds ?? DEFAULT_TIMEOUT); await this.simulate(); return this; } @@ -525,10 +527,7 @@ export class AssembledTransaction { } if (Api.isSimulationSuccess(this.simulation)) { - this.built = assembleTransaction( - this.built, - this.simulation - ).build(); + this.built = assembleTransaction(this.built, this.simulation).build(); } return this; @@ -547,7 +546,7 @@ export class AssembledTransaction { const simulation = this.simulation!; if (!simulation) { throw new AssembledTransaction.Errors.NotYetSimulated( - "Transaction has not yet been simulated", + "Transaction has not yet been simulated" ); } if (Api.isSimulationError(simulation)) { @@ -557,13 +556,16 @@ export class AssembledTransaction { if (Api.isSimulationRestore(simulation)) { throw new AssembledTransaction.Errors.ExpiredState( `You need to restore some contract state before you can invoke this method.\n` + - 'You can set `restore` to true in the method options in order to ' + - 'automatically restore the contract state when needed.' + "You can set `restore` to true in the method options in order to " + + "automatically restore the contract state when needed." ); } // add to object for serialization & deserialization - this.simulationResult = simulation.result ?? { auth: [], retval: xdr.ScVal.scvVoid() }; + this.simulationResult = simulation.result ?? { + auth: [], + retval: xdr.ScVal.scvVoid(), + }; this.simulationTransactionData = simulation.transactionData.build(); return { @@ -597,7 +599,7 @@ export class AssembledTransaction { } /** - * Sign the transaction with the signTransaction function included previously. + * Sign the transaction with the signTransaction function included previously. * If you did not previously include one, you need to include one now. */ sign = async ({ @@ -620,26 +622,25 @@ export class AssembledTransaction { if (!force && this.isReadCall) { throw new AssembledTransaction.Errors.NoSignatureNeeded( "This is a read call. It requires no signature or sending. " + - "Use `force: true` to sign and send anyway." + "Use `force: true` to sign and send anyway." ); } if (!signTransaction) { throw new AssembledTransaction.Errors.NoSigner( "You must provide a signTransaction function, either when calling " + - "`signAndSend` or when initializing your Client" + "`signAndSend` or when initializing your Client" ); } if (this.needsNonInvokerSigningBy().length) { throw new AssembledTransaction.Errors.NeedsMoreSignatures( "Transaction requires more signatures. " + - "See `needsNonInvokerSigningBy` for details.", + "See `needsNonInvokerSigningBy` for details." ); } - const timeoutInSeconds = - this.options.timeoutInSeconds ?? DEFAULT_TIMEOUT; + const timeoutInSeconds = this.options.timeoutInSeconds ?? DEFAULT_TIMEOUT; this.built = TransactionBuilder.cloneFrom(this.built!, { fee: this.built!.fee, timebounds: undefined, @@ -648,16 +649,13 @@ export class AssembledTransaction { .setTimeout(timeoutInSeconds) .build(); - const signature = await signTransaction( - this.built.toXDR(), - { - networkPassphrase: this.options.networkPassphrase, - }, - ); + const signature = await signTransaction(this.built.toXDR(), { + networkPassphrase: this.options.networkPassphrase, + }); this.signed = TransactionBuilder.fromXDR( signature, - this.options.networkPassphrase, + this.options.networkPassphrase ) as Tx; }; @@ -665,18 +663,20 @@ export class AssembledTransaction { * Sends the transaction to the network to return a `SentTransaction` that * keeps track of all the attempts to fetch the transaction. */ - async send(){ - if(!this.signed){ - throw new Error("The transaction has not yet been signed. Run `sign` first, or use `signAndSend` instead."); + async send() { + if (!this.signed) { + throw new Error( + "The transaction has not yet been signed. Run `sign` first, or use `signAndSend` instead." + ); } const sent = await SentTransaction.init(undefined, this); return sent; } /** - * Sign the transaction with the `signTransaction` function included previously. - * If you did not previously include one, you need to include one now. - * After signing, this method will send the transaction to the network and + * Sign the transaction with the `signTransaction` function included previously. + * If you did not previously include one, you need to include one now. + * After signing, this method will send the transaction to the network and * return a `SentTransaction` that keeps track * of all the attempts to fetch the transaction. */ signAndSend = async ({ @@ -692,7 +692,7 @@ export class AssembledTransaction { */ signTransaction?: ClientOptions["signTransaction"]; } = {}): Promise> => { - if(!this.signed){ + if (!this.signed) { await this.sign({ force, signTransaction }); } return this.send(); @@ -700,7 +700,7 @@ export class AssembledTransaction { private getStorageExpiration = async () => { const entryRes = await this.server.getLedgerEntries( - new Contract(this.options.contractId).getFootprint(), + new Contract(this.options.contractId).getFootprint() ); if ( !entryRes.entries || @@ -749,8 +749,8 @@ export class AssembledTransaction { if (!("operations" in this.built)) { throw new Error( `Unexpected Transaction type; no operations: ${JSON.stringify( - this.built, - )}`, + this.built + )}` ); } const rawInvokeHostFunctionOp = this.built @@ -762,16 +762,16 @@ export class AssembledTransaction { .filter( (entry) => entry.credentials().switch() === - xdr.SorobanCredentialsType.sorobanCredentialsAddress() && + xdr.SorobanCredentialsType.sorobanCredentialsAddress() && (includeAlreadySigned || entry.credentials().address().signature().switch().name === - "scvVoid"), + "scvVoid") ) .map((entry) => StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519(), - ), - ), + entry.credentials().address().address().accountId().ed25519() + ) + ) ), ]; }; @@ -822,18 +822,18 @@ export class AssembledTransaction { if (!needsNonInvokerSigningBy) { throw new AssembledTransaction.Errors.NoUnsignedNonInvokerAuthEntries( - "No unsigned non-invoker auth entries; maybe you already signed?", + "No unsigned non-invoker auth entries; maybe you already signed?" ); } if (needsNonInvokerSigningBy.indexOf(publicKey ?? "") === -1) { throw new AssembledTransaction.Errors.NoSignatureNeeded( - `No auth entries for public key "${publicKey}"`, + `No auth entries for public key "${publicKey}"` ); } if (!signAuthEntry) { throw new AssembledTransaction.Errors.NoSigner( "You must provide `signAuthEntry` when calling `signAuthEntries`, " + - "or when constructing the `Client` or `AssembledTransaction`", + "or when constructing the `Client` or `AssembledTransaction`" ); } @@ -854,7 +854,7 @@ export class AssembledTransaction { continue; // eslint-disable-line no-continue } const pk = StrKey.encodeEd25519PublicKey( - entry.credentials().address().address().accountId().ed25519(), + entry.credentials().address().address().accountId().ed25519() ); // this auth entry needs to be signed by a different account @@ -867,7 +867,7 @@ export class AssembledTransaction { async (preimage) => Buffer.from(await signAuthEntry(preimage.toXDR("base64")), "base64"), await expiration, // eslint-disable-line no-await-in-loop - this.options.networkPassphrase, + this.options.networkPassphrase ); } }; @@ -888,28 +888,28 @@ export class AssembledTransaction { } /** - * Restores the footprint (resource ledger entries that can be read or written) - * of an expired transaction. - * + * Restores the footprint (resource ledger entries that can be read or written) + * of an expired transaction. + * * The method will: * 1. Build a new transaction aimed at restoring the necessary resources. * 2. Sign this new transaction if a `signTransaction` handler is provided. * 3. Send the signed transaction to the network. * 4. Await and return the response from the network. - * + * * Preconditions: * - A `signTransaction` function must be provided during the Client initialization. * - The provided `restorePreamble` should include a minimum resource fee and valid * transaction data. - * - * @throws {Error} - Throws an error if no `signTransaction` function is provided during + * + * @throws {Error} - Throws an error if no `signTransaction` function is provided during * Client initialization. - * @throws {AssembledTransaction.Errors.RestoreFailure} - Throws a custom error if the + * @throws {AssembledTransaction.Errors.RestoreFailure} - Throws a custom error if the * restore transaction fails, providing the details of the failure. */ async restoreFootprint( /** - * The preamble object containing data required to + * The preamble object containing data required to * build the restore transaction. */ restorePreamble: { @@ -920,16 +920,19 @@ export class AssembledTransaction { account?: Account ): Promise { if (!this.options.signTransaction) { - throw new Error("For automatic restore to work you must provide a signTransaction function when initializing your Client"); + throw new Error( + "For automatic restore to work you must provide a signTransaction function when initializing your Client" + ); } - account = account ?? await getAccount(this.options, this.server); + account = account ?? (await getAccount(this.options, this.server)); // first try restoring the contract - const restoreTx = await AssembledTransaction.buildFootprintRestoreTransaction( - { ...this.options }, - restorePreamble.transactionData, - account, - restorePreamble.minResourceFee - ); + const restoreTx = + await AssembledTransaction.buildFootprintRestoreTransaction( + { ...this.options }, + restorePreamble.transactionData, + account, + restorePreamble.minResourceFee + ); const sentTransaction = await restoreTx.signAndSend(); if (!sentTransaction.getTransactionResponse) { throw new AssembledTransaction.Errors.RestorationFailure( @@ -938,6 +941,4 @@ export class AssembledTransaction { } return sentTransaction.getTransactionResponse; } - - }