diff --git a/cashtab/src/validation/fixtures/vectors.js b/cashtab/src/validation/fixtures/vectors.js
index 323f2a24d..bc3da5e2a 100644
--- a/cashtab/src/validation/fixtures/vectors.js
+++ b/cashtab/src/validation/fixtures/vectors.js
@@ -1,566 +1,566 @@
 // Copyright (c) 2024 The Bitcoin developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 // Test vectors for validation functions
 import appConfig from 'config/app';
 
 export default {
     shouldDisableXecSend: {
         expectedReturns: [
             {
                 description: 'Disabled on startup',
                 formData: {
                     address: '',
                     value: '',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description:
                     'Disabled if address has been entered but no value',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description: 'Enabled for valid address and value',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '50',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: false,
             },
             {
                 description: 'Disabled on zero balance',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '50',
                 },
                 balances: { totalBalance: '0' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description: 'Disabled for invalid address',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg',
                     value: '50',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError:
                     'a string indicating a validation error msg',
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description: 'Disabled for invalid value',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '5',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError:
                     'a string indicating a validation error msg',
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description: 'Disabled for invalid opreturn msg',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '5',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: 'a string indicating a validation error msg',
                 priceApiError: false,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description: 'Disabled on priceApi error',
                 formData: {
                     address: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
                     value: '5',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: true,
                 isOneToManyXECSend: false,
                 sendDisabled: true,
             },
             {
                 description:
                     'Enabled if isOneToManyXECSend and value is not entered',
                 formData: {
                     address:
                         'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6, 22\necash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6, 22',
                     value: '',
                 },
                 balances: { totalBalance: '10000' },
                 apiError: false,
                 sendBchAmountError: false,
                 sendBchAddressError: false,
                 isMsgError: false,
                 priceApiError: false,
                 isOneToManyXECSend: true,
                 sendDisabled: false,
             },
         ],
     },
     meetsAliasSpecInputCases: {
         expectedReturns: [
             {
                 description:
                     'returns true for a valid lowercase alphanumeric input',
                 inputStr: 'jasdf3873',
                 response: true,
             },
             {
                 description:
                     'returns expected error if input contains uppercase char',
                 inputStr: 'jasDf3873',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description:
                     'returns expected error if input contains special char',
                 inputStr: 'Glück',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description: 'returns expected error if input contains emoji',
                 inputStr: '😉',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description:
                     'returns expected error if input contains other special characters',
                 inputStr: '( ͡° ͜ʖ ͡°)',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description:
                     'returns expected error if input is an empty string',
                 inputStr: '​',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description:
                     'returns expected error if input contains an empty space',
                 inputStr: '​jasdf3873',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description: 'returns expected error if input contains symbols',
                 inputStr: '​jasdf3873@#',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description: 'returns expected error if input is not a string',
                 inputStr: { testAlias: 'string at key' },
                 response: 'Alias input must be a string',
             },
             {
                 description:
                     'returns expected error if input contains underscores',
                 inputStr: 'test_WITH_badchars',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
             {
                 description:
                     'returns expected error if exceeds byte restriction',
                 inputStr: '0123456789012345678901',
                 response: `Invalid bytecount 22. Alias be 1-21 bytes.`,
             },
             {
                 description: 'returns true for an alias of max bytecount',
                 inputStr: '012345678901234567890',
                 response: true,
             },
         ],
     },
     validAliasSendInputCases: {
         expectedReturns: [
             {
                 description: 'Valid alias send input',
                 sendToAliasInput: 'chicken.xec',
                 response: true,
             },
             {
                 description: 'Valid alias missing prefix',
                 sendToAliasInput: 'chicken',
                 response: `Must include '.xec' suffix when sending to an eCash alias`,
             },
             {
                 description: 'Valid alias with double suffix',
                 sendToAliasInput: 'chicken.xec.xec',
                 response: `Must include '.xec' suffix when sending to an eCash alias`,
             },
             {
                 description: 'Valid alias with bad suffix',
                 sendToAliasInput: 'chicken.xe',
                 response: `Must include '.xec' suffix when sending to an eCash alias`,
             },
             {
                 description: 'Invalid alias (too long)',
                 sendToAliasInput: '0123456789012345678901.xec',
                 response: `Invalid bytecount 22. Alias be 1-21 bytes.`,
             },
             {
                 description: 'Invalid alias (nonalphanumeric)',
                 sendToAliasInput: 'Capitalized@.xec',
                 response:
                     'Alias may only contain lowercase characters a-z and 0-9',
             },
         ],
     },
     parseAddressInputCases: {
         expectedReturns: [
             // address only
             {
                 description: 'Blank string',
                 addressInput: '',
                 parsedAddressInput: {
                     address: {
                         value: '',
                         error: 'Invalid address',
                         isAlias: false,
                     },
                 },
             },
             {
                 description: 'Address only and no querystring',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                 },
             },
             {
                 description: 'prefixless address input',
                 addressInput: 'qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                 parsedAddressInput: {
                     address: {
                         value: 'qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                 },
             },
             // alias only
             {
                 description: 'alias only and no querystring',
                 addressInput: 'chicken.xec',
                 parsedAddressInput: {
                     address: {
                         value: 'chicken.xec',
                         error: false,
                         isAlias: true,
                     },
                 },
             },
             {
                 description: 'alias missing .xec suffix',
                 addressInput: 'chicken',
                 parsedAddressInput: {
                     address: {
                         value: 'chicken',
                         error: `Aliases must end with '.xec'`,
                         isAlias: true,
                     },
                 },
             },
             // amount param only
             {
                 description:
                     'Valid address with valid amount param, no decimals',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=500000',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     amount: { value: '500000', error: false },
                     queryString: { value: 'amount=500000', error: false },
                 },
             },
             {
                 description:
                     'Valid address with valid amount param, with decimals',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=123.45',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     amount: { value: '123.45', error: false },
                     queryString: { value: 'amount=123.45', error: false },
                 },
             },
             {
                 description: 'Invalid address with valid amount param',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfg?amount=500000',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfg',
                         error: 'Invalid address',
                         isAlias: false,
                     },
                     amount: { value: '500000', error: false },
                     queryString: { value: 'amount=500000', error: false },
                 },
             },
             {
                 description: 'etoken address with valid amount param',
                 addressInput:
                     'etoken:qq9h6d0a5q65fgywv4ry64x04ep906mdkufhx2swv3?amount=500000',
                 parsedAddressInput: {
                     address: {
                         value: 'etoken:qq9h6d0a5q65fgywv4ry64x04ep906mdkufhx2swv3',
                         error: `eToken addresses are not supported for ${appConfig.ticker} sends`,
                         isAlias: false,
                     },
                     amount: { value: '500000', error: false },
                     queryString: { value: 'amount=500000', error: false },
                 },
             },
             {
                 description:
                     'Valid address with invalid amount param (too many decimal places)',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=123.456',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     amount: {
                         value: '123.456',
                         error: `Invalid XEC send amount "123.456"`,
                     },
                     queryString: { value: 'amount=123.456', error: false },
                 },
             },
             {
                 description: 'Valid alias with valid amount param',
                 addressInput: 'chicken.xec?amount=125',
                 parsedAddressInput: {
                     address: {
                         value: 'chicken.xec',
                         error: false,
                         isAlias: true,
                     },
                     amount: { value: '125', error: false },
                     queryString: { value: 'amount=125', error: false },
                 },
             },
             {
                 description: 'Invalid alias with valid amount param',
                 addressInput: 'chicken?amount=125',
                 parsedAddressInput: {
                     address: {
                         value: 'chicken',
                         error: `Aliases must end with '.xec'`,
                         isAlias: true,
                     },
                     amount: { value: '125', error: false },
                     queryString: { value: 'amount=125', error: false },
                 },
             },
             // opreturn param only
             {
-                description: 'Valid address with valid opreturn param',
+                description: 'Valid address with valid op_return_raw param',
                 addressInput:
-                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
-                    opreturn: {
+                    op_return_raw: {
                         value: '042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                     queryString: {
-                        value: 'opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                        value: 'op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                 },
             },
             {
-                description: 'Valid alias with valid opreturn param',
+                description: 'Valid alias with valid op_return_raw param',
                 addressInput:
-                    'chicken.xec?opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                    'chicken.xec?op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                 parsedAddressInput: {
                     address: {
                         value: 'chicken.xec',
                         error: false,
                         isAlias: true,
                     },
-                    opreturn: {
+                    op_return_raw: {
                         value: '042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                     queryString: {
-                        value: 'opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                        value: 'op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                 },
             },
             {
-                description: 'Valid address with invalid opreturn param',
+                description: 'Valid address with invalid op_return_raw param',
                 addressInput:
-                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?opreturn=notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?op_return_raw=notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
-                    opreturn: {
+                    op_return_raw: {
                         value: 'notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
-                        error: `Invalid opreturn param "notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d"`,
+                        error: `Invalid op_return_raw param "notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d"`,
                     },
                     queryString: {
-                        value: 'opreturn=notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                        value: 'op_return_raw=notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                 },
             },
-            // Both opreturn and amount params
+            // Both op_return_raw and amount params
             {
-                description: 'Valid amount and opreturn params',
+                description: 'Valid amount and op_return_raw params',
                 addressInput:
-                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=500&opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=500&op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     amount: { value: '500', error: false },
-                    opreturn: {
+                    op_return_raw: {
                         value: '042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                     queryString: {
-                        value: 'amount=500&opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                        value: 'amount=500&op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: false,
                     },
                 },
             },
 
             {
                 description: 'invalid querystring (unsupported params)',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?*&@^&%@amount=-500000',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     queryString: {
                         value: '*&@^&%@amount=-500000',
                         error: `Unsupported param "%@amount"`,
                     },
                 },
             },
             // Querystring errors where no params can be returned
             {
                 description: 'Invalid queryString, repeated param',
                 addressInput:
                     'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?amount=123.45&amount=678.9',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     queryString: {
                         value: 'amount=123.45&amount=678.9',
                         error: 'bip21 parameters may not appear more than once',
                     },
                 },
             },
             {
-                description: 'Repeated opreturn param',
+                description: 'Repeated op_return_raw param',
                 addressInput:
-                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d&opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                    'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx?op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d&op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                 parsedAddressInput: {
                     address: {
                         value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
                         error: false,
                         isAlias: false,
                     },
                     queryString: {
-                        value: 'opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d&opreturn=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
+                        value: 'op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d&op_return_raw=042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
                         error: `bip21 parameters may not appear more than once`,
                     },
                 },
             },
         ],
     },
 };
