Page MenuHomePhabricator

D17140.id50877.diff
No OneTemporary

D17140.id50877.diff

diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json
--- a/cashtab/package-lock.json
+++ b/cashtab/package-lock.json
@@ -44,6 +44,7 @@
"@types/lodash.debounce": "^4.0.9",
"@types/randombytes": "^2.0.3",
"@types/react": "^18.3.12",
+ "@types/react-toastify": "^4.0.2",
"@types/styled-components": "^5.1.34",
"@types/wif": "^2.0.5",
"assert": "^2.0.0",
@@ -24020,6 +24021,27 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/@types/react-toastify": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/react-toastify/-/react-toastify-4.0.2.tgz",
+ "integrity": "sha512-pHjCstnN0ZgopIWQ9UiWsD9n+HsXs1PnMQC4hIZuSzpDO0lRjigpTuqsUtnBkMbLIg+mGFSAsBjL49SspzoLKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*",
+ "@types/react-transition-group": "*"
+ }
+ },
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.11",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz",
+ "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/resolve": {
"version": "1.17.1",
"dev": true,
diff --git a/cashtab/package.json b/cashtab/package.json
--- a/cashtab/package.json
+++ b/cashtab/package.json
@@ -61,6 +61,7 @@
"@types/lodash.debounce": "^4.0.9",
"@types/randombytes": "^2.0.3",
"@types/react": "^18.3.12",
+ "@types/react-toastify": "^4.0.2",
"@types/styled-components": "^5.1.34",
"@types/wif": "^2.0.5",
"assert": "^2.0.0",
diff --git a/cashtab/src/alias/index.js b/cashtab/src/alias/index.ts
rename from cashtab/src/alias/index.js
rename to cashtab/src/alias/index.ts
--- a/cashtab/src/alias/index.js
+++ b/cashtab/src/alias/index.ts
@@ -32,7 +32,32 @@
* processedBlockheight: 802965,
* }
*/
-export const queryAliasServer = async (endPoint, aliasParam = false) => {
+
+/**
+ * alias types
+ * Note we will likely implement in a different way
+ */
+
+export interface Alias {
+ alias: string;
+ isRegistered: boolean;
+ registrationFeeSats: number;
+ processedBlockheight: number;
+}
+export interface AddressAliasStatus {
+ registered: Alias[];
+ pending: Alias[];
+}
+export interface AliasPrices {
+ prices: string[];
+}
+interface AliasErrorResponse {
+ error: string;
+}
+export const queryAliasServer = async (
+ endPoint: string,
+ aliasParam: boolean | string = false,
+): Promise<AliasPrices | Alias[] | Alias | AddressAliasStatus> => {
let aliasServerResp;
const fetchUrl = !aliasParam
? `${aliasSettings.aliasServerBaseUrl}/${endPoint}`
@@ -44,10 +69,14 @@
throw new Error('Network request failed');
}
// if alias-server returns a valid error message to the query e.g. address not found
- if (aliasServerResp.error) {
- throw new Error(aliasServerResp.error);
+ if ('error' in aliasServerResp) {
+ throw new Error(
+ (aliasServerResp as unknown as AliasErrorResponse).error,
+ );
}
- return await aliasServerResp.json();
+ aliasServerResp = await aliasServerResp.json();
+
+ return aliasServerResp;
} catch (err) {
console.error(
`queryAliasServer(): Error retrieving alias data from alias-server`,
diff --git a/cashtab/src/chronik/index.js b/cashtab/src/chronik/index.ts
rename from cashtab/src/chronik/index.js
rename to cashtab/src/chronik/index.ts
--- a/cashtab/src/chronik/index.js
+++ b/cashtab/src/chronik/index.ts
@@ -10,11 +10,23 @@
getHashes,
decimalizeTokenAmount,
undecimalizeTokenAmount,
+ CashtabUtxo,
+ CashtabWallet,
+ SlpUtxo,
+ NonSlpUtxo,
+ SlpDecimals,
+ CashtabTx,
} from 'wallet';
+import { ChronikClient, TxHistoryPage, ScriptUtxo, Tx } from 'chronik-client';
+import { CashtabCachedTokenInfo } from 'config/CashtabCache';
const CHRONIK_MAX_PAGE_SIZE = 200;
-export const getTxHistoryPage = async (chronik, hash160, page = 0) => {
+export const getTxHistoryPage = async (
+ chronik: ChronikClient,
+ hash160: string,
+ page = 0,
+): Promise<void | TxHistoryPage> => {
let txHistoryPage;
try {
txHistoryPage = await chronik
@@ -27,11 +39,11 @@
}
};
-export const returnGetTxHistoryPagePromise = async (
- chronik,
- hash160,
+export const returnGetTxHistoryPagePromise = (
+ chronik: ChronikClient,
+ hash160: string,
page = 0,
-) => {
+): Promise<TxHistoryPage> => {
/*
Unlike getTxHistoryPage, this function will reject and
fail Promise.all() if there is an error in the chronik call
@@ -51,7 +63,13 @@
});
};
-export const isAliasRegistered = (registeredAliases, alias) => {
+interface Alias {
+ alias: string;
+}
+export const isAliasRegistered = (
+ registeredAliases: Alias[],
+ alias: string,
+): boolean => {
for (let i = 0; i < registeredAliases.length; i++) {
if (
registeredAliases[i].alias.toString().toLowerCase() ===
@@ -68,22 +86,28 @@
* Return a promise to fetch all utxos at an address (and add a 'path' key to them)
* We need the path key so that we know which wif to sign this utxo with
* If we add HD wallet support, we will need to add an address key, and change the structure of wallet.paths
- * @param {ChronikClient} chronik
- * @param {string} address
- * @param {number} path
- * @returns {Promise}
+ * @param chronik
+ * @paramaddress
+ * @param path
*/
-export const returnGetPathedUtxosPromise = (chronik, address, path) => {
+export const returnGetPathedUtxosPromise = (
+ chronik: ChronikClient,
+ address: string,
+ path: number,
+): Promise<CashtabUtxo[]> => {
return new Promise((resolve, reject) => {
chronik
.address(address)
.utxos()
.then(
result => {
- for (const utxo of result.utxos) {
- utxo.path = path;
- }
- resolve(result.utxos);
+ const cashtabUtxos: CashtabUtxo[] = result.utxos.map(
+ (utxo: ScriptUtxo) => ({
+ ...utxo,
+ path: path,
+ }),
+ );
+ resolve(cashtabUtxos);
},
err => {
reject(err);
@@ -94,12 +118,15 @@
/**
* Get all utxos for a given wallet
- * @param {ChronikClient} chronik
- * @param {object} wallet a cashtab wallet
+ * @param chronik
+ * @param wallet a cashtab wallet
* @returns
*/
-export const getUtxos = async (chronik, wallet) => {
- const chronikUtxoPromises = [];
+export const getUtxos = async (
+ chronik: ChronikClient,
+ wallet: CashtabWallet,
+): Promise<CashtabUtxo[]> => {
+ const chronikUtxoPromises: Promise<CashtabUtxo[]>[] = [];
wallet.paths.forEach((pathInfo, path) => {
const thisPromise = returnGetPathedUtxosPromise(
chronik,
@@ -113,21 +140,26 @@
return flatUtxos;
};
+interface OrganizedUtxos {
+ slpUtxos: SlpUtxo[];
+ nonSlpUtxos: NonSlpUtxo[];
+}
/**
* Organize utxos by token and non-token
* TODO deprecate this and use better coinselect methods
- * @param {Tx[]} chronikUtxos
- * @returns {object} {slpUtxos: [], nonSlpUtxos: []}
+ * @param chronikUtxos
*/
-export const organizeUtxosByType = chronikUtxos => {
+export const organizeUtxosByType = (
+ chronikUtxos: CashtabUtxo[],
+): OrganizedUtxos => {
const nonSlpUtxos = [];
const slpUtxos = [];
for (const utxo of chronikUtxos) {
// Construct nonSlpUtxos and slpUtxos arrays
if (typeof utxo.token !== 'undefined') {
- slpUtxos.push(utxo);
+ slpUtxos.push(utxo as SlpUtxo);
} else {
- nonSlpUtxos.push(utxo);
+ nonSlpUtxos.push(utxo as NonSlpUtxo);
}
}
@@ -136,11 +168,13 @@
/**
* Get just the tx objects from chronik history() responses
- * @param {TxHistoryPage[]} txHistoryOfAllAddresses
- * @returns {Tx[]}
+ * @param txHistoryOfAllAddresses
+ * @returns
*/
-export const flattenChronikTxHistory = txHistoryOfAllAddresses => {
- let flatTxHistoryArray = [];
+export const flattenChronikTxHistory = (
+ txHistoryOfAllAddresses: TxHistoryPage[],
+) => {
+ let flatTxHistoryArray: Tx[] = [];
for (const txHistoryThisAddress of txHistoryOfAllAddresses) {
flatTxHistoryArray = flatTxHistoryArray.concat(
txHistoryThisAddress.txs,
@@ -151,11 +185,14 @@
/**
* Sort an array of chronik txs chronologically and return the first renderedCount of them
- * @param {Tx[]} txs
- * @param {number} renderedCount how many txs to return
+ * @param txs
+ * @param renderedCount how many txs to return
* @returns
*/
-export const sortAndTrimChronikTxHistory = (txs, renderedCount) => {
+export const sortAndTrimChronikTxHistory = (
+ txs: Tx[],
+ renderedCount: number,
+): Tx[] => {
const unconfirmedTxs = [];
const confirmedTxs = [];
for (const tx of txs) {
@@ -170,7 +207,7 @@
const sortedConfirmedTxHistoryArray = confirmedTxs.sort(
(a, b) =>
// We want more recent blocks i.e. higher blockheights to have earlier array indices
- b.block.height - a.block.height ||
+ b.block!.height - a.block!.height ||
// For blocks with the same height, we want more recent timeFirstSeen i.e. higher timeFirstSeen to have earlier array indices
b.timeFirstSeen - a.timeFirstSeen,
);
@@ -191,15 +228,25 @@
return trimmedAndSortedChronikTxHistoryArray;
};
+export enum XecTxType {
+ Received = 'Received',
+ Sent = 'Sent',
+ Staking = 'Staking Reward',
+ Coinbase = 'Coinbase Reward',
+}
+export interface ParsedTx {
+ recipients: string[];
+ satoshisSent: number;
+ stackArray: string[];
+ xecTxType: XecTxType;
+}
/**
* Parse a Tx object for rendering in Cashtab
* TODO Potentially more efficient to do this calculation in the Tx.js component
- * @param {Tx} tx
- * @param {object} wallet cashtab wallet
- * @param {Map} cachedTokens
- * @returns
+ * @param tx
+ * @param hashes array of wallet hashes, one for each path
*/
-export const parseTx = (tx, hashes) => {
+export const parseTx = (tx: Tx, hashes: string[]): ParsedTx => {
const { inputs, outputs, isCoinbase } = tx;
// Assign defaults
@@ -207,7 +254,7 @@
let stackArray = [];
// If it is not an incoming tx, make an educated guess about what addresses were sent to
- const destinationAddresses = new Set();
+ const destinationAddresses: Set<string> = new Set();
// Iterate over inputs to see if this is an incoming tx (incoming === true)
for (const input of inputs) {
@@ -281,7 +328,7 @@
? receivedSatoshis
: outputSatoshis - change;
- let xecTxType = incoming ? 'Received' : 'Sent';
+ let xecTxType = incoming ? XecTxType.Received : XecTxType.Sent;
// Parse for tx label
if (isCoinbase) {
@@ -303,10 +350,10 @@
outputSatoshis,
)
) {
- xecTxType = 'Staking Reward';
+ xecTxType = XecTxType.Staking;
} else {
// We do not specifically parse for IFP reward vs miner reward
- xecTxType = 'Coinbase Reward';
+ xecTxType = XecTxType.Coinbase;
}
}
@@ -325,13 +372,17 @@
* - Trim to number of txs Cashtab renders
* - Parse txs for rendering in Cashtab
* - Update cachedTokens with any new tokenIds
- * @param {ChronikClient} chronik chronik-client instance
- * @param {object} wallet cashtab wallet
- * @param {Map} cachedTokens the map stored at cashtabCache.tokens
- * @returns {array} Tx[], each tx also has a 'parsed' key with other rendering info
+ * @param chronik chronik-client instance
+ * @param wallet cashtab wallet
+ * @param cachedTokens the map stored at cashtabCache.tokens
+ * @returns Tx[], each tx also has a 'parsed' key with other rendering info
*/
-export const getHistory = async (chronik, wallet, cachedTokens) => {
- const txHistoryPromises = [];
+export const getHistory = async (
+ chronik: ChronikClient,
+ wallet: CashtabWallet,
+ cachedTokens: Map<string, CashtabCachedTokenInfo>,
+): Promise<CashtabTx[]> => {
+ const txHistoryPromises: Promise<TxHistoryPage>[] = [];
wallet.paths.forEach(pathInfo => {
txHistoryPromises.push(chronik.address(pathInfo.address).history());
});
@@ -347,12 +398,12 @@
);
// Parse txs
- const history = [];
+ const history: CashtabTx[] = [];
for (const tx of renderedTxs) {
const { tokenEntries } = tx;
// Get all tokenIds associated with this tx
- const tokenIds = new Set();
+ const tokenIds: Set<string> = new Set();
for (const tokenEntry of tokenEntries) {
tokenIds.add(tokenEntry.tokenId);
}
@@ -380,9 +431,9 @@
}
}
- tx.parsed = parseTx(tx, getHashes(wallet));
+ (tx as CashtabTx).parsed = parseTx(tx, getHashes(wallet));
- history.push(tx);
+ history.push(tx as CashtabTx);
}
return history;
@@ -390,11 +441,13 @@
/**
* Get all info about a token used in Cashtab's token cache
- * @param {ChronikClient} chronik
- * @param {string} tokenId
- * @returns {object}
+ * @param chronik
+ * @param tokenId
*/
-export const getTokenGenesisInfo = async (chronik, tokenId) => {
+export const getTokenGenesisInfo = async (
+ chronik: ChronikClient,
+ tokenId: string,
+): Promise<CashtabCachedTokenInfo> => {
// We can get timeFirstSeen, block, tokenType, and genesisInfo from the token() endpoint
// If we call this endpoint before the genesis tx is confirmed, we will not get block
// So, block does not need to be included
@@ -415,7 +468,7 @@
* Cached as a decimalized string, e.g. 0.000 if 0 with 3 decimal places
* 1000.000000000 if one thousand with 9 decimal places
*/
- let genesisSupply = decimalizeTokenAmount('0', decimals);
+ let genesisSupply = decimalizeTokenAmount('0', decimals as SlpDecimals);
/**
* genesisMintBatons {number}
@@ -427,11 +480,11 @@
* genesisOutputScripts {Set(<outputScript>)}
* Address(es) where initial token supply was minted
*/
- let genesisOutputScripts = new Set();
+ const genesisOutputScripts: Set<string> = new Set();
// Iterate over outputs
for (const output of genesisTxInfo.outputs) {
- if ('token' in output && output.token.tokenId === tokenId) {
+ if (output.token?.tokenId === tokenId) {
// If this output of this genesis tx is associated with this tokenId
const { token, outputScript } = output;
@@ -451,15 +504,19 @@
genesisSupply = decimalizeTokenAmount(
(
- BigInt(undecimalizeTokenAmount(genesisSupply, decimals)) +
- BigInt(amount)
+ BigInt(
+ undecimalizeTokenAmount(
+ genesisSupply,
+ decimals as SlpDecimals,
+ ),
+ ) + BigInt(amount)
).toString(),
- decimals,
+ decimals as SlpDecimals,
);
}
}
- const tokenCache = {
+ const tokenCache: CashtabCachedTokenInfo = {
tokenType,
genesisInfo,
timeFirstSeen,
@@ -468,7 +525,7 @@
genesisOutputScripts: [...genesisOutputScripts],
genesisMintBatons,
};
- if ('block' in tokenInfo) {
+ if (typeof tokenInfo.block !== 'undefined') {
// If the genesis tx is confirmed at the time we check
tokenCache.block = tokenInfo.block;
}
@@ -493,14 +550,18 @@
/**
* Get decimalized balance of every token held by a wallet
* Update Cashtab's tokenCache if any tokens are uncached
- * @param {ChronikClient} chronik
- * @param {array} slpUtxos array of token utxos from chronik
- * @param {Map} tokenCache Cashtab's token cache
- * @returns {Map} Map of tokenId => token balance as decimalized string
+ * @param chronik
+ * @param slpUtxos array of token utxos from chronik
+ * @param tokenCache Cashtab's token cache
+ * @returns Map of tokenId => token balance as decimalized string
* Also updates tokenCache
*/
-export const getTokenBalances = async (chronik, slpUtxos, tokenCache) => {
- const walletStateTokens = new Map();
+export const getTokenBalances = async (
+ chronik: ChronikClient,
+ slpUtxos: SlpUtxo[],
+ tokenCache: Map<string, CashtabCachedTokenInfo>,
+): Promise<Map<string, string>> => {
+ const walletStateTokens: Map<string, string> = new Map();
for (const utxo of slpUtxos) {
// Every utxo in slpUtxos will have a tokenId
const { token } = utxo;
@@ -521,17 +582,17 @@
walletStateTokens.set(
tokenId,
typeof tokenBalanceInMap === 'undefined'
- ? decimalizeTokenAmount(amount, decimals)
+ ? decimalizeTokenAmount(amount, decimals as SlpDecimals)
: decimalizeTokenAmount(
(
BigInt(
undecimalizeTokenAmount(
tokenBalanceInMap,
- decimals,
+ decimals as SlpDecimals,
),
) + BigInt(amount)
).toString(),
- decimals,
+ decimals as SlpDecimals,
),
);
}
@@ -541,16 +602,16 @@
/**
*
- * @param {ChronikClient} chronik
- * @param {string} tokenId
- * @param {number} pageSize usually 200, the chronik max, but accept a parameter to simplify unit testing
+ * @param chronik
+ * @param tokenId
+ * @param pageSize usually 200, the chronik max, but accept a parameter to simplify unit testing
* @returns
*/
export const getAllTxHistoryByTokenId = async (
- chronik,
- tokenId,
+ chronik: ChronikClient,
+ tokenId: string,
pageSize = CHRONIK_MAX_PAGE_SIZE,
-) => {
+): Promise<Tx[]> => {
// We will throw an error if we get an error from chronik fetch
const firstPageResponse = await chronik
.tokenId(tokenId)
@@ -562,7 +623,7 @@
const tokenHistoryPromises = [];
for (let i = 1; i < numPages; i += 1) {
tokenHistoryPromises.push(
- new Promise((resolve, reject) => {
+ new Promise<Tx[]>((resolve, reject) => {
chronik
.tokenId(tokenId)
.history(i, CHRONIK_MAX_PAGE_SIZE)
@@ -590,13 +651,13 @@
/**
* Get all child NFTs from a given parent tokenId
* i.e. get all NFTs in an NFT collection *
- * @param {string} parentTokenId
- * @param {Tx[]} allParentTokenTxHistory
+ * @param parentTokenId
+ * @param allParentTokenTxHistory
*/
export const getChildNftsFromParent = (
- parentTokenId,
- allParentTokenTxHistory,
-) => {
+ parentTokenId: string,
+ allParentTokenTxHistory: Tx[],
+): string[] => {
const childNftsFromThisParent = [];
for (const tx of allParentTokenTxHistory) {
// Check tokenEntries
diff --git a/cashtab/src/components/App/fixtures/mocks.js b/cashtab/src/components/App/fixtures/mocks.ts
rename from cashtab/src/components/App/fixtures/mocks.js
rename to cashtab/src/components/App/fixtures/mocks.ts
--- a/cashtab/src/components/App/fixtures/mocks.js
+++ b/cashtab/src/components/App/fixtures/mocks.ts
@@ -2,7 +2,53 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-export const walletWithXecAndTokens_pre_2_1_0 = {
+import { ScriptUtxo } from 'chronik-client';
+import { CashtabTx, CashtabWallet, NonSlpUtxo, SlpUtxo } from 'wallet';
+import { XecTxType } from 'chronik';
+
+interface LegacyPathInfo_Pre_2_1_0 {
+ publicKey: string;
+ hash160: string;
+ cashAddress: string;
+ fundingWif: string;
+}
+interface LegacyBalances {
+ totalBalanceInSatoshis: string;
+ totalBalance: string;
+}
+interface LegacyTokenInfo {
+ tokenTicker: string;
+ tokenName: string;
+ url: string;
+ hash: string;
+ decimals: number;
+ success: boolean;
+}
+interface LegacyTokenState {
+ tokenId: string;
+ balance: string;
+ info: LegacyTokenInfo;
+}
+interface LegacyCashtabUtx_Pre_2_1_0 extends ScriptUtxo {
+ address: string;
+ tokenId?: string;
+}
+interface LegacyCashtabWalletState_Pre_2_1_0 {
+ balances: LegacyBalances;
+ slpUtxos: LegacyCashtabUtx_Pre_2_1_0[];
+ nonSlpUtxos: LegacyCashtabUtx_Pre_2_1_0[];
+ tokens: LegacyTokenState[];
+ parsedTxHistory: CashtabTx[];
+}
+export interface LegacyCashtabWallet_Pre_2_1_0 {
+ mnemonic: string;
+ name: string;
+ Path145: LegacyPathInfo_Pre_2_1_0;
+ Path245: LegacyPathInfo_Pre_2_1_0;
+ Path1899: LegacyPathInfo_Pre_2_1_0;
+ state: LegacyCashtabWalletState_Pre_2_1_0;
+}
+export const walletWithXecAndTokens_pre_2_1_0: LegacyCashtabWallet_Pre_2_1_0 = {
mnemonic:
'beauty shoe decline spend still weird slot snack coach flee between paper',
name: 'Transaction Fixtures',
@@ -219,44 +265,19 @@
tokenFailedParsings: [],
tokenStatus: 'TOKEN_STATUS_NORMAL',
parsed: {
- incoming: true,
- xecAmount: '5.46',
- isEtokenTx: true,
- etokenAmount: '1',
- isTokenBurn: false,
- tokenEntries: [
- {
- tokenId:
- '3fee3384150b030490b7bee095a63900f66a45f2d8e3002ae2cf17ce3ef4d109',
- tokenType: {
- protocol: 'SLP',
- type: 'SLP_TOKEN_TYPE_FUNGIBLE',
- number: 1,
- },
- txType: 'SEND',
- isInvalid: false,
- burnSummary: '',
- failedColorings: [],
- actualBurnAmount: '0',
- intentionalBurn: '0',
- burnsMintBatons: false,
- },
+ xecTxType: XecTxType.Received,
+ satoshisSent: 546,
+ stackArray: [
+ '534c5000',
+ '01',
+ '53454e44',
+ '3fee3384150b030490b7bee095a63900f66a45f2d8e3002ae2cf17ce3ef4d109',
+ '0000000000000001',
+ '0000000000000377',
],
- genesisInfo: {
- tokenTicker: 'BEAR',
- tokenName: 'BearNip',
- url: 'https://cashtab.com/',
- hash: '',
- decimals: 0,
- success: true,
- },
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: '',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
+ recipients: [
'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
+ ],
},
},
{
@@ -314,17 +335,17 @@
timestamp: 1698187386,
},
parsed: {
- incoming: false,
- xecAmount: '5.53',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'newton',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 553,
+ stackArray: [
+ '2e786563',
+ '00',
+ '6e6577746f6e',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -382,17 +403,17 @@
timestamp: 1697463218,
},
parsed: {
- incoming: false,
- xecAmount: '5.51',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'doesthisclear',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 551,
+ stackArray: [
+ '2e786563',
+ '00',
+ '646f657374686973636c656172',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -450,17 +471,17 @@
timestamp: 1697216026,
},
parsed: {
- incoming: false,
- xecAmount: '5.51',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'worksnow',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 551,
+ stackArray: [
+ '2e786563',
+ '00',
+ '776f726b736e6f77',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -518,17 +539,17 @@
timestamp: 1697211295,
},
parsed: {
- incoming: false,
- xecAmount: '5.54',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'test4',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 554,
+ stackArray: [
+ '2e786563',
+ '00',
+ '7465737434',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -586,17 +607,17 @@
timestamp: 1697211196,
},
parsed: {
- incoming: false,
- xecAmount: '5.55',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'test',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 555,
+ stackArray: [
+ '2e786563',
+ '00',
+ '74657374',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -654,17 +675,17 @@
timestamp: 1697211196,
},
parsed: {
- incoming: false,
- xecAmount: '5.55',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'test',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 555,
+ stackArray: [
+ '2e786563',
+ '00',
+ '74657374',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -722,17 +743,17 @@
timestamp: 1697025138,
},
parsed: {
- incoming: false,
- xecAmount: '5.54',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'alias',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 554,
+ stackArray: [
+ '2e786563',
+ '00',
+ '616c696173',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
},
},
{
@@ -774,633 +795,207 @@
spentBy: {
txid: 'f17814a75dad000557f19a3a3f6fcc124ab7880292c9fad4c64dc034d5e46551',
outIdx: 0,
- },
- },
- ],
- lockTime: 0,
- timeFirstSeen: 0,
- size: 267,
- isCoinbase: false,
- tokenEntries: [],
- tokenFailedParsings: [],
- tokenStatus: 'TOKEN_STATUS_NON_TOKEN',
- block: {
- height: 813615,
- hash: '000000000000000006b8990c77dd7a0d3a850a052b6f0bd60b82d44d1ffa7a55',
- timestamp: 1697025138,
- },
- parsed: {
- incoming: false,
- xecAmount: '5.54',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'alias',
- isCashtabMessage: false,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: true,
- },
- },
- {
- txid: '8b3cb0e6c38ee01f9e1e98d611895ff2cd09ad9b4fea73f76f951be815278c26',
- version: 2,
- inputs: [
- {
- prevOut: {
- txid: '727a08ebbaef244d136ffb4ab8db256475db8a83cb2acbdfafa42617019d7dc7',
- outIdx: 3,
- },
- inputScript:
- '473044022037e2b4ac09f71432e97d71938dac7b5f0abcd84a8ea995708b0d58c38fcc743302207570250fe9f8b98b8f7079aafb1c53e796584efd910ed9f7e1f75f491f95e7564121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02d',
- value: 962056,
- sequenceNo: 4294967295,
- outputScript:
- '76a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac',
- },
- ],
- outputs: [
- {
- value: 0,
- outputScript: '6a04007461620b7374696c6c20776f726b73',
- },
- {
- value: 2200,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- spentBy: {
- txid: 'ba69815ef87cb48585a40968d6ff7764cbef4f021fdd015c5eb25afe75feb0a1',
- outIdx: 0,
- },
- },
- {
- value: 959379,
- outputScript:
- '76a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac',
- spentBy: {
- txid: '7eacd3b752003fe761e359cac3d98b1faf4f1dd411150eabc89da8208a312b0e',
- outIdx: 0,
- },
- },
- ],
- lockTime: 0,
- timeFirstSeen: 0,
- size: 252,
- isCoinbase: false,
- tokenEntries: [],
- tokenFailedParsings: [],
- tokenStatus: 'TOKEN_STATUS_NON_TOKEN',
- block: {
- height: 812408,
- hash: '00000000000000000b2dfec91630d335b0233fb323a7acbb297b586d1d0d0678',
- timestamp: 1696282475,
- },
- parsed: {
- incoming: false,
- xecAmount: '22',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage: 'still works',
- isCashtabMessage: true,
- isEncryptedMessage: false,
- replyAddress:
- 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- aliasFlag: false,
- },
- },
- ],
- },
-};
-
-export const walletWithXecAndTokens_pre_2_9_0 = {
- mnemonic:
- 'beauty shoe decline spend still weird slot snack coach flee between paper',
- name: 'Transaction Fixtures',
- // New paths key instead of hardcoded Path145, Path245, Path1899 keys
- paths: [
- {
- // New shape of path info
- path: 1899,
- address: 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- hash: '3a5fb236934ec078b4507c303d3afd82067f8fc1',
- wif: 'KywWPgaLDwvW1tWUtUvs13jgqaaWMoNANLVYoKcK9Ddbpnch7Cmw',
- },
- {
- path: 145,
- address: 'ecash:qz3glzzjlp503rn3a3nxccedd7rwj78sgczljhvzv3',
- hash: 'a28f8852f868f88e71ec666c632d6f86e978f046',
- wif: 'L2HnC8ZT5JuwVFjrAjJUBs2tmmBoxdVa1MVCJccqV8S9YPoR1NuZ',
- },
- {
- path: 245,
- address: 'ecash:qpsqa7cj5mup8mx0zvt34z7xyp2jztvdds67wajntk',
- hash: '600efb12a6f813eccf13171a8bc62055212d8d6c',
- wif: 'L3ndnMkn4574McqhPujguusu48NrmeLUgWYMkRpYQGLXDGAwGmPq',
- },
- ],
- state: {
- // State for wallets after 2.2.0 expected to be in-node shape
- // Note: the legacy wallet had to be adapted to this state to support setting mocks
- balanceSats: 951312,
- slpUtxos: walletWithXecAndTokens_pre_2_1_0.state.slpUtxos,
- nonSlpUtxos: walletWithXecAndTokens_pre_2_1_0.state.nonSlpUtxos,
- tokens: walletWithXecAndTokens_pre_2_1_0.state.tokens,
- parsedTxHistory: walletWithXecAndTokens_pre_2_1_0.state.parsedTxHistory,
- },
-};
-
-export const walletWithXecAndTokens = {
- ...walletWithXecAndTokens_pre_2_9_0,
- paths: new Map([
- [
- 1899,
- {
- address: 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
- hash: '3a5fb236934ec078b4507c303d3afd82067f8fc1',
- wif: 'KywWPgaLDwvW1tWUtUvs13jgqaaWMoNANLVYoKcK9Ddbpnch7Cmw',
- },
- ],
- [
- 145,
- {
- address: 'ecash:qz3glzzjlp503rn3a3nxccedd7rwj78sgczljhvzv3',
- hash: 'a28f8852f868f88e71ec666c632d6f86e978f046',
- wif: 'L2HnC8ZT5JuwVFjrAjJUBs2tmmBoxdVa1MVCJccqV8S9YPoR1NuZ',
- },
- ],
- [
- 245,
- {
- address: 'ecash:qpsqa7cj5mup8mx0zvt34z7xyp2jztvdds67wajntk',
- hash: '600efb12a6f813eccf13171a8bc62055212d8d6c',
- wif: 'L3ndnMkn4574McqhPujguusu48NrmeLUgWYMkRpYQGLXDGAwGmPq',
- },
- ],
- ]),
- state: {
- ...walletWithXecAndTokens_pre_2_9_0.state,
- tokens: new Map([
- [
- '3fee3384150b030490b7bee095a63900f66a45f2d8e3002ae2cf17ce3ef4d109',
- '1',
- ],
- ]),
- },
-};
-
-export const freshWalletWithOneIncomingCashtabMsg = {
- mnemonic: 'some words that would give it all away',
- name: '[Burned] useWallet Mock',
- paths: new Map([
- [
- 1899,
- {
- address: 'ecash:qrfjv9kglpyazkdsyf0nd9nvewzagf0xsvv84u226e',
- hash: 'd32616c8f849d159b0225f36966ccb85d425e683',
- wif: 'nope',
- },
- ],
- [
- 145,
- {
- address: 'ecash:qqdukdf3cdgn0nes83x4ln87hd0mpqvh7uky87rj0a',
- hash: '1bcb3531c35137cf303c4d5fccfebb5fb08197f7',
- wif: 'nope',
- },
- ],
- [
- 245,
- {
- address: 'ecash:qqqtqscqym24ps40v5n2wl88n9zlgu3hqyjzt84eay',
- hash: '00b0430026d550c2af6526a77ce79945f4723701',
- wif: 'nope',
- },
- ],
- ]),
- state: {
- balanceSats: 1000000,
- slpUtxos: [],
- nonSlpUtxos: [
- {
- outpoint: {
- txid: 'f11648484c5ac6bf65c04632208d60e809014ed288171cb96e059d0ed7678fde',
- outIdx: 1,
- },
- blockHeight: -1,
- isCoinbase: false,
- value: '1000000',
- network: 'XEC',
- address: 'ecash:qrfjv9kglpyazkdsyf0nd9nvewzagf0xsvv84u226e',
- },
- ],
- tokens: new Map(),
- parsedTxHistory: [
- {
- txid: 'f11648484c5ac6bf65c04632208d60e809014ed288171cb96e059d0ed7678fde',
- version: 2,
- inputs: [
- {
- prevOut: {
- txid: 'da90c08e3d4afe2ab0446a1f72a3b60cf5308c55cdb3f57a5eaefd373f42e83f',
- outIdx: 0,
- },
- inputScript:
- '483045022100d8350abb126e2ff6c841dcfb3902b175d46b59f141a23c40deeb7dcac1f219e7022072ee779da16bf15a8032093f03693ea98f2bbc6557dca7b48cf1f308ffb8173a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '012eba73fbc4d0383cda231b5b0c51f802569658844ab3700eb77d4475db4c32',
- outIdx: 0,
- },
- inputScript:
- '483045022100989f2cd7b8994a0af144a5033d6959779bd7466226901656a35aac231ceb53f602202606fa0f2de1d82abcfac3180c7b111529792243eff23fc6455a29c92532552e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '05415fd9f02813318f760daf5aac625b61a4f60f1e1797d24e409f674549b678',
- outIdx: 0,
- },
- inputScript:
- '483045022100e09950e7c9a956dd3125f741164802ef7de74a4c8c3c617f1eacdeefd9527ae502202c0cb5f8839ddfb88bf11e5f337cb0220e0228a5a663eb10b22e5567bddb2e404121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 169505,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '4585655d04f1b1da78c3bb39f90f3ab9891b930880882e2bd6ee452b58109b40',
- outIdx: 0,
- },
- inputScript:
- '47304402204d2a2e8aec45ba90295fd09661d1504a8f3f0fe2be42b450c67be34212cfacb402201a438dbee9e1da4712885558925a5d49ce600da2ec25fc55240059e65ce193494121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 5500,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '7ca455f6d19b89ae2a01159b52804d8c80c1b3f4f6f1467a71f90d2639e4dcc2',
- outIdx: 1,
- },
- inputScript:
- '473044022074585d51f69f3a1afd5df2de6c4d630b6d89861454274909373dfbf5f5a63bc0022010abb2dc05e79505dc054f87467a494c28320a2ae4338f30c10e8f645b06c3784121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'ca1083d45081872129c9e2d2f463f1537749ec9aaddcccea8fcd21c6ad78ae09',
- outIdx: 1,
- },
- inputScript:
- '47304402201bb538c5da8e8f4f5fbf5d98b4d8dc8f6870d05e93fed6eead7a535f6b59338302204e0b18a4136fe6842ee01a9439aa8e6b9a089e4116aa4381f0d0281bc853348f4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '60f70d556549933674bfafef147e3d921c3f446b31e1ac74c55b6e17067bbd8f',
- outIdx: 0,
- },
- inputScript:
- '483045022100a9c9c91b48f7fdd781cd132e36c84767541ef738e405ea04e71e2f2d4b54165f02206524b3cb82a2f738906dc91a367c994122dff98c1f187e32ec676989ab3a85874121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '5b2b00f6c5d7d3406885e047caf1dd88e9b6f56058142991c3f78fd1a9a35259',
- outIdx: 0,
- },
- inputScript:
- '473044022048e22c07d4f68235ca8e129c74fabbad7c9b5e17ce36d76d312f4fb08d1496c30220639024f26673e0b46247b419f1605ffea2a195eaa4123b2ab5e3360d926469164121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'cfd2dd5b8a43fe984782d5c548aec943425ee0d2fc09b7a262e504d77956d53b',
- outIdx: 0,
- },
- inputScript:
- '47304402200a629b21dbba359dfa5376bc0c64c984041389e2a64c248d4bfe06f3634143860220331d15a53b4313b19c9ad717c33d7f1a7fbb82768545fc225b9bef2d541685084121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 5500,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '563fc7eec5d7e3bd07ad2e41682afda2939ed983470464457492b23cc523d5cf',
- outIdx: 0,
- },
- inputScript:
- '47304402204f6ac84bfb08b9d194c9cffaa1d28cf62d4003c4f2db08c8d9dc23cd2ba2d53b0220605658e5e00499894b74aa6de304c76a6906c66a687541f33f0205403967e04a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '0a2585f1df1bc178ceac3a62158db2a1d5d136cca67690c081402c72845f9409',
- outIdx: 0,
- },
- inputScript:
- '47304402205126713ba8abf10b9ef384262dc808c23b072df9051b1bd266ead92571a32c7002201a20d649eec51d1f859bd843e3afee4bd15a52c42ae497bb161c34fef6301b174121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 600,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '2eadd98c7a0bf9edbddbdaa6423e2b8b2405f38428a35de6985aba1848bcf5f7',
- outIdx: 2,
- },
- inputScript:
- '483045022100a4f9b2e2175b0bbae6c3de2f89b2585ac6c82b63ac55e2bebb6d9b70a72daf1102204c00d2a9b6ced89eadde34980444ffb755b494082c9b11fc6d21c821ee8ba0b04121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '941ef3bd803f4267eecbdcb76ddb3116b3c781a2a97b6a9bde80b5573e70e205',
- outIdx: 1,
- },
- inputScript:
- '4730440220009194b5caf2d28216f69c0e98b1ee106779a6de485a1ba69dd5fd635d2a23e4022002cfb688783df6b4988309c55eaf31ce346b4b5db9330335f7a3acd3b6f85f514121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '94a30a43b9e1c507121456528ef25f39c51c81e3bbf204ffd08e0c4f4a460ba9',
- outIdx: 0,
- },
- inputScript:
- '483045022100c4654eada3fdde6a3a3e12c09f40ab30c587ed5daf533a550ac8adba5a3f3089022039c6d938474e81fff50f677a3444f87ef35140ce0d09e7a8ba02ee5b08c667174121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1700,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '9d30a8aa240891d966b1b16771500719e4452f1f8f60564f4276fa5305553cb5',
- outIdx: 0,
- },
- inputScript:
- '4830450221008d98777056f9c8b1bb4e3f2fefdbeca1ac9d65d738276e271334ead8f088de060220606388b42a5e7c3eb590ad840e6596b5e48cf5b418bdfaee47c7b2b87b4082af4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'f9865e9014f966e612bcc84e8c234dea9c59afbd25507934a6e2c893feff1af7',
- outIdx: 1,
- },
- inputScript:
- '4730440220468ea9ee32fe1400fffcb6c1a934c3789213e46d16747dc8746f4b9465b11e7c022003192f77afec1bcecf7ad46ddde813301b383b07177440786e9b1bae9946fb3c4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '470883dd586d4cea4ba5ce78379ef92eb5bb4fd4d59f6dd549da3d8692afb21c',
- outIdx: 0,
- },
- inputScript:
- '483045022100ed8bdeedb7decc83d0cf71212a4fe4c21cb4ec091040b27ef15ca716aeb66957022006d12aeb15fc76f15642853dde0b35ae2a5a3efe24cfa275e049fba17690a3914121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'c91999a9755e5236003f4dff60f874bd0330d3cdfdf65e22f10a00ea054289e6',
- outIdx: 0,
- },
- inputScript:
- '483045022100a940abcc3bfd26eff3b6d254750ac19361d734a42282164c432d66b62139e8c502207a28c61e435ee42dae5b1964cc457aeeb316717b51ec0cf1af0e1b4e0a5b5a474121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1000,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '2188f495eedfa0bfe96c7aedad66582900c2969c71f5c530e1ac8b55ca7ed326',
- outIdx: 0,
- },
- inputScript:
- '47304402204dd6870b836e9482a7b68decdc9447f50a2f9df4f9c4d0b6446e28878386f2c60220654b4131261d41fb5b931a89be777c2716d32b77f0b85ce662ce2cade32ac3264121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '87c3f038d5754f6feb7f117dcb2fa85a015270025e919591a303429709da2023',
- outIdx: 0,
- },
- inputScript:
- '483045022100f36c72b34d0350bf1bee5acea403981d41307e0da65a7405501c6631e24abc510220389ecf6b00d46e9af27d2e2463d74a2453d14da076ec94df83e2f63bbb430f0d4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '0e906814d55e889a97f46424497b7564cb2ce1659a03b0ac7c71d4d4b3143b75',
- outIdx: 0,
- },
- inputScript:
- '473044022070d354ca81b378bb1408102755ee19f112dc84e7c97dc44224ce0ec61cf9da5e02200e601eff33e36fc5e96aef74f28d6329201cb8670250fd09501221c67a5eeb034121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '221e9f39e8138c0755d8f5b4fe2445a3d645a98310773cd9afae46e4e9ce35f1',
- outIdx: 1,
- },
- inputScript:
- '47304402205ae9a030a48fad096f465f9a2d564d5f88a83b2207e2bcf9f96522a4eeca3927022050fb4fb6ca418b3559dde7cf0f6412f82dbcee7710de2c895b5989fdca75a8f84121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 900,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '38353c86f9765f23bbe144cdadf5edbc1f24c12212614977ef1569e60e1cfb78',
- outIdx: 0,
- },
- inputScript:
- '47304402207a47b86a75c91022a5f54f2f39d1d2952a8623b8cbdad40ca1d8d25e13f1027502204b5b021a65276886e4f31ce5dfab2eb7979595aecd2b244ca88047453f57e04e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'a29c3cc45360bbce9459d1e4b86b79ac199016b82ab3574ec380edc83d85d947',
- outIdx: 1,
- },
- inputScript:
- '483045022100bf4d11242dd28d5ec4de42388d8fafc6bcaa812a20a728f0e86e5b5e51c0005e022041b6bf9034412e9e2377ebf1ea61ef358c05be1a42829b08032bf03be28c9fa04121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 497990,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'a78d8acd9925ee2e86a6fede4fcb7bebabacbdd92285e8d195fe86a2459a1ac1',
- outIdx: 0,
- },
- inputScript:
- '47304402204f6c5b9e7a8b610bce1eb32c2362fb428137f6797a0a8f463a1043eab51c3dfe02203f5f0c7816573ca5266fb4a5fddb2d29e76d8c79cde8ab8198acaab9ac206dbf4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 700,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: 'd11677c4606a8674a5bfa0e4e6f50aeaa3ff54f3e241e02fdcedde30a08a06fd',
- outIdx: 0,
- },
- inputScript:
- '483045022100b13a13af99fcb0946431f7407b35d2ec513c2fc100c6ea5b9c787cd21513461b02207a6549f50789f5027fbef437d7cd74785fbc01f6976eee5b99a60b0515a325ab4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1100,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '10df437f64451165ac1eb371cef97aab8602d6d61c57eb97811fe724fe7371c3',
- outIdx: 0,
- },
- inputScript:
- '473044022029e166c7c52719ae6dd4b0e54102f796041c3dec3edb61a87d6cca8acb31b67302206696df121a4b71251c99c1ad9d80f4acb302d44be2ecd860e76f6241675c50264121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2500,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '1bc75da40fc86396873499114418dafec6f6541a09879c5727996fbf938feeec',
- outIdx: 0,
- },
- inputScript:
- '483045022100cd99ca822e8cceedff64c67577c4ba55ae1db654c28b9982926269a7b2e1847c022020054e903352d1079c19513d990da774ab12d98860c241c50066bc4f6b37357f4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 5500,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- },
- {
- prevOut: {
- txid: '4ff71400743a3271151352875612d150f690ac1ab6c3d062ce22785935d92444',
- outIdx: 0,
- },
- inputScript:
- '47304402200b570f4c81ef54cfc9d77d1fcd615b90063243f99aa665a53a4fa1b6204fb83802200d83088d8dc9690932d4a22e33556221ad1f44dc596b3d31cbff38b2c6a29c0a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 2200,
- sequenceNo: 4294967295,
- outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
+ },
},
+ ],
+ lockTime: 0,
+ timeFirstSeen: 0,
+ size: 267,
+ isCoinbase: false,
+ tokenEntries: [],
+ tokenFailedParsings: [],
+ tokenStatus: 'TOKEN_STATUS_NON_TOKEN',
+ block: {
+ height: 813615,
+ hash: '000000000000000006b8990c77dd7a0d3a850a052b6f0bd60b82d44d1ffa7a55',
+ timestamp: 1697025138,
+ },
+ parsed: {
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 554,
+ stackArray: [
+ '2e786563',
+ '00',
+ '616c696173',
+ '003a5fb236934ec078b4507c303d3afd82067f8fc1',
+ ],
+ recipients: [
+ 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
+ ],
+ },
+ },
+ {
+ txid: '8b3cb0e6c38ee01f9e1e98d611895ff2cd09ad9b4fea73f76f951be815278c26',
+ version: 2,
+ inputs: [
{
prevOut: {
- txid: '58e4c28a6318d677a49d2bfc0d99fcc069a13dc95881a8403f43da65f0f1ee9f',
- outIdx: 0,
+ txid: '727a08ebbaef244d136ffb4ab8db256475db8a83cb2acbdfafa42617019d7dc7',
+ outIdx: 3,
},
inputScript:
- '483045022100dc7147775fd80ccb6e75710ae2f226249d3a99017ddae7a0163900595967765f02205d84c411885d90a31b41360e8b0a8aa0a70be079c5c5a643db238a52d978835e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
- value: 1000000,
+ '473044022037e2b4ac09f71432e97d71938dac7b5f0abcd84a8ea995708b0d58c38fcc743302207570250fe9f8b98b8f7079aafb1c53e796584efd910ed9f7e1f75f491f95e7564121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02d',
+ value: 962056,
sequenceNo: 4294967295,
outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
+ '76a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac',
},
],
outputs: [
{
value: 0,
- outputScript:
- '6a04007461623a6865726520697320612043617368746162204d736720666f722075736520696e204361736874616220696e746567726174696f6e207465737473',
+ outputScript: '6a04007461620b7374696c6c20776f726b73',
},
{
- value: 1000000,
+ value: 2200,
outputScript:
- '76a914d32616c8f849d159b0225f36966ccb85d425e68388ac',
+ '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
+ spentBy: {
+ txid: 'ba69815ef87cb48585a40968d6ff7764cbef4f021fdd015c5eb25afe75feb0a1',
+ outIdx: 0,
+ },
},
{
- value: 713065,
+ value: 959379,
outputScript:
- '76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
+ '76a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac',
spentBy: {
- txid: '4ca77715698d7f5acab6fcd3703cc07d6a760b4cab064a87c03a755bc734e501',
- outIdx: 41,
+ txid: '7eacd3b752003fe761e359cac3d98b1faf4f1dd411150eabc89da8208a312b0e',
+ outIdx: 0,
},
},
],
lockTime: 0,
timeFirstSeen: 0,
- size: 4576,
+ size: 252,
isCoinbase: false,
tokenEntries: [],
tokenFailedParsings: [],
tokenStatus: 'TOKEN_STATUS_NON_TOKEN',
block: {
- height: 830308,
- hash: '000000000000000007f40dd57695bb3e592e9769bd74fbe0fe9c8c09db32da61',
- timestamp: 1707162498,
+ height: 812408,
+ hash: '00000000000000000b2dfec91630d335b0233fb323a7acbb297b586d1d0d0678',
+ timestamp: 1696282475,
+ },
+ parsed: {
+ xecTxType: XecTxType.Sent,
+ satoshisSent: 2200,
+ stackArray: ['00746162', '7374696c6c20776f726b73'],
+ recipients: [
+ 'ecash:qphlhe78677sz227k83hrh542qeehh8el5lcjwk72y',
+ ],
},
},
],
},
};
-export const freshWalletWithOneIncomingCashtabMsgTxs = [
+interface LegacyPathInfo_Pre_2_9_0 {
+ path: number;
+ address: string;
+ hash: string;
+ wif: string;
+}
+interface LegacyCashtabWalletState_Pre_2_9_0 {
+ balanceSats: number;
+ slpUtxos: SlpUtxo[];
+ nonSlpUtxos: NonSlpUtxo[];
+ tokens: LegacyTokenState[];
+ parsedTxHistory: CashtabTx[];
+}
+export interface LegacyCashtabWallet_Pre_2_9_0 {
+ mnemonic: string;
+ name: string;
+ paths: LegacyPathInfo_Pre_2_9_0[];
+ state: LegacyCashtabWalletState_Pre_2_9_0;
+}
+export const walletWithXecAndTokens_pre_2_9_0: LegacyCashtabWallet_Pre_2_9_0 = {
+ mnemonic:
+ 'beauty shoe decline spend still weird slot snack coach flee between paper',
+ name: 'Transaction Fixtures',
+ // New paths key instead of hardcoded Path145, Path245, Path1899 keys
+ paths: [
+ {
+ // New shape of path info
+ path: 1899,
+ address: 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
+ hash: '3a5fb236934ec078b4507c303d3afd82067f8fc1',
+ wif: 'KywWPgaLDwvW1tWUtUvs13jgqaaWMoNANLVYoKcK9Ddbpnch7Cmw',
+ },
+ {
+ path: 145,
+ address: 'ecash:qz3glzzjlp503rn3a3nxccedd7rwj78sgczljhvzv3',
+ hash: 'a28f8852f868f88e71ec666c632d6f86e978f046',
+ wif: 'L2HnC8ZT5JuwVFjrAjJUBs2tmmBoxdVa1MVCJccqV8S9YPoR1NuZ',
+ },
+ {
+ path: 245,
+ address: 'ecash:qpsqa7cj5mup8mx0zvt34z7xyp2jztvdds67wajntk',
+ hash: '600efb12a6f813eccf13171a8bc62055212d8d6c',
+ wif: 'L3ndnMkn4574McqhPujguusu48NrmeLUgWYMkRpYQGLXDGAwGmPq',
+ },
+ ],
+ state: {
+ // State for wallets after 2.2.0 expected to be in-node shape
+ // Note: the legacy wallet had to be adapted to this state to support setting mocks
+ balanceSats: 951312,
+ slpUtxos: [
+ {
+ ...walletWithXecAndTokens_pre_2_1_0.state.slpUtxos[0],
+ path: 1899,
+ } as SlpUtxo,
+ ],
+ nonSlpUtxos: [
+ {
+ ...walletWithXecAndTokens_pre_2_1_0.state.nonSlpUtxos[0],
+ path: 1899,
+ },
+ ],
+ tokens: walletWithXecAndTokens_pre_2_1_0.state.tokens,
+ parsedTxHistory: walletWithXecAndTokens_pre_2_1_0.state.parsedTxHistory,
+ },
+};
+
+export const walletWithXecAndTokens: CashtabWallet = {
+ ...walletWithXecAndTokens_pre_2_9_0,
+ paths: new Map([
+ [
+ 1899,
+ {
+ address: 'ecash:qqa9lv3kjd8vq7952p7rq0f6lkpqvlu0cydvxtd70g',
+ hash: '3a5fb236934ec078b4507c303d3afd82067f8fc1',
+ wif: 'KywWPgaLDwvW1tWUtUvs13jgqaaWMoNANLVYoKcK9Ddbpnch7Cmw',
+ },
+ ],
+ [
+ 145,
+ {
+ address: 'ecash:qz3glzzjlp503rn3a3nxccedd7rwj78sgczljhvzv3',
+ hash: 'a28f8852f868f88e71ec666c632d6f86e978f046',
+ wif: 'L2HnC8ZT5JuwVFjrAjJUBs2tmmBoxdVa1MVCJccqV8S9YPoR1NuZ',
+ },
+ ],
+ [
+ 245,
+ {
+ address: 'ecash:qpsqa7cj5mup8mx0zvt34z7xyp2jztvdds67wajntk',
+ hash: '600efb12a6f813eccf13171a8bc62055212d8d6c',
+ wif: 'L3ndnMkn4574McqhPujguusu48NrmeLUgWYMkRpYQGLXDGAwGmPq',
+ },
+ ],
+ ]),
+ state: {
+ ...walletWithXecAndTokens_pre_2_9_0.state,
+ tokens: new Map([
+ [
+ '3fee3384150b030490b7bee095a63900f66a45f2d8e3002ae2cf17ce3ef4d109',
+ '1',
+ ],
+ ]),
+ },
+};
+
+export const freshWalletWithOneIncomingCashtabMsgTxs: CashtabTx[] = [
{
txid: 'f11648484c5ac6bf65c04632208d60e809014ed288171cb96e059d0ed7678fde',
version: 2,
@@ -1412,10 +1007,10 @@
},
inputScript:
'483045022100d8350abb126e2ff6c841dcfb3902b175d46b59f141a23c40deeb7dcac1f219e7022072ee779da16bf15a8032093f03693ea98f2bbc6557dca7b48cf1f308ffb8173a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1424,10 +1019,10 @@
},
inputScript:
'483045022100989f2cd7b8994a0af144a5033d6959779bd7466226901656a35aac231ceb53f602202606fa0f2de1d82abcfac3180c7b111529792243eff23fc6455a29c92532552e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1436,10 +1031,10 @@
},
inputScript:
'483045022100e09950e7c9a956dd3125f741164802ef7de74a4c8c3c617f1eacdeefd9527ae502202c0cb5f8839ddfb88bf11e5f337cb0220e0228a5a663eb10b22e5567bddb2e404121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 169505,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '169505',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1448,10 +1043,10 @@
},
inputScript:
'47304402204d2a2e8aec45ba90295fd09661d1504a8f3f0fe2be42b450c67be34212cfacb402201a438dbee9e1da4712885558925a5d49ce600da2ec25fc55240059e65ce193494121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 5500,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '5500',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1460,10 +1055,10 @@
},
inputScript:
'473044022074585d51f69f3a1afd5df2de6c4d630b6d89861454274909373dfbf5f5a63bc0022010abb2dc05e79505dc054f87467a494c28320a2ae4338f30c10e8f645b06c3784121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1472,10 +1067,10 @@
},
inputScript:
'47304402201bb538c5da8e8f4f5fbf5d98b4d8dc8f6870d05e93fed6eead7a535f6b59338302204e0b18a4136fe6842ee01a9439aa8e6b9a089e4116aa4381f0d0281bc853348f4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1484,10 +1079,10 @@
},
inputScript:
'483045022100a9c9c91b48f7fdd781cd132e36c84767541ef738e405ea04e71e2f2d4b54165f02206524b3cb82a2f738906dc91a367c994122dff98c1f187e32ec676989ab3a85874121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1496,10 +1091,10 @@
},
inputScript:
'473044022048e22c07d4f68235ca8e129c74fabbad7c9b5e17ce36d76d312f4fb08d1496c30220639024f26673e0b46247b419f1605ffea2a195eaa4123b2ab5e3360d926469164121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1508,10 +1103,10 @@
},
inputScript:
'47304402200a629b21dbba359dfa5376bc0c64c984041389e2a64c248d4bfe06f3634143860220331d15a53b4313b19c9ad717c33d7f1a7fbb82768545fc225b9bef2d541685084121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 5500,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '5500',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1520,10 +1115,10 @@
},
inputScript:
'47304402204f6ac84bfb08b9d194c9cffaa1d28cf62d4003c4f2db08c8d9dc23cd2ba2d53b0220605658e5e00499894b74aa6de304c76a6906c66a687541f33f0205403967e04a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1532,10 +1127,10 @@
},
inputScript:
'47304402205126713ba8abf10b9ef384262dc808c23b072df9051b1bd266ead92571a32c7002201a20d649eec51d1f859bd843e3afee4bd15a52c42ae497bb161c34fef6301b174121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 600,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '600',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1544,10 +1139,10 @@
},
inputScript:
'483045022100a4f9b2e2175b0bbae6c3de2f89b2585ac6c82b63ac55e2bebb6d9b70a72daf1102204c00d2a9b6ced89eadde34980444ffb755b494082c9b11fc6d21c821ee8ba0b04121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1556,10 +1151,10 @@
},
inputScript:
'4730440220009194b5caf2d28216f69c0e98b1ee106779a6de485a1ba69dd5fd635d2a23e4022002cfb688783df6b4988309c55eaf31ce346b4b5db9330335f7a3acd3b6f85f514121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1568,10 +1163,10 @@
},
inputScript:
'483045022100c4654eada3fdde6a3a3e12c09f40ab30c587ed5daf533a550ac8adba5a3f3089022039c6d938474e81fff50f677a3444f87ef35140ce0d09e7a8ba02ee5b08c667174121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1700,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1700',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1580,10 +1175,10 @@
},
inputScript:
'4830450221008d98777056f9c8b1bb4e3f2fefdbeca1ac9d65d738276e271334ead8f088de060220606388b42a5e7c3eb590ad840e6596b5e48cf5b418bdfaee47c7b2b87b4082af4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1592,10 +1187,10 @@
},
inputScript:
'4730440220468ea9ee32fe1400fffcb6c1a934c3789213e46d16747dc8746f4b9465b11e7c022003192f77afec1bcecf7ad46ddde813301b383b07177440786e9b1bae9946fb3c4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1604,10 +1199,10 @@
},
inputScript:
'483045022100ed8bdeedb7decc83d0cf71212a4fe4c21cb4ec091040b27ef15ca716aeb66957022006d12aeb15fc76f15642853dde0b35ae2a5a3efe24cfa275e049fba17690a3914121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1616,10 +1211,10 @@
},
inputScript:
'483045022100a940abcc3bfd26eff3b6d254750ac19361d734a42282164c432d66b62139e8c502207a28c61e435ee42dae5b1964cc457aeeb316717b51ec0cf1af0e1b4e0a5b5a474121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1000,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1000',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1628,10 +1223,10 @@
},
inputScript:
'47304402204dd6870b836e9482a7b68decdc9447f50a2f9df4f9c4d0b6446e28878386f2c60220654b4131261d41fb5b931a89be777c2716d32b77f0b85ce662ce2cade32ac3264121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1640,10 +1235,10 @@
},
inputScript:
'483045022100f36c72b34d0350bf1bee5acea403981d41307e0da65a7405501c6631e24abc510220389ecf6b00d46e9af27d2e2463d74a2453d14da076ec94df83e2f63bbb430f0d4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1652,10 +1247,10 @@
},
inputScript:
'473044022070d354ca81b378bb1408102755ee19f112dc84e7c97dc44224ce0ec61cf9da5e02200e601eff33e36fc5e96aef74f28d6329201cb8670250fd09501221c67a5eeb034121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1664,10 +1259,10 @@
},
inputScript:
'47304402205ae9a030a48fad096f465f9a2d564d5f88a83b2207e2bcf9f96522a4eeca3927022050fb4fb6ca418b3559dde7cf0f6412f82dbcee7710de2c895b5989fdca75a8f84121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 900,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '900',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1676,10 +1271,10 @@
},
inputScript:
'47304402207a47b86a75c91022a5f54f2f39d1d2952a8623b8cbdad40ca1d8d25e13f1027502204b5b021a65276886e4f31ce5dfab2eb7979595aecd2b244ca88047453f57e04e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1688,10 +1283,10 @@
},
inputScript:
'483045022100bf4d11242dd28d5ec4de42388d8fafc6bcaa812a20a728f0e86e5b5e51c0005e022041b6bf9034412e9e2377ebf1ea61ef358c05be1a42829b08032bf03be28c9fa04121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 497990,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '497990',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1700,10 +1295,10 @@
},
inputScript:
'47304402204f6c5b9e7a8b610bce1eb32c2362fb428137f6797a0a8f463a1043eab51c3dfe02203f5f0c7816573ca5266fb4a5fddb2d29e76d8c79cde8ab8198acaab9ac206dbf4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 700,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '700',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1712,10 +1307,10 @@
},
inputScript:
'483045022100b13a13af99fcb0946431f7407b35d2ec513c2fc100c6ea5b9c787cd21513461b02207a6549f50789f5027fbef437d7cd74785fbc01f6976eee5b99a60b0515a325ab4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1100,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1100',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1724,10 +1319,10 @@
},
inputScript:
'473044022029e166c7c52719ae6dd4b0e54102f796041c3dec3edb61a87d6cca8acb31b67302206696df121a4b71251c99c1ad9d80f4acb302d44be2ecd860e76f6241675c50264121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2500,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2500',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1736,10 +1331,10 @@
},
inputScript:
'483045022100cd99ca822e8cceedff64c67577c4ba55ae1db654c28b9982926269a7b2e1847c022020054e903352d1079c19513d990da774ab12d98860c241c50066bc4f6b37357f4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 5500,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '5500',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1748,10 +1343,10 @@
},
inputScript:
'47304402200b570f4c81ef54cfc9d77d1fcd615b90063243f99aa665a53a4fa1b6204fb83802200d83088d8dc9690932d4a22e33556221ad1f44dc596b3d31cbff38b2c6a29c0a4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 2200,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '2200',
- sequenceNo: 4294967295,
},
{
prevOut: {
@@ -1760,51 +1355,108 @@
},
inputScript:
'483045022100dc7147775fd80ccb6e75710ae2f226249d3a99017ddae7a0163900595967765f02205d84c411885d90a31b41360e8b0a8aa0a70be079c5c5a643db238a52d978835e4121024781b5971a20049fa211c364a868d2fa8f258c31bb3738e01957400067eeee0f',
+ value: 1000000,
+ sequenceNo: 4294967295,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
- value: '1000000',
- sequenceNo: 4294967295,
},
],
outputs: [
{
- value: '0',
+ value: 0,
outputScript:
'6a04007461623a6865726520697320612043617368746162204d736720666f722075736520696e204361736874616220696e746567726174696f6e207465737473',
},
{
- value: '1000000',
+ value: 1000000,
outputScript:
'76a914d32616c8f849d159b0225f36966ccb85d425e68388ac',
},
{
- value: '713065',
+ value: 713065,
outputScript:
'76a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88ac',
+ spentBy: {
+ txid: '4ca77715698d7f5acab6fcd3703cc07d6a760b4cab064a87c03a755bc734e501',
+ outIdx: 41,
+ },
},
],
lockTime: 0,
- timeFirstSeen: '1707161386',
+ timeFirstSeen: 0,
size: 4576,
isCoinbase: false,
- network: 'XEC',
+ tokenEntries: [],
+ tokenFailedParsings: [],
+ tokenStatus: 'TOKEN_STATUS_NON_TOKEN',
+ block: {
+ height: 830308,
+ hash: '000000000000000007f40dd57695bb3e592e9769bd74fbe0fe9c8c09db32da61',
+ timestamp: 1707162498,
+ },
parsed: {
- incoming: true,
- xecAmount: '10000',
- isEtokenTx: false,
- airdropFlag: false,
- airdropTokenId: '',
- opReturnMessage:
- 'here is a Cashtab Msg for use in Cashtab integration tests',
- isCashtabMessage: true,
- isEncryptedMessage: false,
- replyAddress: 'ecash:qphlhe78677sz227k83hrh542qeehh8el5lcjwk72y',
- aliasFlag: false,
+ xecTxType: XecTxType.Received,
+ satoshisSent: 1000000,
+ stackArray: [
+ '00746162',
+ '6865726520697320612043617368746162204d736720666f722075736520696e204361736874616220696e746567726174696f6e207465737473',
+ ],
+ recipients: ['ecash:qphlhe78677sz227k83hrh542qeehh8el5lcjwk72y'],
},
},
];
-export const requiredUtxoThisToken = {
+export const freshWalletWithOneIncomingCashtabMsg: CashtabWallet = {
+ mnemonic: 'some words that would give it all away',
+ name: '[Burned] useWallet Mock',
+ paths: new Map([
+ [
+ 1899,
+ {
+ address: 'ecash:qrfjv9kglpyazkdsyf0nd9nvewzagf0xsvv84u226e',
+ hash: 'd32616c8f849d159b0225f36966ccb85d425e683',
+ wif: 'nope',
+ },
+ ],
+ [
+ 145,
+ {
+ address: 'ecash:qqdukdf3cdgn0nes83x4ln87hd0mpqvh7uky87rj0a',
+ hash: '1bcb3531c35137cf303c4d5fccfebb5fb08197f7',
+ wif: 'nope',
+ },
+ ],
+ [
+ 245,
+ {
+ address: 'ecash:qqqtqscqym24ps40v5n2wl88n9zlgu3hqyjzt84eay',
+ hash: '00b0430026d550c2af6526a77ce79945f4723701',
+ wif: 'nope',
+ },
+ ],
+ ]),
+ state: {
+ balanceSats: 1000000,
+ slpUtxos: [],
+ nonSlpUtxos: [
+ {
+ outpoint: {
+ txid: 'f11648484c5ac6bf65c04632208d60e809014ed288171cb96e059d0ed7678fde',
+ outIdx: 1,
+ },
+ blockHeight: -1,
+ isCoinbase: false,
+ value: 1000000,
+ isFinal: false,
+ path: 1899,
+ },
+ ],
+ tokens: new Map(),
+ parsedTxHistory: freshWalletWithOneIncomingCashtabMsgTxs,
+ },
+};
+
+export const requiredUtxoThisToken: ScriptUtxo = {
outpoint: {
txid: '423e24bf0715cfb80727e5e7a6ff7b9e37cb2f555c537ab06fdc7fd9b3a0ba3a',
outIdx: 1,
@@ -1812,7 +1464,6 @@
blockHeight: 833612,
isCoinbase: false,
value: 546,
- address: 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035',
isFinal: true,
token: {
tokenId:
@@ -2390,31 +2041,41 @@
// Replace state.balances with state.balanceSats
migratedWallet.state.balanceSats =
'totalBalanceInSatoshis' in unmigratedWallet.state.balances
- ? parseInt(unmigratedWallet.state.balances.totalBalanceInSatoshis)
+ ? parseInt(
+ (unmigratedWallet as unknown as LegacyCashtabWallet_Pre_2_1_0)
+ .state.balances.totalBalanceInSatoshis,
+ )
: 0;
validSavedWalletsBuilder_pre_2_9_0.push(migratedWallet);
}
-export const validSavedWallets_pre_2_9_0 = validSavedWalletsBuilder_pre_2_9_0;
+export const validSavedWallets_pre_2_9_0: LegacyCashtabWallet_Pre_2_9_0[] =
+ validSavedWalletsBuilder_pre_2_9_0;
const validSavedWalletsBuilder = [];
for (const unmigratedWallet of validSavedWalletsBuilder_pre_2_9_0) {
// Clone legacy wallet
- const migratedWallet = { ...unmigratedWallet };
+ const migratedWallet: LegacyCashtabWallet_Pre_2_9_0 | CashtabWallet = {
+ ...unmigratedWallet,
+ };
+
// Build new paths array
migratedWallet.paths = new Map([
[
1899,
{
hash: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 1899,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 1899,
).hash,
address: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 1899,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 1899,
).address,
wif: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 1899,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 1899,
).wif,
},
],
@@ -2422,13 +2083,16 @@
145,
{
hash: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 145,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 145,
).hash,
address: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 145,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 145,
).address,
wif: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 145,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 145,
).wif,
},
],
@@ -2436,13 +2100,16 @@
245,
{
hash: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 245,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 245,
).hash,
address: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 245,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 245,
).address,
wif: unmigratedWallet.paths.find(
- pathInfo => pathInfo.path === 245,
+ (pathInfo: LegacyPathInfo_Pre_2_9_0) =>
+ pathInfo.path === 245,
).wif,
},
],
diff --git a/cashtab/src/config/CashtabSettings.ts b/cashtab/src/config/CashtabSettings.ts
--- a/cashtab/src/config/CashtabSettings.ts
+++ b/cashtab/src/config/CashtabSettings.ts
@@ -30,8 +30,14 @@
}
export default CashtabSettings;
+interface FiatCurrency {
+ name: string;
+ symbol: string;
+ slug: string;
+}
+
// Cashtab supported fiat currencies
-export const supportedFiatCurrencies = {
+export const supportedFiatCurrencies: Record<string, FiatCurrency> = {
usd: { name: 'US Dollar', symbol: '$', slug: 'usd' },
aed: { name: 'UAE Dirham', symbol: 'Dh', slug: 'aed' },
aud: { name: 'Australian Dollar', symbol: '$', slug: 'aud' },
diff --git a/cashtab/src/config/CashtabState.js b/cashtab/src/config/CashtabState.ts
rename from cashtab/src/config/CashtabState.js
rename to cashtab/src/config/CashtabState.ts
--- a/cashtab/src/config/CashtabState.js
+++ b/cashtab/src/config/CashtabState.ts
@@ -10,10 +10,23 @@
import CashtabSettings from 'config/CashtabSettings';
import CashtabCache from 'config/CashtabCache';
+import { CashtabWallet } from 'wallet';
-class CashtabState {
+export interface CashtabContact {
+ name: string;
+ address: string;
+}
+
+interface CashtabState {
+ contactList: CashtabContact[];
+ cashtabCache: CashtabCache;
+ settings: CashtabSettings;
+ wallets: CashtabWallet[];
+}
+
+class CashtabState implements CashtabState {
constructor(
- contactList = [],
+ contactList: CashtabContact[] = [],
cashtabCache = new CashtabCache(),
settings = new CashtabSettings(),
wallets = [],
diff --git a/cashtab/src/helpers/index.js b/cashtab/src/helpers/index.js
deleted file mode 100644
--- a/cashtab/src/helpers/index.js
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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.
-
-import appConfig from 'config/app';
-
-/**
- * Call in a web browser. Return true if browser is on a mobile device.
- * Return false if browser is desktop or browser is too old to support navigator.userAgentData
- * @param {object | undefined} navigator
- * @returns {boolean}
- */
-export const isMobile = navigator => {
- return (
- typeof navigator?.userAgentData?.mobile !== 'undefined' &&
- navigator.userAgentData.mobile === true
- );
-};
-
-/**
- * Call in a web browser. Return user locale if available or default (e.g. 'en-US') if not.
- * @param {object | undefined} navigator
- * @returns {string}
- */
-export const getUserLocale = navigator => {
- if (typeof navigator?.language !== 'undefined') {
- return navigator.language;
- }
- return appConfig.defaultLocale;
-};
-
-/**
- * Convert cashtabCache to a JSON for storage
- * @param {object} cashtabCache {tokens: <Map of genesis info by tokenId>}
- * @returns {JSON}
- */
-export const cashtabCacheToJSON = cashtabCache => {
- return {
- ...cashtabCache,
- tokens: Array.from(cashtabCache.tokens.entries()),
- };
-};
-
-/**
- * Convert stored cashtabCache from JSON to more useful data types
- * For now, this means converting the one key 'tokens' from a keyvalue array to a Map
- * @param {JSON} storedCashtabCache
- * @returns {Map}
- */
-export const storedCashtabCacheToMap = storedCashtabCache => {
- return {
- ...storedCashtabCache,
- tokens: new Map(storedCashtabCache.tokens),
- };
-};
-
-/**
- * Convert stored cashtabWallet, which includes Map type stored as keyvalue array to some keys
- * to include actual Maps at these keys
- * @param {JSON} storedCashtabWallet
- * @returns {object}
- */
-export const cashtabWalletFromJSON = storedCashtabWallet => {
- // If you are pulling a pre-2.9.0 wallet out of storage, no conversion necessary
- // Cashtab will find this wallet invalid and migrate it
- // But, you need to ber able to handle pulling old wallets from storage
- if (
- 'Path1899' in storedCashtabWallet ||
- // Pre 2.9.0 wallet
- (Array.isArray(storedCashtabWallet.paths) &&
- storedCashtabWallet.paths.length > 0 &&
- typeof storedCashtabWallet.paths[0].path !== 'undefined')
- ) {
- return storedCashtabWallet;
- }
- return {
- ...storedCashtabWallet,
- paths: new Map(storedCashtabWallet.paths),
- state: {
- ...storedCashtabWallet.state,
- tokens: new Map(storedCashtabWallet.state.tokens),
- },
- };
-};
-
-/**
- * Store Map objects as keyvalue arrays before saving in localforage
- * @param {object} cashtabWallet
- * @returns {JSON}
- */
-export const cashtabWalletToJSON = cashtabWallet => {
- if (!(cashtabWallet.paths instanceof Map)) {
- // Cashtab wallets before 2.9.0 were already JSON
- // We do not plan to ever use this function on such a wallet
- // Handle so we can be sure no errors are thrown
- return cashtabWallet;
- }
- return {
- ...cashtabWallet,
- paths: Array.from(cashtabWallet.paths.entries()),
- state: {
- ...cashtabWallet.state,
- tokens: Array.from(cashtabWallet.state.tokens.entries()),
- },
- };
-};
-
-/**
- * Convert cashtab wallets to JSON for localforage writing
- * @param {array} wallets array of valid cashtab wallets
- * @returns {array} jsonWallets
- */
-export const cashtabWalletsToJSON = wallets => {
- const jsonWallets = [];
- for (const wallet of wallets) {
- jsonWallets.push(cashtabWalletToJSON(wallet));
- }
- return jsonWallets;
-};
-
-/**
- * Convert cashtab wallets from JSON after reading from localforage
- * @param {array} storedWallets array of stored JSON cashtab wallets
- * @returns {array} wallets
- */
-export const cashtabWalletsFromJSON = storedWallets => {
- const wallets = [];
- for (const storedWallet of storedWallets) {
- wallets.push(cashtabWalletFromJSON(storedWallet));
- }
- return wallets;
-};
-
-/**
- * Get the width of a given string of text
- * Useful to customize the width of a component according to the size of displayed text
- *
- * Note
- * It is not practical to unit test this function as document is an html document
- * @param {html} document document global of a rendered web page
- * @param {string} text text to get width of
- * @param {string} font e.g. '16px Poppins'
- * @returns {number} pixel width of text
- */
-export const getTextWidth = (document, text, font) => {
- try {
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- context.font = font || getComputedStyle(document.body).font;
- return context.measureText(text).width;
- } catch (err) {
- // If we do not have access to HTML methods, e.g. if we are in the test environment
- // Return a hard-coded width
- return 200;
- }
-};
diff --git a/cashtab/src/helpers/index.ts b/cashtab/src/helpers/index.ts
new file mode 100644
--- /dev/null
+++ b/cashtab/src/helpers/index.ts
@@ -0,0 +1,223 @@
+// 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.
+
+import appConfig from 'config/app';
+import CashtabCache, { CashtabCachedTokenInfo } from 'config/CashtabCache';
+import {
+ CashtabWallet,
+ StoredCashtabState,
+ CashtabPathInfo,
+ LegacyCashtabWallet,
+ LegacyPathInfo,
+ CashtabWalletState,
+} from 'wallet';
+import {
+ LegacyCashtabWallet_Pre_2_1_0,
+ LegacyCashtabWallet_Pre_2_9_0,
+} from 'components/App/fixtures/mocks';
+
+/**
+ * the userAgentData key is supported by recent
+ * versions of Chromium-based browsers
+ */
+interface ExtendedNavigator extends Navigator {
+ userAgentData?: NavigatorUAData;
+}
+interface NavigatorUAData {
+ readonly brands: NavigatorUABrandVersion[];
+ readonly mobile: boolean;
+ readonly platform: string;
+ getHighEntropyValues(hints: string[]): Promise<Record<string, string>>;
+}
+
+interface NavigatorUABrandVersion {
+ readonly brand: string;
+ readonly version: string;
+}
+
+/**
+ * Call in a web browser. Return true if browser is on a mobile device.
+ * Return false if browser is desktop or browser is too old to support navigator.userAgentData
+ * @param navigator
+ */
+export const isMobile = (navigator: ExtendedNavigator): boolean => {
+ return (
+ typeof navigator?.userAgentData?.mobile !== 'undefined' &&
+ navigator.userAgentData.mobile === true
+ );
+};
+
+/**
+ * Call in a web browser. Return user locale if available or default (e.g. 'en-US') if not.
+ * @param navigator
+ * @returns
+ */
+export const getUserLocale = (navigator?: ExtendedNavigator): string => {
+ if (typeof navigator?.language !== 'undefined') {
+ return navigator.language;
+ }
+ return appConfig.defaultLocale;
+};
+
+export interface CashtabCacheJson extends Omit<CashtabCache, 'tokens'> {
+ tokens: [string, CashtabCachedTokenInfo][];
+}
+/**
+ * Convert cashtabCache to a JSON for storage
+ * @param cashtabCache {tokens: <Map of genesis info by tokenId>}
+ */
+export const cashtabCacheToJSON = (
+ cashtabCache: CashtabCache,
+): CashtabCacheJson => {
+ return {
+ ...cashtabCache,
+ tokens: Array.from(cashtabCache.tokens.entries()),
+ };
+};
+
+/**
+ * Convert stored cashtabCache from JSON to more useful data types
+ * For now, this means converting the one key 'tokens' from a keyvalue array to a Map
+ * @param storedCashtabCache
+ */
+export const storedCashtabCacheToMap = (
+ storedCashtabCache: CashtabCacheJson,
+): CashtabCache => {
+ return {
+ ...storedCashtabCache,
+ tokens: new Map(storedCashtabCache.tokens),
+ };
+};
+
+export interface StoredCashtabWallet {
+ name: string;
+ mnemonic: string;
+ paths: [number, CashtabPathInfo][];
+ state: StoredCashtabState;
+}
+
+/**
+ * Convert stored cashtabWallet, which includes Map type stored as keyvalue array to some keys
+ * to include actual Maps at these keys
+ * @param storedCashtabWallet
+ * @returns
+ */
+export const cashtabWalletFromJSON = (
+ storedCashtabWallet: StoredCashtabWallet | LegacyCashtabWallet,
+): CashtabWallet | LegacyCashtabWallet => {
+ // If you are pulling a pre-2.9.0 wallet out of storage, no conversion necessary
+ // Cashtab will find this wallet invalid and migrate it
+ // But, you need to ber able to handle pulling old wallets from storage
+ if (
+ 'Path1899' in storedCashtabWallet ||
+ // Pre 2.9.0 wallet
+ (Array.isArray(storedCashtabWallet.paths) &&
+ storedCashtabWallet.paths.length > 0 &&
+ typeof (storedCashtabWallet.paths[0] as LegacyPathInfo).path !==
+ 'undefined')
+ ) {
+ return storedCashtabWallet as LegacyCashtabWallet;
+ }
+ return {
+ ...(storedCashtabWallet as StoredCashtabWallet),
+ paths: new Map(
+ storedCashtabWallet.paths as [number, CashtabPathInfo][],
+ ),
+ state: {
+ ...storedCashtabWallet.state,
+ tokens: new Map(
+ (storedCashtabWallet as StoredCashtabWallet).state.tokens,
+ ),
+ },
+ } as CashtabWallet;
+};
+
+/**
+ * Store Map objects as keyvalue arrays before saving in localforage
+ * @param cashtabWallet
+ */
+export const cashtabWalletToJSON = (
+ cashtabWallet: LegacyCashtabWallet | CashtabWallet,
+): StoredCashtabWallet | LegacyCashtabWallet => {
+ if (
+ typeof (cashtabWallet as unknown as CashtabWallet).paths ===
+ 'undefined' ||
+ !((cashtabWallet as unknown as CashtabWallet).paths instanceof Map)
+ ) {
+ // Cashtab wallets before 2.9.0 were already JSON
+ // We do not plan to ever use this function on such a wallet
+ // Handle so we can be sure no errors are thrown
+ return cashtabWallet as LegacyCashtabWallet;
+ }
+ return {
+ ...(cashtabWallet as CashtabWallet),
+ paths: Array.from((cashtabWallet as CashtabWallet).paths.entries()),
+ state: {
+ ...(cashtabWallet.state as CashtabWalletState),
+ tokens: Array.from(
+ (cashtabWallet as CashtabWallet).state.tokens.entries(),
+ ),
+ },
+ } as StoredCashtabWallet;
+};
+
+/**
+ * Convert cashtab wallets to JSON for localforage writing
+ * @param wallets array of valid cashtab wallets
+ */
+export const cashtabWalletsToJSON = (
+ wallets: CashtabWallet[] | LegacyCashtabWallet[],
+): (StoredCashtabWallet | LegacyCashtabWallet)[] => {
+ const jsonWallets = [];
+ for (const wallet of wallets) {
+ jsonWallets.push(cashtabWalletToJSON(wallet));
+ }
+ return jsonWallets;
+};
+
+/**
+ * Convert cashtab wallets from JSON after reading from localforage
+ * @param {array} storedWallets array of stored JSON cashtab wallets
+ * @returns {array} wallets
+ */
+export const cashtabWalletsFromJSON = (
+ storedWallets: (StoredCashtabWallet | LegacyCashtabWallet)[],
+): (LegacyCashtabWallet | CashtabWallet)[] => {
+ const wallets = [];
+ for (const storedWallet of storedWallets) {
+ wallets.push(cashtabWalletFromJSON(storedWallet));
+ }
+ return wallets;
+};
+
+/**
+ * Get the width of a given string of text
+ * Useful to customize the width of a component according to the size of displayed text
+ *
+ * Note
+ * It is not practical to unit test this function as document is an html document
+ * @param document document global of a rendered web page
+ * @param text text to get width of
+ * @param font e.g. '16px Poppins'
+ * @returns pixel width of text
+ */
+export const getTextWidth = (
+ document: Document,
+ text: string,
+ font: string,
+): number => {
+ try {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ if (context !== null) {
+ context.font = font || getComputedStyle(document.body).font;
+ return context.measureText(text).width;
+ }
+ return 200;
+ } catch (err) {
+ // If we do not have access to HTML methods, e.g. if we are in the test environment
+ // Return a hard-coded width
+ return 200;
+ }
+};
diff --git a/cashtab/src/wallet/__tests__/useWallet.test.js b/cashtab/src/wallet/__tests__/useWallet.test.js
--- a/cashtab/src/wallet/__tests__/useWallet.test.js
+++ b/cashtab/src/wallet/__tests__/useWallet.test.js
@@ -590,7 +590,8 @@
when(fetch)
.calledWith(fetchUrl)
.mockResolvedValue({
- json: () => Promise.resolve('not a valid prices response'),
+ json: () =>
+ Promise.resolve({ error: 'not a valid prices response' }),
});
// Wait for the wallet to load
@@ -611,7 +612,7 @@
// Verify the `aliasServerError` state var in useWallet is updated
expect(result.current.aliasServerError).toStrictEqual(
- new Error(expectedError),
+ `Error: ${expectedError}`,
);
});
diff --git a/cashtab/src/wallet/index.ts b/cashtab/src/wallet/index.ts
--- a/cashtab/src/wallet/index.ts
+++ b/cashtab/src/wallet/index.ts
@@ -11,14 +11,31 @@
import { fromHex, Script, P2PKHSignatory, ALL_BIP143 } from 'ecash-lib';
import { OutPoint, Token, Tx } from 'chronik-client';
import { AgoraOffer } from 'ecash-agora';
+import { ParsedTx } from 'chronik';
+import {
+ LegacyCashtabWallet_Pre_2_1_0,
+ LegacyCashtabWallet_Pre_2_9_0,
+} from 'components/App/fixtures/mocks';
export type SlpDecimals = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
-interface CashtabPathInfo {
+export interface CashtabPathInfo {
address: string;
hash: string;
wif: string;
+ /**
+ * Public key as a hex string
+ * Introduced in 2.54.0
+ * Cashtab migrates legacy wallets to this
+ */
+ pk?: string;
+ /**
+ * Private key as a hex string
+ * Introduced in 2.54.0
+ * Cashtab migrates legacy wallets to this
+ */
+ sk?: string;
}
-interface NonSlpUtxo {
+export interface NonSlpUtxo {
blockHeight: number;
isCoinbase: boolean;
isFinal: boolean;
@@ -32,21 +49,14 @@
export interface CashtabUtxo extends NonSlpUtxo {
token?: Token;
}
-interface CashtabWalletState {
+export interface CashtabWalletState {
balanceSats: number;
nonSlpUtxos: NonSlpUtxo[];
slpUtxos: SlpUtxo[];
- parsedTxHistory: CashtabParsedTx[];
+ parsedTxHistory: CashtabTx[];
tokens: Map<string, string>;
}
-interface ParsedTx {
- recipients: string[];
- satoshisSent: number;
- stackArray: string[];
- xecTxType: string;
- size: number;
-}
-interface CashtabParsedTx extends Tx {
+export interface CashtabTx extends Tx {
parsed: ParsedTx;
}
export interface CashtabWallet {
@@ -290,12 +300,34 @@
return Math.floor((Number(sendAmountFiat) / fiatPrice) * SATOSHIS_PER_XEC);
};
+/**
+ * A user who has no opened Cashtab in some time may have a legacy wallet
+ * The wallet shape has changed a few times
+ * Cashtab is designed so that a user starting up the app with legacy wallet(s)
+ * in storage will have them all migrated on startup
+ * So, if cashtabWalletFromJSON is called with a legacy wallet, it returns the
+ * wallet as-is so it can be invalidated and recreated
+ */
+export interface LegacyPathInfo extends CashtabPathInfo {
+ path: number;
+}
+
+export interface StoredCashtabState extends Omit<CashtabWalletState, 'tokens'> {
+ tokens: [string, string][];
+}
+
+export type LegacyCashtabWallet =
+ | LegacyCashtabWallet_Pre_2_1_0
+ | LegacyCashtabWallet_Pre_2_9_0;
+
/**
* Determine if a legacy wallet includes legacy paths that must be migrated
* @param wallet a cashtab wallet
* @returns array of legacy paths
*/
-export const getLegacyPaths = (wallet: CashtabWallet): number[] => {
+export const getLegacyPaths = (
+ wallet: LegacyCashtabWallet | CashtabWallet,
+): number[] => {
const legacyPaths = [];
if ('paths' in wallet) {
if (Array.isArray(wallet.paths)) {
@@ -308,7 +340,7 @@
}
} else {
// Cashtab wallet post 2.9.0
- wallet.paths.forEach((pathInfo, path) => {
+ wallet.paths?.forEach((pathInfo, path) => {
if (path !== 1899) {
legacyPaths.push(path);
}
diff --git a/cashtab/src/wallet/useWallet.js b/cashtab/src/wallet/useWallet.ts
rename from cashtab/src/wallet/useWallet.js
rename to cashtab/src/wallet/useWallet.ts
--- a/cashtab/src/wallet/useWallet.js
+++ b/cashtab/src/wallet/useWallet.ts
@@ -17,17 +17,22 @@
organizeUtxosByType,
parseTx,
getTokenBalances,
+ getTokenGenesisInfo,
} from 'chronik';
-import { queryAliasServer } from 'alias';
+import { queryAliasServer, AliasPrices, AddressAliasStatus } from 'alias';
import appConfig from 'config/app';
import aliasSettings from 'config/alias';
import { CashReceivedNotificationIcon } from 'components/Common/CustomIcons';
-import { supportedFiatCurrencies } from 'config/CashtabSettings';
+import CashtabSettings, {
+ supportedFiatCurrencies,
+} from 'config/CashtabSettings';
import {
cashtabCacheToJSON,
storedCashtabCacheToMap,
cashtabWalletsFromJSON,
cashtabWalletsToJSON,
+ CashtabCacheJson,
+ StoredCashtabWallet,
} from 'helpers';
import {
createCashtabWallet,
@@ -36,35 +41,55 @@
getHashes,
toXec,
hasUnfinalizedTxsInHistory,
+ CashtabWallet,
+ LegacyCashtabWallet,
+ CashtabPathInfo,
} from 'wallet';
import { toast } from 'react-toastify';
-import CashtabState from 'config/CashtabState';
+import CashtabState, { CashtabContact } from 'config/CashtabState';
import TokenIcon from 'components/Etokens/TokenIcon';
import { getUserLocale } from 'helpers';
import { toFormattedXec } from 'utils/formatting';
-
-const useWallet = (chronik, agora, ecc) => {
- const [cashtabLoaded, setCashtabLoaded] = useState(false);
- const [ws, setWs] = useState(null);
- const [fiatPrice, setFiatPrice] = useState(null);
- const [apiError, setApiError] = useState(false);
- const [checkFiatInterval, setCheckFiatInterval] = useState(null);
- const [loading, setLoading] = useState(true);
- const [aliases, setAliases] = useState({
+import {
+ ChronikClient,
+ WsEndpoint,
+ WsMsgClient,
+ MsgBlockClient,
+ MsgTxClient,
+} from 'chronik-client';
+import { Agora } from 'ecash-agora';
+import { Ecc } from 'ecash-lib';
+import CashtabCache from 'config/CashtabCache';
+import { ToastIcon } from 'react-toastify/dist/types';
+
+const useWallet = (chronik: ChronikClient, agora: Agora, ecc: Ecc) => {
+ const [cashtabLoaded, setCashtabLoaded] = useState<boolean>(false);
+ const [ws, setWs] = useState<null | WsEndpoint>(null);
+ const [fiatPrice, setFiatPrice] = useState<null | number>(null);
+ const [apiError, setApiError] = useState<boolean>(false);
+ const [checkFiatInterval, setCheckFiatInterval] =
+ useState<null | NodeJS.Timeout>(null);
+ const [loading, setLoading] = useState<boolean>(true);
+ const [aliases, setAliases] = useState<AddressAliasStatus>({
registered: [],
pending: [],
});
- const [aliasPrices, setAliasPrices] = useState(null);
- const [aliasServerError, setAliasServerError] = useState(false);
- const [aliasIntervalId, setAliasIntervalId] = useState(null);
+ const [aliasPrices, setAliasPrices] = useState<null | AliasPrices>(null);
+ const [aliasServerError, setAliasServerError] = useState<false | string>(
+ false,
+ );
+ const [aliasIntervalId, setAliasIntervalId] =
+ useState<null | NodeJS.Timeout>(null);
const [chaintipBlockheight, setChaintipBlockheight] = useState(0);
- const [cashtabState, setCashtabState] = useState(new CashtabState());
+ const [cashtabState, setCashtabState] = useState<CashtabState>(
+ new CashtabState(),
+ );
const locale = getUserLocale();
// Ref https://stackoverflow.com/questions/53446020/how-to-compare-oldvalues-and-newvalues-on-react-hooks-useeffect
// Get the previous value of a state variable
- const usePrevious = value => {
- const ref = useRef();
+ const usePrevious = <T>(value: T | undefined): T | undefined => {
+ const ref = useRef<T | undefined>(undefined);
useEffect(() => {
ref.current = value;
}, [value]);
@@ -74,7 +99,7 @@
const prevFiatPrice = usePrevious(fiatPrice);
const prevFiatCurrency = usePrevious(cashtabState.settings.fiatCurrency);
- const update = async cashtabState => {
+ const update = async (cashtabState: CashtabState) => {
if (!cashtabLoaded) {
// Wait for cashtab to get state from localforage before updating
return;
@@ -143,14 +168,25 @@
/**
* Lock UI while you update cashtabState in state and indexedDb
- * @param {key} string
- * @param {object} value what is being stored at this key
- * @returns {boolean}
+ * @param string
+ * @param value what is being stored at this key
*/
- const updateCashtabState = async (key, value) => {
+ const updateCashtabState = async (
+ key: string,
+ value:
+ | CashtabWallet[]
+ | CashtabCache
+ | CashtabContact[]
+ | CashtabSettings
+ | CashtabCacheJson
+ | StoredCashtabWallet[]
+ | (LegacyCashtabWallet | StoredCashtabWallet)[],
+ ) => {
// If we are dealing with savedWallets, sort alphabetically by wallet name
if (key === 'savedWallets') {
- value.sort((a, b) => a.name.localeCompare(b.name));
+ (value as CashtabWallet[]).sort((a, b) =>
+ a.name.localeCompare(b.name),
+ );
}
// Update the changed key in state
@@ -161,10 +197,10 @@
// Handle any items that must be converted to JSON before storage
// For now, this is just cashtabCache
if (key === 'cashtabCache') {
- value = cashtabCacheToJSON(value);
+ value = cashtabCacheToJSON(value as CashtabCache);
}
if (key === 'wallets') {
- value = cashtabWalletsToJSON(value);
+ value = cashtabWalletsToJSON(value as CashtabWallet[]);
}
// We lock the UI by setting loading to true while we set items in localforage
@@ -202,10 +238,13 @@
// We do not call a function to migrate contactList as no other migration is expected
contactList = [];
// Update localforage on app load only if existing values are in an obsolete format
- updateCashtabState('contactList', contactList);
+ updateCashtabState(
+ 'contactList',
+ contactList as CashtabContact[],
+ );
}
// Set cashtabState contactList to valid localforage or migrated
- cashtabState.contactList = contactList;
+ cashtabState.contactList = contactList as CashtabContact[];
}
// settings
@@ -214,23 +253,31 @@
// If we find settings in localforage
if (!isValidCashtabSettings(settings)) {
// If a settings object is present but invalid, parse to find and add missing keys
- settings = migrateLegacyCashtabSettings(settings);
+ settings = migrateLegacyCashtabSettings(
+ settings as unknown as CashtabSettings,
+ );
// Update localforage on app load only if existing values are in an obsolete format
- updateCashtabState('settings', settings);
+ updateCashtabState(
+ 'settings',
+ settings as unknown as CashtabSettings,
+ );
}
// Set cashtabState settings to valid localforage or migrated settings
- cashtabState.settings = settings;
+ cashtabState.settings = settings as CashtabSettings;
}
// cashtabCache
- let cashtabCache = await localforage.getItem('cashtabCache');
+ let cashtabCache: null | CashtabCacheJson | CashtabCache =
+ await localforage.getItem('cashtabCache');
if (cashtabCache !== null) {
// If we find cashtabCache in localforage
// cashtabCache must be converted from JSON as it stores a Map
- cashtabCache = storedCashtabCacheToMap(cashtabCache);
+ cashtabCache = storedCashtabCacheToMap(
+ cashtabCache as CashtabCacheJson,
+ );
if (!isValidCashtabCache(cashtabCache)) {
// If a cashtabCache object is present but invalid, nuke it and start again
@@ -247,10 +294,14 @@
// Make sure case of nothing at wallet or wallets is handled properly
// A legacy Cashtab user may have the active wallet stored at the wallet key
- let wallet = await localforage.getItem('wallet');
+ const storedWallet: null | LegacyCashtabWallet =
+ await localforage.getItem('wallet');
// After version 1.7.x, Cashtab users have all wallets stored at the wallets key
- let wallets = await localforage.getItem('wallets');
+ const storedWallets:
+ | null
+ | LegacyCashtabWallet[]
+ | StoredCashtabWallet[] = await localforage.getItem('wallets');
/**
* Possible cases
@@ -278,46 +329,51 @@
* Migrate to wallets key
*/
- const legacyMigrationRequired = wallet !== null && wallets === null;
-
- if (legacyMigrationRequired) {
- // Initialize wallets array
- wallets = [];
+ const legacyKeyMigrationRequired =
+ storedWallet !== null && storedWallets === null;
+ let wallets: CashtabWallet[] = [];
+ if (legacyKeyMigrationRequired) {
+ // No need to check if a wallet stored at legacy 'wallet' key is valid
+ // We know it won't be, rebuild it
// Migrate this Cashtab user from keys "wallet" and "savedWallets" to key "wallets"
- if (!isValidCashtabWallet(wallet)) {
- // Determine if this wallet has legacy paths
- // Cashtab wallets used to be created with Path145, Path245, and Path1899 keys
- const extraPathsToMigrate = getLegacyPaths(wallet);
-
- // If wallet is invalid, rebuild to latest Cashtab schema
- const newWallet = await createCashtabWallet(
- wallet.mnemonic,
- extraPathsToMigrate,
- );
- // Keep original name
- wallet = { ...newWallet, name: wallet.name };
- }
+ // Determine if this wallet has legacy paths
+ // Cashtab wallets used to be created with Path145, Path245, and Path1899 keys
+ const extraPathsToMigrate = getLegacyPaths(storedWallet);
+
+ // If wallet is invalid, rebuild to latest Cashtab schema
+ let newWallet = await createCashtabWallet(
+ storedWallet.mnemonic,
+ extraPathsToMigrate,
+ );
+
+ // Keep original name
+ newWallet = { ...newWallet, name: storedWallet.name };
// wallets[0] is the active wallet in upgraded Cashtab localforage model
- wallets.push(wallet);
+ wallets.push(newWallet);
// Also migrate savedWallets
- let savedWallets = await localforage.getItem('savedWallets');
+ // Note that savedWallets is also a legacy key
+ const savedWallets: null | LegacyCashtabWallet[] =
+ await localforage.getItem('savedWallets');
if (savedWallets !== null) {
- // If we find savedWallets in localforage
+ // If we find savedWallets in localforage, they will all be invalid
+ // as this key is deprecated
// Iterate over all savedWallets.
// If valid, do not change.
// If invalid, migrate and update savedWallets
- savedWallets = await Promise.all(
- savedWallets.map(async savedWallet => {
- if (!isValidCashtabWallet(savedWallet)) {
+ const migratedSavedWallets = await Promise.all(
+ savedWallets.map(
+ async (savedWallet): Promise<CashtabWallet> => {
// We may also have to migrate legacy paths for a saved wallet
- const extraPathsToMigrate = getLegacyPaths(wallet);
+ const extraPathsToMigrate =
+ getLegacyPaths(savedWallet);
// Recreate this wallet at latest format from mnemonic
+
const newSavedWallet = await createCashtabWallet(
savedWallet.mnemonic,
extraPathsToMigrate,
@@ -327,25 +383,30 @@
...newSavedWallet,
name: savedWallet.name,
};
- }
- // No modification if it is valid
- return savedWallet;
- }),
+ },
+ ),
);
- // Because Promise.all() will not preserve order, sort savedWallets alphabetically by name
- savedWallets.sort((a, b) => a.name.localeCompare(b.name));
+ // Because Promise.all() will not preserve order, sort alphabetically by name
+ migratedSavedWallets.sort((a, b) =>
+ a.name.localeCompare(b.name),
+ );
// In legacy Cashtab storage, the key savedWallets also stored the active wallet
// Delete wallet from savedWallets
- const indexOfSavedWalletMatchingWallet = savedWallets.findIndex(
- savedWallet => savedWallet.mnemonic === wallet.mnemonic,
+ const indexOfSavedWalletMatchingWallet =
+ migratedSavedWallets.findIndex(
+ savedWallet =>
+ savedWallet.mnemonic === newWallet.mnemonic,
+ );
+ migratedSavedWallets.splice(
+ indexOfSavedWalletMatchingWallet,
+ 1,
);
- savedWallets.splice(indexOfSavedWalletMatchingWallet, 1);
// Update wallets array to include legacy wallet and legacy savedWallets
// migrated to current Cashtab format
- wallets = wallets.concat(savedWallets);
+ wallets = wallets.concat(migratedSavedWallets);
// Set cashtabState wallets to migrated wallet + savedWallets
cashtabState.wallets = wallets;
@@ -357,60 +418,119 @@
} else {
// Load from wallets key, or initialize new user
- // If the user has already migrated, we load wallets from localforage key directly
+ // If the user has already migrated to latest keys, we load wallets from localforage key directly
- if (wallets !== null) {
+ if (storedWallets !== null && storedWallets.length > 0) {
// If we find wallets in localforage
// In this case, we do not need to migrate from the wallet and savedWallets keys
// We may or may not need to migrate wallets found at the wallets key to a new format
// Revive from storage
- wallets = cashtabWalletsFromJSON(wallets);
+ const loadedPossiblyLegacyWallets =
+ cashtabWalletsFromJSON(storedWallets);
+
+ // Validate
+ let walletsValid = true;
+ for (const loadedPossiblyLegacyWallet of loadedPossiblyLegacyWallets) {
+ if (!isValidCashtabWallet(loadedPossiblyLegacyWallet)) {
+ walletsValid = false;
+ // Any invalid wallet means we need to migrate
+ break;
+ }
+ }
+ console.log(`walletsValid`, walletsValid);
- // Iterate over all wallets. If valid, do not change. If invalid, migrate and update array.
- wallets = await Promise.all(
- wallets.map(async wallet => {
- if (!isValidCashtabWallet(wallet)) {
- // We may also have to migrate legacy paths for a saved wallet
- const extraPathsToMigrate = getLegacyPaths(wallet);
+ if (walletsValid) {
+ // Set cashtabState wallets to wallets from localforage
+ // (or migrated wallets if localforage included any invalid wallet)
+ cashtabState.wallets =
+ loadedPossiblyLegacyWallets as CashtabWallet[];
- // Recreate this wallet at latest format from mnemonic
- const migratedWallet = await createCashtabWallet(
- wallet.mnemonic,
+ // We do not updateCashtabState('wallets', wallets) here
+ // because it will happen in the update routine as soon as
+ // the active wallet is populated
+ } else {
+ // Handle the 0-index wallet separately, as this is the active wallet
+ const activeWallet = loadedPossiblyLegacyWallets.shift() as
+ | LegacyCashtabWallet
+ | CashtabWallet;
+ let migratedWallets: CashtabWallet[] = [];
+ if (!isValidCashtabWallet(activeWallet)) {
+ // Migrate the active wallet
+ // We may also have to migrate legacy paths for a saved wallet
+ const extraPathsToMigrate =
+ getLegacyPaths(activeWallet);
+
+ // Recreate this wallet at latest format from mnemonic
+
+ const migratedUnnamedActiveWallet =
+ await createCashtabWallet(
+ activeWallet.mnemonic,
extraPathsToMigrate,
);
- // Keep the same name as existing wallet
- return {
- ...migratedWallet,
- name: wallet.name,
- };
- }
+ // Keep the same name as existing wallet
+ const migratedNamedActiveWallet = {
+ ...migratedUnnamedActiveWallet,
+ name: activeWallet.name,
+ };
+ migratedWallets.push(migratedNamedActiveWallet);
+ } else {
+ migratedWallets.push(activeWallet as CashtabWallet);
+ }
+ // Iterate over all wallets. If valid, do not change. If invalid, migrate and update array.
+ const otherMigratedWallets = await Promise.all(
+ loadedPossiblyLegacyWallets.map(
+ async loadedPossiblyLegacyWallet => {
+ if (
+ !isValidCashtabWallet(
+ loadedPossiblyLegacyWallet,
+ )
+ ) {
+ // We may also have to migrate legacy paths for a saved wallet
+ const extraPathsToMigrate = getLegacyPaths(
+ loadedPossiblyLegacyWallet as LegacyCashtabWallet,
+ );
+
+ // Recreate this wallet at latest format from mnemonic
+
+ const migratedWallet =
+ await createCashtabWallet(
+ loadedPossiblyLegacyWallet.mnemonic,
+ extraPathsToMigrate,
+ );
+
+ // Keep the same name as existing wallet
+ return {
+ ...migratedWallet,
+ name: loadedPossiblyLegacyWallet.name,
+ };
+ }
+
+ // No modification if it is valid
+ return loadedPossiblyLegacyWallet as CashtabWallet;
+ },
+ ),
+ );
+ // Because Promise.all() will not preserve order, sort wallets alphabetically by name
+ otherMigratedWallets.sort((a, b) =>
+ a.name.localeCompare(b.name),
+ );
- // No modification if it is valid
- return wallet;
- }),
- );
+ migratedWallets =
+ migratedWallets.concat(otherMigratedWallets);
- // Because Promise.all() will not preserve order, sort wallets alphabetically by name
- // First remove wallets[0] as this is the active wallet and we do not want to sort it
- const activeWallet = wallets.shift();
- // Sort other wallets alphabetically
- wallets.sort((a, b) => a.name.localeCompare(b.name));
- // Replace the active wallet at the 0-index
- wallets.unshift(activeWallet);
+ console.log(`migratedWallets`, migratedWallets);
- // Set cashtabState wallets to wallets from localforage
- // (or migrated wallets if localforage included any invalid wallet)
+ // Set cashtabState wallets to wallets from localforage
+ // (or migrated wallets if localforage included any invalid wallet)
+ cashtabState.wallets = migratedWallets;
+ }
+ } else {
+ // So, if we do not find wallets from localforage, cashtabState will be initialized with default
+ // wallets []
cashtabState.wallets = wallets;
-
- // We do not updateCashtabState('wallets', wallets) here
- // because it will happen in the update routine as soon as
- // the active wallet is populated
}
-
- // So, if we do not find wallets from localforage, cashtabState will be initialized with default
- // wallets []
}
setCashtabState(cashtabState);
setCashtabLoaded(true);
@@ -421,10 +541,10 @@
// 1) txs may not be marked as avalanche finalized until we get it
// 2) we ignore all coinbase utxos in tx building
try {
- let info = await chronik.blockchainInfo();
+ const info = await chronik.blockchainInfo();
const { tipHeight } = info;
// See if it is finalized
- let blockDetails = await chronik.block(tipHeight);
+ const blockDetails = await chronik.block(tipHeight);
if (blockDetails.blockInfo.isFinal) {
// We only set a chaintip if it is avalanche finalized
@@ -478,10 +598,17 @@
/**
* Update websocket subscriptions when active wallet changes
* Update websocket onMessage handler when fiatPrice changes
- * @param {object} cashtabState
- * @param {number} fiatPrice
+ * @param cashtabState
+ * @param fiatPrice
*/
- const updateWebsocket = (cashtabState, fiatPrice) => {
+ const updateWebsocket = (
+ cashtabState: CashtabState,
+ fiatPrice: number | null,
+ ) => {
+ if (ws === null) {
+ // Should never happen, we only call this in a useEffect when ws is not null
+ return;
+ }
// Set or update the onMessage handler
// We can only set this when wallet is defined, so we do not set it in loadCashtabState
ws.onMessage = msg => {
@@ -541,11 +668,16 @@
// Parse chronik ws message for incoming tx notifications
const processChronikWsMsg = async (
- msg,
- cashtabState,
- fiatPrice,
- aliasesEnabled,
+ msg: WsMsgClient,
+ cashtabState: CashtabState,
+ fiatPrice: null | number,
+ aliasesEnabled: boolean,
) => {
+ if (!('msgType' in msg)) {
+ // No processing chronik error msgs
+ console.error(`Error from chronik websocket`, msg);
+ return;
+ }
// get the message type
const { msgType } = msg;
// get cashtabState params from param, so you know they are the most recent
@@ -568,7 +700,7 @@
if (aliasesEnabled) {
try {
const aliasPricesResp = await queryAliasServer('prices');
- if (!aliasPricesResp || !aliasPricesResp.prices) {
+ if (!aliasPricesResp || !('prices' in aliasPricesResp)) {
throw new Error(
'Invalid response from alias prices endpoint',
);
@@ -586,7 +718,7 @@
}
setAliasPrices(aliasPricesResp);
} catch (err) {
- setAliasServerError(err);
+ setAliasServerError(`${err}`);
}
}
@@ -609,7 +741,7 @@
update(cashtabState);
// get txid info
- const txid = msg.txid;
+ const txid = (msg as MsgTxClient).txid;
let incomingTxDetails;
try {
@@ -622,7 +754,7 @@
);
}
- let tokenCacheForParsingThisTx = cashtabCache.tokens;
+ const tokenCacheForParsingThisTx = cashtabCache.tokens;
let thisTokenCachedInfo;
let tokenId;
if (
@@ -638,7 +770,10 @@
// If we do not have this token cached
// Note we do not update the cache here because this is handled in update
try {
- thisTokenCachedInfo = await chronik.token(tokenId);
+ thisTokenCachedInfo = await getTokenGenesisInfo(
+ chronik,
+ tokenId,
+ );
tokenCacheForParsingThisTx.set(
tokenId,
thisTokenCachedInfo,
@@ -666,6 +801,7 @@
if (parsedTx.xecTxType === 'Received') {
if (
incomingTxDetails.tokenEntries.length > 0 &&
+ typeof tokenId === 'string' &&
incomingTxDetails.tokenEntries[0].txType === 'SEND' &&
incomingTxDetails.tokenEntries[0].burnSummary === '' &&
incomingTxDetails.tokenEntries[0].actualBurnAmount === '0'
@@ -688,14 +824,19 @@
// getNotification(Tx_InNode) that can be easily tested and called here
}
toast(eTokenReceivedString, {
- icon: <TokenIcon size={32} tokenId={tokenId} />,
+ icon: React.createElement(TokenIcon, {
+ size: 32,
+ tokenId: tokenId,
+ }) as unknown as ToastIcon,
});
} else {
const xecReceivedString = `Received ${toFormattedXec(
parsedTx.satoshisSent,
locale,
)} ${appConfig.ticker}${
- settings && typeof settings.fiatCurrency !== 'undefined'
+ settings &&
+ typeof settings.fiatCurrency !== 'undefined' &&
+ fiatPrice !== null
? ` (${
supportedFiatCurrencies[settings.fiatCurrency]
.symbol
@@ -716,7 +857,7 @@
// With different currency selections possible, need unique intervals for price checks
// Must be able to end them and set new ones with new currencies
- const initializeFiatPriceApi = async selectedFiatCurrency => {
+ const initializeFiatPriceApi = async (selectedFiatCurrency: string) => {
if (process.env.REACT_APP_TESTNET === 'true') {
return setFiatPrice(0);
}
@@ -737,7 +878,7 @@
setCheckFiatInterval(thisFiatInterval);
};
- const clearFiatPriceApi = fiatPriceApi => {
+ const clearFiatPriceApi = (fiatPriceApi: NodeJS.Timeout) => {
// Clear fiat price check interval of previously selected currency
clearInterval(fiatPriceApi);
};
@@ -754,14 +895,19 @@
try {
const xecPrice = await fetch(priceApiUrl);
const xecPriceJson = await xecPrice.json();
- let xecPriceInFiat = xecPriceJson[cryptoId][fiatCode];
+ const xecPriceInFiat = xecPriceJson[cryptoId][fiatCode];
if (typeof xecPriceInFiat === 'number') {
// If we have a good fetch
return setFiatPrice(xecPriceInFiat);
}
} catch (err) {
- if (err.message === 'Failed to fetch') {
+ if (
+ typeof err === 'object' &&
+ err !== null &&
+ 'message' in err &&
+ err.message === 'Failed to fetch'
+ ) {
// The most common error is coingecko 429
console.error(
`Failed to fetch XEC Price: Bad response or rate limit from CoinGecko`,
@@ -779,27 +925,27 @@
* and stores them in the aliases state var for other components to access
* @param {string} thisAddress the address to be queried for attached aliases
*/
- const refreshAliases = async thisAddress => {
+ const refreshAliases = async (thisAddress: string) => {
try {
const aliasesForThisAddress = await queryAliasServer(
'address',
thisAddress,
);
- if (aliasesForThisAddress.error) {
- // If an error is returned from the address endpoint
- throw new Error(aliasesForThisAddress.error);
- }
setAliases({
- registered: aliasesForThisAddress.registered.sort((a, b) =>
- a.alias.localeCompare(b.alias),
- ),
- pending: aliasesForThisAddress.pending.sort((a, b) =>
- a.alias.localeCompare(b.alias),
- ),
+ registered: (
+ aliasesForThisAddress as AddressAliasStatus
+ ).registered.sort((a, b) => a.alias.localeCompare(b.alias)),
+ pending: (
+ aliasesForThisAddress as AddressAliasStatus
+ ).pending.sort((a, b) => a.alias.localeCompare(b.alias)),
});
setAliasServerError(false);
// Clear interval if there are no pending aliases
- if (aliasesForThisAddress.pending.length === 0 && aliasIntervalId) {
+ if (
+ (aliasesForThisAddress as AddressAliasStatus).pending.length ===
+ 0 &&
+ aliasIntervalId
+ ) {
console.info(
`refreshAliases(): No pending aliases, clearing interval ${aliasIntervalId}`,
);
@@ -839,7 +985,10 @@
return;
}
// Clear existing fiat price API check
- clearFiatPriceApi(checkFiatInterval);
+ if (checkFiatInterval !== null) {
+ clearFiatPriceApi(checkFiatInterval);
+ }
+
// Reset fiat price API when fiatCurrency setting changes
initializeFiatPriceApi(cashtabState.settings.fiatCurrency);
}, [cashtabLoaded, cashtabState.settings.fiatCurrency]);
@@ -874,15 +1023,19 @@
return;
}
// Otherwise we do support them
- if (fiatPrice === null || prevFiatPrice === null) {
+ if (
+ fiatPrice === null ||
+ prevFiatPrice === null ||
+ typeof prevFiatPrice === 'undefined'
+ ) {
return;
}
const priceIncreased = fiatPrice - prevFiatPrice > 0;
if (priceIncreased) {
// We only show price notifications if price has increased
// "tens" for USD price per 1,000,000 XEC
- const prevTens = parseInt(Math.floor(prevFiatPrice * 1e5));
- const tens = parseInt(Math.floor(fiatPrice * 1e5));
+ const prevTens = Math.floor(prevFiatPrice * 1e5);
+ const tens = Math.floor(fiatPrice * 1e5);
if (tens > prevTens) {
// We have passed a $10 milestone
toast(
@@ -932,7 +1085,7 @@
* @param {string} address
* @returns callback function to cleanup interval
*/
- const refreshAliasesOnStartup = async address => {
+ const refreshAliasesOnStartup = async (address: string) => {
// Initial refresh to ensure `aliases` state var is up to date
await refreshAliases(address);
const aliasRefreshInterval = 30000;
@@ -961,7 +1114,8 @@
// 4) We have an active wallet
// Set an interval to watch these pending aliases
refreshAliasesOnStartup(
- cashtabState.wallets[0].paths.get(1899).address,
+ (cashtabState.wallets[0].paths.get(1899) as CashtabPathInfo)
+ .address,
);
} else if (aliases?.pending?.length === 0 && aliasIntervalId !== null) {
// If we have no pending aliases but we still have an interval to check them, clearInterval

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 6, 17:29 (21 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082779
Default Alt Text
D17140.id50877.diff (147 KB)

Event Timeline