diff --git a/web/cashtab/src/utils/__tests__/cashMethods.test.js b/web/cashtab/src/utils/__tests__/cashMethods.test.js --- a/web/cashtab/src/utils/__tests__/cashMethods.test.js +++ b/web/cashtab/src/utils/__tests__/cashMethods.test.js @@ -1,4 +1,5 @@ import { ValidationError } from 'ecashaddrjs'; +import BigNumber from 'bignumber.js'; import { fromSmallestDenomination, batchArray, @@ -28,6 +29,7 @@ parseChronikTx, checkWalletForTokenInfo, isActiveWebsocket, + parseXecSendValue, } from 'utils/cashMethods'; import { currency } from 'components/Common/Ticker'; import { @@ -173,6 +175,70 @@ unsubscribedWebsocket, } from '../__mocks__/chronikWs'; +it(`parseXecSendValue() correctly parses the value for a valid one to one send XEC transaction`, () => { + expect(parseXecSendValue(false, '550', null)).toStrictEqual( + new BigNumber(550), + ); +}); + +it(`parseXecSendValue() correctly parses the value for a valid one to many send XEC transaction`, () => { + const destinationAddressAndValueArray = [ + 'ecash:qrmz0egsqxj35x5jmzf8szrszdeu72fx0uxgwk3r48,6', + 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx,6', + 'ecash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqq0knykfed,6', + ]; + expect( + parseXecSendValue(true, null, destinationAddressAndValueArray), + ).toStrictEqual(new BigNumber(18)); +}); + +it(`parseXecSendValue() correctly throws error when singleSendValue is invalid for a one to one send XEC transaction`, () => { + let errorThrown; + try { + parseXecSendValue(false, null, 550); + } catch (err) { + errorThrown = err; + } + expect(errorThrown.message).toStrictEqual('Invalid singleSendValue'); +}); + +it(`parseXecSendValue() correctly throws error when destinationAddressAndValueArray is invalid for a one to many send XEC transaction`, () => { + let errorThrown; + try { + parseXecSendValue(true, null, null); + } catch (err) { + errorThrown = err; + } + expect(errorThrown.message).toStrictEqual( + 'Invalid destinationAddressAndValueArray', + ); +}); + +it(`parseXecSendValue() correctly throws error when the total value for a one to one send XEC transaction is below dust`, () => { + let errorThrown; + try { + parseXecSendValue(false, '4.5', null); + } catch (err) { + errorThrown = err; + } + expect(errorThrown.message).toStrictEqual('dust'); +}); + +it(`parseXecSendValue() correctly throws error when the total value for a one to many send XEC transaction is below dust`, () => { + const destinationAddressAndValueArray = [ + 'ecash:qrmz0egsqxj35x5jmzf8szrszdeu72fx0uxgwk3r48,2', + 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx,2', + 'ecash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqq0knykfed,1', + ]; + let errorThrown; + try { + parseXecSendValue(true, null, destinationAddressAndValueArray); + } catch (err) { + errorThrown = err; + } + expect(errorThrown.message).toStrictEqual('dust'); +}); + describe('Correctly executes cash utility functions', () => { it(`Correctly converts smallest base unit to smallest decimal for cashDecimals = 2`, () => { expect(fromSmallestDenomination(1, 2)).toBe(0.01); diff --git a/web/cashtab/src/utils/cashMethods.js b/web/cashtab/src/utils/cashMethods.js --- a/web/cashtab/src/utils/cashMethods.js +++ b/web/cashtab/src/utils/cashMethods.js @@ -8,6 +8,67 @@ import BigNumber from 'bignumber.js'; import cashaddr from 'ecashaddrjs'; +/* + * Parse the total value of a send XEC tx and checks whether it is more than dust + * One to many: isOneToMany is true, singleSendValue is null + * One to one: isOneToMany is false, destinationAddressAndValueArray is null + * Returns the aggregate send value in BigNumber format + */ +export const parseXecSendValue = ( + isOneToMany, + singleSendValue, + destinationAddressAndValueArray, +) => { + let value = new BigNumber(0); + + try { + if (isOneToMany) { + // this is a one to many XEC transaction + if ( + !destinationAddressAndValueArray || + !destinationAddressAndValueArray.length + ) { + throw new Error('Invalid destinationAddressAndValueArray'); + } + const arrayLength = destinationAddressAndValueArray.length; + for (let i = 0; i < arrayLength; i++) { + // add the total value being sent in this array of recipients + // each array row is: 'eCash address, send value' + value = BigNumber.sum( + value, + new BigNumber( + destinationAddressAndValueArray[i].split(',')[1], + ), + ); + } + } else { + // this is a one to one XEC transaction then check singleSendValue + // note: one to many transactions won't be sending a singleSendValue param + + if (!singleSendValue) { + throw new Error('Invalid singleSendValue'); + } + + value = new BigNumber(singleSendValue); + } + // If user is attempting to send an aggregate value that is less than minimum accepted by the backend + if ( + value.lt( + new BigNumber( + fromSmallestDenomination(currency.dustSats).toString(), + ), + ) + ) { + // Throw the same error given by the backend attempting to broadcast such a tx + throw new Error('dust'); + } + } catch (err) { + console.log('Error in parseXecSendValue: ' + err); + throw err; + } + return value; +}; + export function parseOpReturn(hexStr) { if ( !hexStr ||