diff --git a/cashtab/src/validation/index.js b/cashtab/src/validation/index.js
index 8387b26a4..b7c716539 100644
--- a/cashtab/src/validation/index.js
+++ b/cashtab/src/validation/index.js
@@ -1,749 +1,749 @@
 import { BN } from 'slp-mdm';
 import { fromSatoshisToXec } from 'utils/cashMethods';
 import cashaddr from 'ecashaddrjs';
 import * as bip39 from 'bip39';
 import {
     cashtabSettings as cashtabDefaultConfig,
     cashtabSettingsValidation,
 } from 'config/cashtabSettings';
 import tokenBlacklist from 'config/tokenBlacklist';
 import { queryAliasServer } from 'utils/aliasUtils';
 import defaultCashtabCache from 'config/cashtabCache';
 import appConfig from 'config/app';
 import { opReturn } from 'config/opreturn';
 import { getStackArray } from 'ecash-script';
 import aliasSettings from 'config/alias';
 import { getAliasByteCount } from 'opreturn';
 
 /**
  * Checks whether the instantiated sideshift library object has loaded
  * correctly with the expected API.
  *
  * @param {Object} sideshiftObj the instantiated sideshift library object
  * @returns {boolean} whether or not this sideshift object is valid
  */
 export const isValidSideshiftObj = sideshiftObj => {
     return (
         sideshiftObj !== null &&
         typeof sideshiftObj === 'object' &&
         typeof sideshiftObj.show === 'function' &&
         typeof sideshiftObj.hide === 'function' &&
         typeof sideshiftObj.addEventListener === 'function'
     );
 };
 
 // Parses whether the value is a valid eCash address
 // or a valid and registered alias
 export const isValidRecipient = async value => {
     if (cashaddr.isValidCashAddress(value, 'ecash')) {
         return true;
     }
     // If not a valid XEC address, check if it's an alias
     if (isValidAliasSendInput(value) !== true) {
         return false;
     }
     // extract alias without the `.xec`
     const aliasName = value.slice(0, -4);
     try {
         const aliasDetails = await queryAliasServer('alias', aliasName);
         return aliasDetails && !aliasDetails.error && !!aliasDetails.address;
     } catch (err) {
         console.log(`isValidRecipient(): Error retrieving alias details`, err);
     }
     return false;
 };
 
 /**
  * Does a given string meet the spec of a valid ecash alias
  * See spec a doc/standards/ecash-alias.md
  * Note that an alias is only "valid" if it has been registered
  * So here, we are only testing spec compliance
  * @param {string} inputStr
  * @returns {true | string} true if isValid, string for reason why if not
  */
 export const meetsAliasSpec = inputStr => {
     if (typeof inputStr !== 'string') {
         return 'Alias input must be a string';
     }
     if (!/^[a-z0-9]+$/.test(inputStr)) {
         return 'Alias may only contain lowercase characters a-z and 0-9';
     }
     const aliasByteCount = getAliasByteCount(inputStr);
     if (aliasByteCount > aliasSettings.aliasMaxLength) {
         return `Invalid bytecount ${aliasByteCount}. Alias be 1-21 bytes.`;
     }
     return true;
 };
 
 /**
  * Validate user input of an alias for cases that require the .xec suffix
  * Note this only validates the format according to spec and requirements
  * Must validate with indexer for associated ecash address before a tx is broadcast
  * @param {string} sendToAliasInput
  * @returns {true | string}
  */
 export const isValidAliasSendInput = sendToAliasInput => {
     // To send to an alias, a user must include the '.xec' extension
     // This is to prevent confusion with alias platforms on other crypto networks
     const aliasParts = sendToAliasInput.split('.');
     const aliasName = aliasParts[0];
     const aliasMeetsSpec = meetsAliasSpec(aliasName);
     if (aliasMeetsSpec !== true) {
         return aliasMeetsSpec;
     }
     if (aliasParts.length !== 2 || aliasParts[1] !== 'xec') {
         return `Must include '.xec' suffix when sending to an eCash alias`;
     }
     return true;
 };
 
 export const validateMnemonic = (
     mnemonic,
     wordlist = bip39.wordlists.english,
 ) => {
     try {
         if (!mnemonic || !wordlist) return false;
 
         // Preprocess the words
         const words = mnemonic.split(' ');
         // Detect blank phrase
         if (words.length === 0) return false;
 
         // Check the words are valid
         return bip39.validateMnemonic(mnemonic, wordlist);
     } catch (err) {
         console.log(err);
         return false;
     }
 };
 
 // Validate cash amount
 export const shouldRejectAmountInput = (
     cashAmount,
     selectedCurrency,
     fiatPrice,
     totalCashBalance,
 ) => {
     // Take cashAmount as input, a string from form input
     let error = false;
     let testedAmount = new BN(cashAmount);
 
     if (selectedCurrency !== appConfig.ticker) {
         // Ensure no more than appConfig.cashDecimals decimal places
         testedAmount = new BN(fiatToCrypto(cashAmount, fiatPrice));
     }
 
     // Validate value for > 0
     if (isNaN(testedAmount)) {
         error = 'Amount must be a number';
     } else if (testedAmount.lte(0)) {
         error = 'Amount must be greater than 0';
     } else if (testedAmount.lt(fromSatoshisToXec(appConfig.dustSats))) {
         error = `Send amount must be at least ${fromSatoshisToXec(
             appConfig.dustSats,
         ).toString()} ${appConfig.ticker}`;
     } else if (testedAmount.gt(totalCashBalance)) {
         error = `Amount cannot exceed your ${appConfig.ticker} balance`;
     } else if (!isNaN(testedAmount) && testedAmount.toString().includes('.')) {
         if (
             testedAmount.toString().split('.')[1].length >
             appConfig.cashDecimals
         ) {
             error = `${appConfig.ticker} transactions do not support more than ${appConfig.cashDecimals} decimal places`;
         }
     }
     // return false if no error, or string error msg if error
     return error;
 };
 
 export const fiatToCrypto = (
     fiatAmount,
     fiatPrice,
     cashDecimals = appConfig.cashDecimals,
 ) => {
     let cryptoAmount = new BN(fiatAmount)
         .div(new BN(fiatPrice))
         .toFixed(cashDecimals);
     return cryptoAmount;
 };
 
 export const isProbablyNotAScam = tokenNameOrTicker => {
     // convert to lower case, trim leading and trailing spaces
     // split, filter then join on ' ' for cases where user inputs multiple spaces
     const sanitized = tokenNameOrTicker
         .toLowerCase()
         .trim()
         .split(' ')
         .filter(string => string)
         .join(' ');
 
     return !tokenBlacklist.includes(sanitized);
 };
 
 export const isValidTokenName = tokenName => {
     return (
         typeof tokenName === 'string' &&
         tokenName.length > 0 &&
         tokenName.length < 68
     );
 };
 
 export const isValidTokenTicker = tokenTicker => {
     return (
         typeof tokenTicker === 'string' &&
         tokenTicker.length > 0 &&
         tokenTicker.length < 13
     );
 };
 
 export const isValidTokenDecimals = tokenDecimals => {
     return ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(
         tokenDecimals,
     );
 };
 
 export const isValidTokenInitialQty = (tokenInitialQty, tokenDecimals) => {
     const minimumQty = new BN(1 / 10 ** tokenDecimals);
     const tokenIntialQtyBig = new BN(tokenInitialQty);
     return (
         tokenIntialQtyBig.gte(minimumQty) &&
         tokenIntialQtyBig.lt(100000000000) &&
         tokenIntialQtyBig.dp() <= tokenDecimals
     );
 };
 
 export const isValidTokenDocumentUrl = tokenDocumentUrl => {
     const urlPattern = new RegExp(
         '^(https?:\\/\\/)?' + // protocol
             '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
             '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
             '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
             '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
             '(\\#[-a-z\\d_]*)?$',
         'i',
     ); // fragment locator
 
     const urlTestResult = urlPattern.test(tokenDocumentUrl);
     return (
         tokenDocumentUrl === '' ||
         (typeof tokenDocumentUrl === 'string' &&
             tokenDocumentUrl.length >= 0 &&
             tokenDocumentUrl.length < 68 &&
             urlTestResult)
     );
 };
 
 export const isValidCashtabSettings = settings => {
     try {
         let isValidSettingParams = true;
         for (let param in cashtabDefaultConfig) {
             if (
                 !Object.prototype.hasOwnProperty.call(settings, param) ||
                 !cashtabSettingsValidation[param].includes(settings[param])
             ) {
                 isValidSettingParams = false;
                 break;
             }
         }
         const isValid = typeof settings === 'object' && isValidSettingParams;
 
         return isValid;
     } catch (err) {
         return false;
     }
 };
 
 export const parseInvalidSettingsForMigration = invalidCashtabSettings => {
     // create a copy of the invalidCashtabSettings
     let migratedCashtabSettings = invalidCashtabSettings;
     // determine if settings are invalid because it is missing a parameter
     for (let param in cashtabDefaultConfig) {
         if (
             !Object.prototype.hasOwnProperty.call(invalidCashtabSettings, param)
         ) {
             // adds the default setting for only that parameter
             migratedCashtabSettings[param] = cashtabDefaultConfig[param];
         }
     }
     return migratedCashtabSettings;
 };
 
 export const parseInvalidCashtabCacheForMigration = invalidCashtabCache => {
     // create a copy of the invalidCashtabCache
     let migratedCashtabCache = invalidCashtabCache;
     // determine if settings are invalid because it is missing a parameter
     for (let param in defaultCashtabCache) {
         if (!Object.prototype.hasOwnProperty.call(invalidCashtabCache, param)) {
             // adds the default setting for only that parameter
             migratedCashtabCache[param] = defaultCashtabCache[param];
         }
     }
     return migratedCashtabCache;
 };
 
 export const isValidContactList = contactList => {
     /* 
     A valid contact list is an array of objects
     An empty contact list looks like [{}]
     
     Although a valid contact list does not contain duplicated addresses, this is not checked here.
     This is checked for when contacts are added. Duplicate addresses will not break the app if a user
     somehow sideloads a contact list with everything valid except some addresses are duplicated.
     */
     if (!Array.isArray(contactList)) {
         return false;
     }
     for (let i = 0; i < contactList.length; i += 1) {
         const contactObj = contactList[i];
         // Must have keys 'address' and 'name'
         if (
             typeof contactObj === 'object' &&
             'address' in contactObj &&
             'name' in contactObj
         ) {
             // Address must be a valid XEC address, name must be a string
             if (
                 cashaddr.isValidCashAddress(contactObj.address, 'ecash') &&
                 typeof contactObj.name === 'string'
             ) {
                 continue;
             }
             return false;
         } else {
             // Check for empty object in an array of length 1, the default blank contactList
             if (
                 contactObj &&
                 Object.keys(contactObj).length === 0 &&
                 Object.getPrototypeOf(contactObj) === Object.prototype &&
                 contactList.length === 1
             ) {
                 // [{}] is valid, default blank
                 // But a list with random blanks is not valid
                 return true;
             }
             return false;
         }
     }
     // If you get here, it's good
     return true;
 };
 
 export const isValidCashtabCache = cashtabCache => {
     /* 
         Object must contain all keys listed in defaultCashtabCache
         The tokenInfoById object must have keys that are valid token IDs, 
         and at each one an object like:
         {
             "tokenTicker": "ST",
             "tokenName": "ST",
             "tokenDocumentUrl": "developer.bitcoin.com",
             "tokenDocumentHash": "",
             "decimals": 0,
             "tokenId": "bf24d955f59351e738ecd905966606a6837e478e1982943d724eab10caad82fd"
         }
 
         i.e. an object that contains these keys
         'tokenTicker' is a string
         'tokenName' is a string
         'tokenDocumentUrl' is a string
         'tokenDocumentHash' is a string
         'decimals' is a number
         'tokenId' is a valid tokenId
     */
 
     // Check that every key in defaultCashtabCache is also in this cashtabCache
     const cashtabCacheKeys = Object.keys(defaultCashtabCache);
     for (let i = 0; i < cashtabCacheKeys.length; i += 1) {
         if (!(cashtabCacheKeys[i] in cashtabCache)) {
             return false;
         }
     }
 
     // Check that tokenInfoById is expected type and that tokenIds are valid
 
     const { tokenInfoById } = cashtabCache;
 
     const tokenIds = Object.keys(tokenInfoById);
 
     for (let i = 0; i < tokenIds.length; i += 1) {
         const thisTokenId = tokenIds[i];
         if (!isValidTokenId(thisTokenId)) {
             return false;
         }
         const {
             tokenTicker,
             tokenName,
             tokenDocumentUrl,
             tokenDocumentHash,
             decimals,
             tokenId,
         } = tokenInfoById[thisTokenId];
 
         if (
             typeof tokenTicker !== 'string' ||
             typeof tokenName !== 'string' ||
             typeof tokenDocumentUrl !== 'string' ||
             typeof tokenDocumentHash !== 'string' ||
             typeof decimals !== 'number' ||
             !isValidTokenId(tokenId)
         ) {
             return false;
         }
     }
 
     return true;
 };
 
 /**
  * Returns true if input is a valid xec send amount
  * @param {string} xecSendAmount
  * @returns {boolean}
  */
 export const isValidXecSendAmount = xecSendAmount => {
     // A valid XEC send amount must be
     // - higher than the app dust limit
     // - have no more than 2 decimal places
     if (typeof xecSendAmount !== 'string') {
         return false;
     }
     // xecSendAmount may only contain numbers and '.', must contain at least 1 char
     const xecSendAmountFormatRegExp = /^[0-9.]+$/;
     const xecSendAmountCharCheck =
         xecSendAmountFormatRegExp.test(xecSendAmount);
 
     if (!xecSendAmountCharCheck) {
         return false;
     }
     if (xecSendAmount.includes('.')) {
         // If you have decimal places
         const decimalCount = xecSendAmount.split('.')[1].length;
         const XEC_DECIMALS = 2;
         if (decimalCount > XEC_DECIMALS) {
             return false;
         }
     }
     return (
         !isNaN(parseFloat(xecSendAmount)) &&
         parseFloat(xecSendAmount) >=
             fromSatoshisToXec(appConfig.dustSats).toNumber()
     );
 };
 
 export const isValidEtokenBurnAmount = (tokenBurnAmount, maxAmount) => {
     // A valid eToken burn amount must be between 1 and the wallet's token balance
     return (
         tokenBurnAmount !== null &&
         maxAmount !== null &&
         typeof tokenBurnAmount !== 'undefined' &&
         typeof maxAmount !== 'undefined' &&
         new BN(tokenBurnAmount).gt(0) &&
         new BN(tokenBurnAmount).lte(maxAmount)
     );
 };
 
 // XEC airdrop field validations
 export const isValidTokenId = tokenId => {
     // disable no-useless-escape for regex
     //eslint-disable-next-line
     const format = /[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/;
     const specialCharCheck = format.test(tokenId);
 
     return (
         typeof tokenId === 'string' &&
         tokenId.length === 64 &&
         tokenId.trim() != '' &&
         !specialCharCheck
     );
 };
 
 export const isValidNewWalletNameLength = newWalletName => {
     return (
         typeof newWalletName === 'string' &&
         newWalletName.length > 0 &&
         newWalletName.length <= appConfig.localStorageMaxCharacters &&
         newWalletName.length !== ''
     );
 };
 
 export const isValidXecAirdrop = xecAirdrop => {
     return (
         typeof xecAirdrop === 'string' &&
         xecAirdrop.length > 0 &&
         xecAirdrop.trim() != '' &&
         new BN(xecAirdrop).gt(0)
     );
 };
 
 export const isValidAirdropOutputsArray = airdropOutputsArray => {
     if (!airdropOutputsArray) {
         return false;
     }
 
     let isValid = true;
 
     // split by individual rows
     const addressStringArray = airdropOutputsArray.split('\n');
 
     for (let i = 0; i < addressStringArray.length; i++) {
         const substring = addressStringArray[i].split(',');
         let valueString = substring[1];
         // if the XEC being sent is less than dust sats or contains extra values per line
         if (
             new BN(valueString).lt(fromSatoshisToXec(appConfig.dustSats)) ||
             substring.length !== 2
         ) {
             isValid = false;
         }
     }
 
     return isValid;
 };
 
 export const isValidAirdropExclusionArray = airdropExclusionArray => {
     if (!airdropExclusionArray || airdropExclusionArray.length === 0) {
         return false;
     }
 
     let isValid = true;
 
     // split by comma as the delimiter
     const addressStringArray = airdropExclusionArray.split(',');
 
     // parse and validate each address in array
     for (let i = 0; i < addressStringArray.length; i++) {
         if (!cashaddr.isValidCashAddress(addressStringArray[i], 'ecash')) {
             return false;
         }
     }
 
     return isValid;
 };
 
 /**
  * Validate user input on Send.js for multi-input mode
  * @param {string} userMultisendInput formData.address from Send.js screen, validated for multi-send
  * @returns {boolean | string} true if is valid, error msg about why if not
  */
 export const isValidMultiSendUserInput = userMultisendInput => {
     if (typeof userMultisendInput !== 'string') {
         // In usage pairing to a form input, this should never happen
         return 'Input must be a string';
     }
     if (userMultisendInput.trim() === '') {
         return 'Input must not be blank';
     }
     let inputLines = userMultisendInput.split('\n');
     for (let i = 0; i < inputLines.length; i += 1) {
         if (inputLines[i].trim() === '') {
             return `Remove empty row at line ${i + 1}`;
         }
 
         const addressAndValueThisLine = inputLines[i].split(',');
 
         const elementsThisLine = addressAndValueThisLine.length;
 
         if (elementsThisLine < 2) {
             return `Line ${
                 i + 1
             } must have address and value, separated by a comma`;
         } else if (elementsThisLine > 2) {
             return `Line ${i + 1}: Comma can only separate address and value.`;
         }
 
         const address = addressAndValueThisLine[0].trim();
         const isValidAddress = cashaddr.isValidCashAddress(address, 'ecash');
 
         if (!isValidAddress) {
             return `Invalid address "${address}" at line ${i + 1}`;
         }
 
         const value = addressAndValueThisLine[1].trim();
         const isValidValue = isValidXecSendAmount(value);
 
         if (!isValidValue) {
             return `Invalid value ${
                 typeof value === 'string' ? value.trim() : `at line ${i + 1}`
             }. Amount must be >= ${(appConfig.dustSats / 100).toFixed(
                 2,
             )} XEC and <= 2 decimals.`;
         }
     }
     // If you make it here, all good
     return true;
 };
 
 /**
  * Test a bip21 opreturn param for spec compliance
  * @param {string} opreturn
  * @returns {bool}
  */
 export const isValidOpreturnParam = testedParam => {
     // Spec
     // The param must contain a valid hex string for a valid `OP_RETURN` output,
     // not beginning with the`OP_RETURN` `6a`.
 
     try {
         if (testedParam === '') {
             // No empty OP_RETURN for this param per ecash bip21 spec
             return false;
         }
 
         // Use validation from ecash-script library
         // Apply .toLowerCase() to support uppercase, lowercase, or mixed case input
         getStackArray(
             `${opReturn.opReturnPrefixHex}${testedParam.toLowerCase()}`,
         );
 
         return true;
     } catch (err) {
         return false;
     }
 };
 
 /**
  * Should the Send button be disabled on the SendXec screen
  * @param {object} formData must have keys address: string and value: string
  * @param {object} balances must have key totalBalance: string
  * @param {boolean} apiError
  * @param {false | string} sendBchAmountError
  * @param {false | string} sendBchAddressError
  * @param {false | string} isMsgError
  * @param {boolean} priceApiError
  * @param {boolean} isOneToManyXECSend
  * @returns boolean
  */
 export const shouldSendXecBeDisabled = (
     formData,
     balances,
     apiError,
     sendAmountError,
     sendAddressError,
     isMsgError,
     priceApiError,
     isOneToManyXECSend,
 ) => {
     return (
         (formData.value === '' && formData.address === '') || // No user inputs
         balances.totalBalance === '0' || // user has no funds
         apiError || // API error
         typeof sendAmountError === 'string' || // validation error for send amount
         typeof sendAddressError === 'string' || // validation error for destinationa ddress
         typeof isMsgError === 'string' || // validation error in Cashtab Msg
         priceApiError || // we don't have a good price AND fiat currency is selected
         (!isOneToManyXECSend &&
             (isNaN(formData.value) || formData.value === ''))
     ); // Value is blank or NaN and is expected to not be so
 };
 
 /**
  * Parse an address string with bip21 params for use in Cashtab
  * @param {string} addressString User input into the send field of Cashtab.
  * Must be validated for bip21 and Cashtab supported features
  * For now, Cashtab supports only
  * amount - amount to be sent in XEC
  * opreturn - raw hex for opreturn output
  * @returns {object} addressInfo. Object with parsed params designed for use in Send.js
  */
 export function parseAddressInput(addressInput) {
     // Build return obj
     const parsedAddressInput = {
         address: { value: null, error: false, isAlias: false },
     };
 
     // Reject non-string input
     if (typeof addressInput !== 'string') {
         parsedAddressInput.address.error = 'Address must be a string';
         return parsedAddressInput;
     }
 
     // Parse address string for parameters
     const paramCheck = addressInput.split('?');
 
     let cleanAddress = paramCheck[0];
 
     // Set cleanAddress to addressInfo.address.value even if validation fails
     // If there is an error, this will be set later
     parsedAddressInput.address.value = cleanAddress;
 
     // Validate address
     const isValidAddr = cashaddr.isValidCashAddress(cleanAddress, 'ecash');
 
     // Is this valid address?
     if (!isValidAddr) {
         // Check if this is an alias address
         if (isValidAliasSendInput(cleanAddress) !== true) {
             if (meetsAliasSpec(cleanAddress) === true) {
                 // If it would be a valid alias except for the missing '.xec', this is a useful validation error
                 parsedAddressInput.address.error = `Aliases must end with '.xec'`;
                 parsedAddressInput.address.isAlias = true;
             } else if (cashaddr.isValidCashAddress(cleanAddress, 'etoken')) {
                 // If it is, though, a valid eToken address
                 parsedAddressInput.address.error = `eToken addresses are not supported for ${appConfig.ticker} sends`;
             } else {
                 // If your address is not a valid address and not a valid alias format
                 parsedAddressInput.address.error = `Invalid address`;
             }
         } else {
             parsedAddressInput.address.isAlias = true;
         }
     }
 
     // Check for parameters
     if (paramCheck.length > 1) {
         // add other keys
 
         const queryString = paramCheck[1];
         parsedAddressInput.queryString = { value: queryString, error: false };
 
         // Note that URLSearchParams is not an array
         // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
         const addrParams = new URLSearchParams(queryString);
 
         // Check for duplicated params
         const duplicatedParams =
             new Set(addrParams.keys()).size !==
             Array.from(addrParams.keys()).length;
 
         if (duplicatedParams) {
             // In this case, we can't pass any values back for supported params,
             // without changing the shape of addressInfo
             parsedAddressInput.queryString.error = `bip21 parameters may not appear more than once`;
             return parsedAddressInput;
         }
 
-        const supportedParams = ['amount', 'opreturn'];
+        const supportedParams = ['amount', 'op_return_raw'];
 
         // Iterate over params to check for valid and/or invalid params
         for (const paramKeyValue of addrParams) {
             const paramKey = paramKeyValue[0];
             if (!supportedParams.includes(paramKey)) {
                 // queryString error
                 // Keep parsing for other params though
                 parsedAddressInput.queryString.error = `Unsupported param "${paramKey}"`;
             }
             if (paramKey === 'amount') {
                 // Handle Cashtab-supported bip21 param 'amount'
                 const amount = paramKeyValue[1];
                 parsedAddressInput.amount = { value: amount, error: false };
                 if (!isValidXecSendAmount(amount)) {
                     // amount must be a valid xec send amount
                     parsedAddressInput.amount.error = `Invalid XEC send amount "${amount}"`;
                 }
             }
-            if (paramKey === 'opreturn') {
-                // Handle Cashtab-supported bip21 param 'opreturn'
+            if (paramKey === 'op_return_raw') {
+                // Handle Cashtab-supported bip21 param 'op_return_raw'
                 const opreturnParam = paramKeyValue[1];
-                parsedAddressInput.opreturn = {
+                parsedAddressInput.op_return_raw = {
                     value: opreturnParam,
                     error: false,
                 };
                 if (!isValidOpreturnParam(opreturnParam)) {
                     // opreturn must be valid
-                    parsedAddressInput.opreturn.error = `Invalid opreturn param "${opreturnParam}"`;
+                    parsedAddressInput.op_return_raw.error = `Invalid op_return_raw param "${opreturnParam}"`;
                 }
             }
         }
     }
 
     return parsedAddressInput;
 }