diff --git a/web/cashtab/src/utils/__tests__/validation.test.js b/web/cashtab/src/utils/__tests__/validation.test.js --- a/web/cashtab/src/utils/__tests__/validation.test.js +++ b/web/cashtab/src/utils/__tests__/validation.test.js @@ -8,6 +8,7 @@ isValidTokenDocumentUrl, isValidCashtabSettings, isValidXecAddress, + isValidBchAddress, isValidNewWalletNameLength, isValidEtokenAddress, isValidXecSendAmount, @@ -352,6 +353,54 @@ const addr = ''; expect(isValidXecAddress(addr)).toBe(false); }); + it(`isValidBchAddress correctly validates a valid BCH address with bitcoincash: prefix`, () => { + const addr = 'bitcoincash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqqkm80dnl6'; + expect(isValidBchAddress(addr)).toBe(true); + }); + it(`isValidBchAddress correctly validates a valid BCH address without bitcoincash: prefix`, () => { + const addr = 'qzvydd4n3lm3xv62cx078nu9rg0e3srmqqkm80dnl6'; + expect(isValidBchAddress(addr)).toBe(true); + }); + it(`isValidBchAddress rejects a valid legacy address`, () => { + const addr = '1Efd9z9GRVJK2r73nUpFmBnsKUmfXNm2y2'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid ecash: address`, () => { + const addr = 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid ecash: address without the ecash prefix`, () => { + const addr = 'qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid etoken: address with prefix`, () => { + const addr = 'etoken:qz2708636snqhsxu8wnlka78h6fdp77ar5tv2tzg4r'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid etoken: address without prefix`, () => { + const addr = 'qz2708636snqhsxu8wnlka78h6fdp77ar5tv2tzg4r'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid simpleledger: address with prefix`, () => { + const addr = 'simpleledger:qrujw0wrzncyxw8q3d0xkfet4jafrqhk6csev0v6y3'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a valid simpleledger: address without prefix`, () => { + const addr = 'qrujw0wrzncyxw8q3d0xkfet4jafrqhk6csev0v6y3'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects an invalid address`, () => { + const addr = 'wtf is this definitely not an address'; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects a null input`, () => { + const addr = null; + expect(isValidBchAddress(addr)).toBe(false); + }); + it(`isValidBchAddress rejects an empty string input`, () => { + const addr = ''; + expect(isValidBchAddress(addr)).toBe(false); + }); it(`isValidEtokenAddress rejects a valid XEC address with ecash: prefix`, () => { const addr = 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035'; expect(isValidEtokenAddress(addr)).toBe(false); 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 @@ -3,6 +3,7 @@ isValidXecAddress, isValidEtokenAddress, isValidContactList, + isValidBchAddress, } from 'utils/validation'; import BigNumber from 'bignumber.js'; import cashaddr from 'ecashaddrjs'; @@ -351,7 +352,9 @@ // Validate address try { changeAddress = inputUtxos[0].address; - BCH.Address.isCashAddress(changeAddress); + if (!isValidBchAddress(changeAddress)) { + throw new Error('Invalid change address'); + } } catch (err) { throw new Error('Invalid input utxo'); } diff --git a/web/cashtab/src/utils/validation.js b/web/cashtab/src/utils/validation.js --- a/web/cashtab/src/utils/validation.js +++ b/web/cashtab/src/utils/validation.js @@ -389,6 +389,51 @@ return isValidXecAddress; }; +export const isValidBchAddress = addr => { + /* + Returns true for a valid BCH address + + Valid BCH address: + - May or may not have prefix `bitcoincash:` + - Checksum must validate for prefix `bitcoincash:` + + A simple ledger address is not considered a valid bitcoincash address + */ + + if (!addr) { + return false; + } + + let isValidBchAddress; + let isPrefixedBchAddress; + + // Check for possible prefix + if (addr.includes(':')) { + // Test for 'ecash:' prefix + isPrefixedBchAddress = addr.slice(0, 12) === 'bitcoincash:'; + // Any address including ':' that doesn't start explicitly with 'bitcoincash:' is invalid + if (!isPrefixedBchAddress) { + isValidBchAddress = false; + return isValidBchAddress; + } + } else { + isPrefixedBchAddress = false; + } + + // If no prefix, assume it is checksummed for an bitcoincash: prefix + const testedXecAddr = isPrefixedBchAddress ? addr : `bitcoincash:${addr}`; + + try { + const decoded = cashaddr.decode(testedXecAddr); + if (decoded.prefix === 'bitcoincash') { + isValidBchAddress = true; + } + } catch (err) { + isValidBchAddress = false; + } + return isValidBchAddress; +}; + export const isValidEtokenAddress = addr => { /* Returns true for a valid eToken address