From 81e0eceb30e0017ba7b79cd939806425558dfa31 Mon Sep 17 00:00:00 2001 From: directorfloo Date: Sun, 25 Oct 2026 17:27:51 +0100 Subject: [PATCH] fix(stellar-sdk-helpers): return total fee from prepareSorobanTx --- apps/docs/architecture/api.md | 2 +- docs/signing-flow.md | 3 ++- packages/sdk/src/types.ts | 2 +- packages/stellar-sdk-helpers/src/keeper-tx.ts | 2 +- packages/stellar-sdk-helpers/src/tx.test.ts | 22 +++++++++++++++++++ packages/stellar-sdk-helpers/src/tx.ts | 5 +++-- packages/stellar-sdk-helpers/src/vaults.ts | 2 +- 7 files changed, 31 insertions(+), 7 deletions(-) diff --git a/apps/docs/architecture/api.md b/apps/docs/architecture/api.md index 8e6b4a4d..f04c118f 100644 --- a/apps/docs/architecture/api.md +++ b/apps/docs/architecture/api.md @@ -121,7 +121,7 @@ Builds an unsigned Soroban deposit transaction. } ``` -The `xdr` field is a base64-encoded unsigned `TransactionEnvelope`. The `fee` is the simulated resource fee in stroops. The client must forward the XDR to the wallet for signing before submitting. +The `xdr` field is a base64-encoded unsigned `TransactionEnvelope`. The `fee` is the assembled transaction's total fee in stroops, including the inclusion fee. The client must forward the XDR to the wallet for signing before submitting. ### `POST /api/v1/tx/withdraw` diff --git a/docs/signing-flow.md b/docs/signing-flow.md index 182b7387..2e6966b2 100644 --- a/docs/signing-flow.md +++ b/docs/signing-flow.md @@ -84,7 +84,8 @@ Response `200`: - `xdr` — base64 transaction envelope, **unsigned**, already prepared via `assembleTransaction` (Soroban footprint + resource fee included). -- `fee` — the simulated `minResourceFee` in stroops, for display. +- `fee` is the assembled transaction's total fee in stroops, including the + inclusion fee, for display. ### `POST /api/v1/tx/withdraw` diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 4fee97ac..c28cac02 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -41,7 +41,7 @@ export interface Position { export interface Transaction { /** Base64-encoded unsigned Soroban transaction XDR. */ xdr: string; - /** Minimum Soroban resource fee in stroops, returned by simulation. */ + /** Total assembled transaction fee in stroops, including the inclusion fee. */ fee: string; } diff --git a/packages/stellar-sdk-helpers/src/keeper-tx.ts b/packages/stellar-sdk-helpers/src/keeper-tx.ts index ad666b78..5d9b266f 100644 --- a/packages/stellar-sdk-helpers/src/keeper-tx.ts +++ b/packages/stellar-sdk-helpers/src/keeper-tx.ts @@ -314,7 +314,7 @@ export interface KeeperTxConfig { // confirmed on-chain failure). Callers must persist `priorHash` across their // own retry attempts (see accrual-keeper.ts and migration-keeper.ts), and // persist it across invocations through `hooks` (see keeper-state.ts). -// `attempt` (1-indexed, matching withKeeperRetry's own callback, see +// `attempt` (0-indexed, matching withKeeperRetry's callback, see // keeperFeeForAttempt above) sets the classic inclusion fee for a // freshly-built transaction, escalating on each retry so a // `txInsufficientFee` rejection has a real chance of clearing next time; diff --git a/packages/stellar-sdk-helpers/src/tx.test.ts b/packages/stellar-sdk-helpers/src/tx.test.ts index 21cd650e..797d9799 100644 --- a/packages/stellar-sdk-helpers/src/tx.test.ts +++ b/packages/stellar-sdk-helpers/src/tx.test.ts @@ -6,6 +6,7 @@ import { Asset, Contract, Operation, + SorobanDataBuilder, TransactionBuilder, nativeToScVal, xdr, @@ -245,6 +246,27 @@ describe("simErrorMessage snapshots", () => { describe("prepareSorobanTx contract errors", () => { afterEach(() => vi.restoreAllMocks()); + it("returns the assembled transaction fee, including its inclusion fee", async () => { + const caller = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"; + const op = new Contract(CONTRACT_ADDRESSES.testnet.vault).call("deposit"); + const account = new Account(caller, "0"); + + vi.spyOn(rpc.Server.prototype, "getAccount").mockResolvedValue(account); + vi.spyOn(rpc.Server.prototype, "simulateTransaction").mockResolvedValue({ + id: "1", + latestLedger: 1, + minResourceFee: "500", + events: [], + transactionData: new SorobanDataBuilder().setResourceFee("500"), + result: { auth: [], retval: xdr.ScVal.scvVoid() }, + _parsed: true, + }); + + await expect(prepareSorobanTx(TESTNET, caller, op)).resolves.toMatchObject({ + fee: "600", + }); + }); + it.each([ ["HostError: Error(Contract, #18)", true], ["HostError: Error(Contract, #15)", true], diff --git a/packages/stellar-sdk-helpers/src/tx.ts b/packages/stellar-sdk-helpers/src/tx.ts index b33b57c0..7819c041 100644 --- a/packages/stellar-sdk-helpers/src/tx.ts +++ b/packages/stellar-sdk-helpers/src/tx.ts @@ -190,7 +190,8 @@ export async function simulateView( /** * Fetch the caller's account, simulate the operation to obtain the resource * footprint and fee, assemble the transaction, and return the unsigned XDR - * and minimum resource fee. Throws if simulation fails. + * and total transaction fee, including the inclusion fee. Throws if + * simulation fails. */ export async function prepareSorobanTx( network: StellarNetwork, @@ -227,7 +228,7 @@ export async function prepareSorobanTx( const prepared = rpc.assembleTransaction(tx, sim).build(); return { xdr: prepared.toEnvelope().toXDR("base64"), - fee: sim.minResourceFee, + fee: prepared.fee, }; } diff --git a/packages/stellar-sdk-helpers/src/vaults.ts b/packages/stellar-sdk-helpers/src/vaults.ts index 6ae5224e..3689e121 100644 --- a/packages/stellar-sdk-helpers/src/vaults.ts +++ b/packages/stellar-sdk-helpers/src/vaults.ts @@ -156,7 +156,7 @@ async function fetchMeridianVault( "Meridian adapter RPC" ), ]); - const tvl = Math.round(Number(toBigInt(totalAssetsRaw) ?? 0n) / 1e7); + const tvl = Math.round(Number(toBigInt(totalAssetsRaw)) / 1e7); return { ...meta, asset: meta.asset ?? "USDC",