From 7dc0a8cfee29c0260c260d2a2552bb394d08c929 Mon Sep 17 00:00:00 2001 From: Chad Ostrowski <221614+chadoh@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:41:51 -0400 Subject: [PATCH] Update `simulationData` after re-simulation (#985) The new test added here shows that you can edit a transaction with `tx.raw = cloneFrom(tx.build)` and that the `tx.simulationData` will be updated correctly. --- CHANGELOG.md | 3 ++ src/contract/assembled_transaction.ts | 4 ++ test/e2e/src/test-swap.js | 66 +++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 151a3761f..e2642090a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ are gathered when using the XDR methods. - In `contract.AssembledTransaction`, `toJSON` and `fromJSON` should be replaced with `toXDR` and `fromXDR`. Similarly, in `contract.Client`, `txFromJSON` should be replaced with `txFromXDR`. +### Fixed +- Contract Client: if you edit an `AssembledTransaction` with `tx.raw = cloneFrom(tx.build)`, the `tx.simulationData` will now be updated correctly. `simulationData` is effectively a cache, and we now make sure we clear it when you re-simulate. + ## [v12.0.1](https://github.com/stellar/js-stellar-sdk/compare/v11.3.0...v12.0.1) diff --git a/src/contract/assembled_transaction.ts b/src/contract/assembled_transaction.ts index de584fb81..e4d627c0f 100644 --- a/src/contract/assembled_transaction.ts +++ b/src/contract/assembled_transaction.ts @@ -466,6 +466,10 @@ export class AssembledTransaction { } this.simulation = await this.server.simulateTransaction(this.built); + // need to force re-calculation of simulationData for new simulation + delete this.simulationResult; + delete this.simulationTransactionData; + if (Api.isSimulationSuccess(this.simulation)) { this.built = assembleTransaction( this.built, diff --git a/test/e2e/src/test-swap.js b/test/e2e/src/test-swap.js index 71e0e0ab4..a55497071 100644 --- a/test/e2e/src/test-swap.js +++ b/test/e2e/src/test-swap.js @@ -1,7 +1,17 @@ -const { expect } = require('chai'); -const { describe, it, before } = require('mocha'); -const { contract, rpc } = require("../../.."); -const { clientFor, generateFundedKeypair } = require("./util"); +const { expect } = require("chai"); +const { describe, it, before } = require("mocha"); +const { + contract, + rpc, + SorobanDataBuilder, + xdr, + TransactionBuilder, +} = require("../../.."); +const { + clientFor, + generateFundedKeypair, + networkPassphrase, +} = require("./util"); const amountAToSwap = 2n; const amountBToSwap = 1n; @@ -74,7 +84,7 @@ describe("Swap Contract Tests", function () { // Further assertions on the error object expect(error).to.be.instanceOf(contract.AssembledTransaction.Errors.NeedsMoreSignatures, `error is not of type 'NeedsMoreSignaturesError'; instead it is of type '${error?.constructor.name}'`); - + if (error) { // Using regex to check the error message expect(error.message).to.match(/needsNonInvokerSigningBy/); @@ -82,6 +92,52 @@ describe("Swap Contract Tests", function () { }); }); + it("modified & re-simulated transactions show updated data", async function () { + const tx = await this.context.swapContractAsRoot.swap({ + a: this.context.alice.publicKey(), + b: this.context.bob.publicKey(), + token_a: this.context.tokenAId, + token_b: this.context.tokenBId, + amount_a: amountAToSwap, + min_a_for_b: amountAToSwap, + amount_b: amountBToSwap, + min_b_for_a: amountBToSwap, + }); + await tx.signAuthEntries({ + publicKey: this.context.alice.publicKey(), + ...contract.basicNodeSigner(this.context.alice, networkPassphrase), + }); + await tx.signAuthEntries({ + publicKey: this.context.bob.publicKey(), + ...contract.basicNodeSigner(this.context.bob, networkPassphrase), + }); + + const originalResourceFee = Number( + tx.simulationData.transactionData.resourceFee() + ); + const bumpedResourceFee = originalResourceFee + 10000; + + tx.raw = TransactionBuilder.cloneFrom(tx.built, { + fee: tx.built.fee, + sorobanData: new SorobanDataBuilder( + tx.simulationData.transactionData.toXDR() + ) + .setResourceFee( + xdr.Int64.fromString(bumpedResourceFee.toString()).toBigInt() + ) + .build(), + }); + + await tx.simulate(); + + const newSimulatedResourceFee = Number( + tx.simulationData.transactionData.resourceFee() + ); + + expect(originalResourceFee).to.not.equal(newSimulatedResourceFee); + expect(newSimulatedResourceFee).to.be.greaterThan(bumpedResourceFee); + }); + it("alice swaps bob 10 A for 1 B", async function() { const tx = await this.context.swapContractAsRoot.swap({ a: this.context.alice.publicKey(),