diff --git a/extension/src/helpers/transactionResult.test.ts b/extension/src/helpers/transactionResult.test.ts index 9bd1545eef..aec26c2781 100644 --- a/extension/src/helpers/transactionResult.test.ts +++ b/extension/src/helpers/transactionResult.test.ts @@ -100,6 +100,44 @@ describe("getSettledPathPaymentStrictSendAmount", () => { getSettledPathPaymentStrictSendAmount(txResult.toXdr("base64"), 0), ).toBeNull(); }); + + it("returns null when the path payment succeeded but a later operation failed", () => { + // Stellar transactions are atomic: a txFailed result still reports the + // earlier operation's own success, but that path payment was rolled back + // and nothing settled. Reading the amount out of it would report volume + // for a swap that never happened. + const settledPathPayment = xdr.OperationResult.opInner( + xdr.OperationResultTr.pathPaymentStrictSend( + xdr.PathPaymentStrictSendResult.pathPaymentStrictSendSuccess( + new xdr.PathPaymentStrictSendResultSuccess({ + offers: [], + last: new xdr.SimplePaymentResult({ + destination: xdr.PublicKey.publicKeyTypeEd25519( + Keypair.random().rawPublicKey(), + ), + asset: Asset.native().toXdrObject(), + amount: BigInt("50000000"), + }), + }), + ), + ), + ); + const laterFailedOp = xdr.OperationResult.opInner( + xdr.OperationResultTr.payment(xdr.PaymentResult.paymentUnderfunded()), + ); + const txResult = new xdr.TransactionResult({ + feeCharged: BigInt("100"), + result: xdr.TransactionResultResult.txFailed([ + settledPathPayment, + laterFailedOp, + ]), + ext: xdr.TransactionResultExt.v0(), + }); + + expect( + getSettledPathPaymentStrictSendAmount(txResult.toXdr("base64"), 0), + ).toBeNull(); + }); }); describe("findPathPaymentStrictSendIndex", () => { diff --git a/extension/src/helpers/transactionResult.ts b/extension/src/helpers/transactionResult.ts index 2fda3eac6a..c7b94825c8 100644 --- a/extension/src/helpers/transactionResult.ts +++ b/extension/src/helpers/transactionResult.ts @@ -50,10 +50,12 @@ export const getSettledPathPaymentStrictSendAmount = ( ? innerResult.innerResultPair.result.result : innerResult; - if ( - innerTxResult.type !== "txSuccess" && - innerTxResult.type !== "txFailed" - ) { + // txSuccess only. A `txFailed` result still carries per-operation + // results, and an operation that succeeded before a later one failed + // reports its own success there — but Stellar transactions are atomic, so + // that path payment was rolled back and nothing settled. Reading an + // amount out of it would report volume for a swap that never happened. + if (innerTxResult.type !== "txSuccess") { return null; } const opResults = innerTxResult.results;