From af03cac6afef3d6b24f7d16d7b4ca410601e5731 Mon Sep 17 00:00:00 2001 From: blaineheffron Date: Thu, 30 May 2024 17:38:23 -0400 Subject: [PATCH] add toXDR and fromXDR for multiauth flow --- src/contract/assembled_transaction.ts | 51 +++++++++++++++++++++++---- src/contract/client.ts | 3 ++ test/e2e/src/test-swap.js | 15 ++++---- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/contract/assembled_transaction.ts b/src/contract/assembled_transaction.ts index 1f46824b9..38b9938ef 100644 --- a/src/contract/assembled_transaction.ts +++ b/src/contract/assembled_transaction.ts @@ -28,6 +28,7 @@ import { implementsToString, } from "./utils"; import { SentTransaction } from "./sent_transaction"; +import { Spec } from "./spec"; export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; @@ -364,6 +365,40 @@ export class AssembledTransaction { return txn; } + /** + * Serialize the AssembledTransaction to a base64-encoded XDR string. + */ + toXDR(): string { + if(!this.built) throw new Error( + "Transaction has not yet been simulated; " + + "call `AssembledTransaction.simulate` first.", + ); + return this.built?.toEnvelope().toXDR('base64'); + } + + /** + * Deserialize the AssembledTransaction from a base64-encoded XDR string. + */ + static fromXDR( + options: Omit, "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 method = ((built.operations[0] as Operation.InvokeHostFunction).func.value() as xdr.InvokeContractArgs).functionName().toString('utf-8'); + console.log(`method name is ${method}`); + const txn = new AssembledTransaction( + { ...options, + method, + parseResultXdr: (result: xdr.ScVal) => + spec.funcResToNative(method, result), + } + ); + txn.built = built; + return txn; + } + private constructor(public options: AssembledTransactionOptions) { this.options.simulate = this.options.simulate ?? true; this.server = new Server(this.options.rpcUrl, { @@ -412,14 +447,16 @@ export class AssembledTransaction { } simulate = async (): Promise => { - if (!this.raw) { - throw new Error( - "Transaction has not yet been assembled; " + - "call `AssembledTransaction.build` first.", - ); - } + if(!this.built){ + if (!this.raw) { + throw new Error( + "Transaction has not yet been assembled; " + + "call `AssembledTransaction.build` first.", + ); + } - this.built = this.raw.build(); + this.built = this.raw.build(); + } this.simulation = await this.server.simulateTransaction(this.built); if (Api.isSimulationSuccess(this.simulation)) { diff --git a/src/contract/client.ts b/src/contract/client.ts index e0c298b4a..f576762a6 100644 --- a/src/contract/client.ts +++ b/src/contract/client.ts @@ -124,5 +124,8 @@ export class Client { tx, ); }; + + txFromXDR = (xdrBase64: string): AssembledTransaction => AssembledTransaction.fromXDR(this.options, xdrBase64, this.spec); + } diff --git a/test/e2e/src/test-swap.js b/test/e2e/src/test-swap.js index f1f651f31..6694d9255 100644 --- a/test/e2e/src/test-swap.js +++ b/test/e2e/src/test-swap.js @@ -104,32 +104,33 @@ test("alice swaps bob 10 A for 1 B", async (t) => { ); // root serializes & sends to alice - const jsonFromRoot = tx.toJSON(); + const xdrFromRoot = tx.toXDR(); const { client: clientAlice } = await clientFor("swap", { keypair: t.context.alice, contractId: t.context.swapId, }); - const txAlice = clientAlice.txFromJSON(jsonFromRoot); + const txAlice = clientAlice.txFromXDR(xdrFromRoot); await txAlice.signAuthEntries(); // alice serializes & sends to bob - const jsonFromAlice = txAlice.toJSON(); + const xdrFromAlice = txAlice.toXDR(); const { client: clientBob } = await clientFor("swap", { keypair: t.context.bob, contractId: t.context.swapId, }); - const txBob = clientBob.txFromJSON(jsonFromAlice); + const txBob = clientBob.txFromXDR(xdrFromAlice); await txBob.signAuthEntries(); // bob serializes & sends back to root - const jsonFromBob = txBob.toJSON(); + const xdrFromBob = txBob.toXDR(); const { client: clientRoot } = await clientFor("swap", { keypair: t.context.root, contractId: t.context.swapId, }); - const txRoot = clientRoot.txFromJSON(jsonFromBob); + const txRoot = clientRoot.txFromXDR(xdrFromBob); - const result = await txRoot.signAndSend(); + await txRoot.simulate(); + const result = await txRoot.signAndSend({force: true}); t.truthy( result.sendTransactionResponse,