From d3291e16437c07b356e0c7503b4889d91516e7b7 Mon Sep 17 00:00:00 2001 From: Fabien Date: Fri, 20 Feb 2026 16:18:52 +0100 Subject: [PATCH] Don't prevent detection when paying to self The amount detection is actually an exact match, computing the transaction amount by summing the recipient output and subtracting the sender inputs to get the (out - in) difference. When sending to self this computed value is zero so the amount is not matched (the tx is not even processed). This also fixes the edge case of a tx sending more coins to the recipient, e.g. via an extra output. Test Plan: yarn test Try sending to yourself and check detection now works. --- react/lib/tests/util/validate.test.ts | 49 +++++++++++++++++++++++++-- react/lib/util/chronik.ts | 9 +---- react/lib/util/validate.ts | 4 +-- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/react/lib/tests/util/validate.test.ts b/react/lib/tests/util/validate.test.ts index 79523efe..60ce5aaa 100644 --- a/react/lib/tests/util/validate.test.ts +++ b/react/lib/tests/util/validate.test.ts @@ -129,7 +129,7 @@ describe('Validate Util Tests', () => { expect(result).toBe(false); }); - it('false when amount does not match received amount', async () => { + it('true when received amount is equal or greater than expected', async () => { const transaction: Transaction = { amount: '101.00', paymentId: '123', @@ -140,7 +140,52 @@ describe('Validate Util Tests', () => { address: 'ecash:qrmm7edwuj4jf7tnvygjyztyy0a0qxvl7quss2vxek', }; const expectedPaymentId = '123'; - const expectedAmount = resolveNumber('100.00'); + let expectedAmount = resolveNumber('100.00'); + const expectedOpReturn = 'test opReturn message'; + const price = 1; + const currency = 'XEC'; + + let result1 = shouldTriggerOnSuccess( + transaction, + currency, + price, + false, + false, + expectedPaymentId, + expectedAmount, + expectedOpReturn + ); + + expect(result1).toBe(true); + + expectedAmount = resolveNumber('101.00'); + + let result2 = shouldTriggerOnSuccess( + transaction, + currency, + price, + false, + false, + expectedPaymentId, + expectedAmount, + expectedOpReturn + ); + + expect(result2).toBe(true); + }); + + it('false when received amount is less than expected', async () => { + const transaction: Transaction = { + amount: '100.00', + paymentId: '123', + message: 'test opReturn message', + rawMessage: 'test opReturn message', + hash: '', + timestamp: 0, + address: 'ecash:qrmm7edwuj4jf7tnvygjyztyy0a0qxvl7quss2vxek', + }; + const expectedPaymentId = '123'; + const expectedAmount = resolveNumber('100.01'); const expectedOpReturn = 'test opReturn message'; const price = 1; const currency = 'XEC'; diff --git a/react/lib/util/chronik.ts b/react/lib/util/chronik.ts index 1db85dae..2ea6d28b 100644 --- a/react/lib/util/chronik.ts +++ b/react/lib/util/chronik.ts @@ -115,7 +115,6 @@ export async function satoshisToUnit(satoshis: bigint, networkFormat: string): P const getTransactionAmountAndData = async (transaction: Tx, addressString: string): Promise<{amount: string, opReturn: string}> => { let totalOutput = BigInt(0); - let totalInput = BigInt(0); const addressFormat = xecaddr.detectAddressFormat(addressString) const script = toHash160(addressString).hash160 let opReturn = '' @@ -133,14 +132,8 @@ const getTransactionAmountAndData = async (transaction: Tx, addressString: stri } } } - for (const input of transaction.inputs) { - if (input?.outputScript?.includes(script) === true) { - totalInput += input.sats - } - } - const satoshis = totalOutput - totalInput return { - amount: await satoshisToUnit(satoshis, addressFormat), + amount: await satoshisToUnit(totalOutput, addressFormat), opReturn } } diff --git a/react/lib/util/validate.ts b/react/lib/util/validate.ts index 2632f9ef..0862eabf 100644 --- a/react/lib/util/validate.ts +++ b/react/lib/util/validate.ts @@ -34,12 +34,12 @@ export const shouldTriggerOnSuccess = ( if (transactionCurrency !== currency) { if (currencyObject){ const value = (currencyObject.float / price).toFixed(DECIMALS[transactionCurrency]) - isAmountValid = resolveNumber(value).isEqualTo(amount) + isAmountValid = resolveNumber(value).isLessThanOrEqualTo(amount) }else { isAmountValid = false } } else { - isAmountValid = expectedAmount.isEqualTo(amount); + isAmountValid = expectedAmount.isLessThanOrEqualTo(amount); } } let isPaymentIdValid = true