Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12945049
D17140.id50877.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
147 KB
Subscribers
None
D17140.id50877.diff
View Options
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
Details
Attached
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)
Attached To
D17140: [Cashtab] More ts implementation as prep for wallet upgrade
Event Timeline
Log In to Comment