diff --git a/chrome-extension/src/background/chains/bitcoinCashHandler.ts b/chrome-extension/src/background/chains/bitcoinCashHandler.ts index bd7c370..2497e6b 100644 --- a/chrome-extension/src/background/chains/bitcoinCashHandler.ts +++ b/chrome-extension/src/background/chains/bitcoinCashHandler.ts @@ -1,12 +1,17 @@ +import { bip32ToAddressNList } from '@pioneer-platform/pioneer-coins'; + const TAG = ' | bitcoinCashHandler | '; import { JsonRpcProvider } from 'ethers'; -import { Chain } from '@coinmasters/types'; +import { Chain, DerivationPath } from '@coinmasters/types'; import { AssetValue } from '@pioneer-platform/helpers'; // @ts-ignore // @ts-ignore import { ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '@pioneer-platform/pioneer-caip'; // @ts-ignore import { v4 as uuidv4 } from 'uuid'; +import { requestStorage } from '@extension/storage/dist/lib'; +//@ts-ignore +import * as coinSelect from 'coinselect'; interface ProviderRpcError extends Error { code: number; @@ -57,49 +62,156 @@ export const handleBitcoinCashRequest = async ( return [balance]; } case 'transfer': { - //caip const caip = shortListSymbolToCaip['BCH']; console.log(tag, 'caip: ', caip); const networkId = caipToNetworkId(caip); - //verify context is bitcoin + requestInfo.id = uuidv4(); + console.log(tag, 'assetContext: ', KEEPKEY_WALLET); + // eslint-disable-next-line no-constant-condition if (!KEEPKEY_WALLET.assetContext) { + // Set context to the chain, defaults to ETH await KEEPKEY_WALLET.setAssetContext({ caip }); } + const pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => + e.networks.includes(ChainToNetworkId[Chain.BitcoinCash]), + ); + console.log(tag, 'pubkeys: ', pubkeys); + if (!pubkeys || pubkeys.length === 0) throw Error('Failed to locate pubkeys for chain ' + Chain.BitcoinCash); - //send tx console.log(tag, 'params[0]: ', params[0]); const assetString = 'BCH.BCH'; await AssetValue.loadStaticAssets(); console.log(tag, 'params[0].amount.amount: ', params[0].amount.amount); const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); + + const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.BitcoinCash); + if (!wallet) throw new Error('Failed to init swapkit'); + const walletAddress = await wallet.getAddress(); + console.log(tag, 'walletAddress: ', walletAddress); + const sendPayload = { - from: params[0].from, + from: walletAddress, // Select preference change address assetValue, memo: params[0].memo || '', recipient: params[0].recipient, }; - console.log(tag, 'sendPayload: ', sendPayload); - // const txHash = await KEEPKEY_WALLET.swapKit.transfer(sendPayload); - // console.log(tag, 'txHash: ', txHash); - - const unsignedTx = await wallet.buildTx(sendPayload); - log.info('unsignedTx: ', unsignedTx); - requestInfo.unsignedTx = unsignedTx; - // Require user approval - const result = await requireApproval(networkId, requestInfo, 'bitcoin', method, params[0]); + + const buildTx = async function () { + try { + const utxos = []; + for (let i = 0; i < pubkeys.length; i++) { + const pubkey = pubkeys[i]; + let utxosResp = await KEEPKEY_WALLET.pioneer.ListUnspent({ network: 'BCH', xpub: pubkey.pubkey }); + utxosResp = utxosResp.data; + console.log('utxosResp: ', utxosResp); + utxos.push(...utxosResp); + } + console.log(tag, 'utxos: ', utxos); + + //get new change address + let changeAddressIndex = await KEEPKEY_WALLET.pioneer.GetChangeAddress({ + network: 'BCH', + xpub: pubkeys[0].pubkey || pubkeys[0].xpub, + }); + changeAddressIndex = changeAddressIndex.data.changeIndex; + console.log(tag, 'changeAddressIndex: ', changeAddressIndex); + + const path = DerivationPath['BCH'].replace('/0/0', `/1/${changeAddressIndex}`); + console.log(tag, 'path: ', path); + const customAddressInfo = { + coin: 'BitcoinCash', + script_type: 'p2pkh', + address_n: bip32ToAddressNList(path), + }; + const address = await wallet.getAddress(customAddressInfo); + console.log('address: ', address); + const changeAddress = { + address: address, + path: path, + index: changeAddressIndex, + addressNList: bip32ToAddressNList(path), + }; + + for (let i = 0; i < utxos.length; i++) { + const utxo = utxos[i]; + //@ts-ignore + utxo.value = Number(utxo.value); + } + console.log('utxos: ', utxos); + + const amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); + + console.log(tag, 'amountOut: ', amountOut); + const effectiveFeeRate = 10; + console.log('utxos: ', utxos); + const { inputs, outputs, fee } = coinSelect.default( + utxos, + [{ address: params[0].recipient, value: amountOut }], + effectiveFeeRate, + ); + console.log('inputs: ', inputs); + console.log('outputs: ', outputs); + console.log('fee: ', fee); + + const unsignedTx = await wallet.buildTx({ + inputs, + outputs, + memo: 'test', + changeAddress, + }); + + //push to front + + chrome.runtime.sendMessage({ + action: 'utxo_build_tx', + unsignedTx: requestInfo, + }); + + const storedEvent = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'storedEvent: ', storedEvent); + storedEvent.utxos = utxos; + storedEvent.changeAddress = changeAddress; + storedEvent.unsignedTx = unsignedTx; + await requestStorage.updateEventById(requestInfo.id, storedEvent); + } catch (e) { + console.error(e); + } + }; + + buildTx(); + + // Proceed with requiring approval without waiting for buildTx to resolve + const result = await requireApproval(networkId, requestInfo, 'bitcoincash', method, params[0]); console.log(tag, 'result:', result); - // signTransaction - const signedTx = await wallet.signTransaction(unsignedTx.psbt, unsignedTx.inputs, unsignedTx.memo); - log.info('signedTx: ', signedTx); + const response = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'response: ', response); + + if (result.success && response.unsignedTx) { + const signedTx = await wallet.signTx( + response.unsignedTx.inputs, + response.unsignedTx.outputs, + response.unsignedTx.memo, + ); + + response.signedTx = signedTx; + await requestStorage.updateEventById(requestInfo.id, response); - //push signed to user to approve + const txHash = await wallet.broadcastTx(signedTx); - //broadcastTx - const txid = await wallet.broadcastTx(signedTx); - log.info('txid: ', txid); + response.txid = txHash; + await requestStorage.updateEventById(requestInfo.id, response); - return txid; + //push event + chrome.runtime.sendMessage({ + action: 'transaction_complete', + txHash: txHash, + }); + + return txHash; + } else { + throw createProviderRpcError(4200, 'User denied transaction'); + } } default: { console.log(tag, `Method ${method} not supported`); diff --git a/chrome-extension/src/background/chains/bitcoinHandler.ts b/chrome-extension/src/background/chains/bitcoinHandler.ts index 845bdfd..cfbf457 100644 --- a/chrome-extension/src/background/chains/bitcoinHandler.ts +++ b/chrome-extension/src/background/chains/bitcoinHandler.ts @@ -1,11 +1,14 @@ import { requestStorage } from '@extension/storage/dist/lib'; const TAG = ' | bitcoinHandler | '; -import { Chain } from '@coinmasters/types'; +import { Chain, DerivationPath } from '@coinmasters/types'; import { AssetValue } from '@pioneer-platform/helpers'; import { ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '@pioneer-platform/pioneer-caip'; //@ts-ignore import { v4 as uuidv4 } from 'uuid'; +import { bip32ToAddressNList } from '@pioneer-platform/pioneer-coins'; +//@ts-ignore +import * as coinSelect from 'coinselect'; interface ProviderRpcError extends Error { code: number; @@ -66,68 +69,149 @@ export const handleBitcoinRequest = async ( case 'transfer': { const caip = shortListSymbolToCaip['BTC']; + console.log(tag, 'caip: ', caip); const networkId = caipToNetworkId(caip); requestInfo.id = uuidv4(); + console.log(tag, 'assetContext: ', KEEPKEY_WALLET); + // eslint-disable-next-line no-constant-condition if (!KEEPKEY_WALLET.assetContext) { + // Set context to the chain, defaults to ETH await KEEPKEY_WALLET.setAssetContext({ caip }); } - const pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => e.networks.includes(ChainToNetworkId[Chain.Bitcoin])); - const accounts = pubkeys.map((pubkey: any) => pubkey.master || pubkey.address); + console.log(tag, 'pubkeys: ', pubkeys); + if (!pubkeys || pubkeys.length === 0) throw Error('Failed to locate pubkeys for chain ' + Chain.Bitcoin); + console.log(tag, 'params[0]: ', params[0]); const assetString = 'BTC.BTC'; await AssetValue.loadStaticAssets(); + console.log(tag, 'params[0].amount.amount: ', params[0].amount.amount); + const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); + + const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Bitcoin); + if (!wallet) throw new Error('Failed to init swapkit'); + const walletAddress = await wallet.getAddress(); + console.log(tag, 'walletAddress: ', walletAddress); - const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount) / 100000000); const sendPayload = { - from: accounts[0], // Select preference change address + from: walletAddress, // Select preference change address assetValue, memo: params[0].memo || '', recipient: params[0].recipient, }; - // Start building the transaction but don't wait for it to finish - const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Bitcoin); - if (!wallet) throw new Error('Failed to init swapkit'); - - const pubkeysLocal = await wallet.getPubkeys(); - console.log(tag, 'pubkeysLocal: ', pubkeysLocal); - - const buildTxPromise = wallet - .buildTx(sendPayload) - .then(async unsignedTx => { - console.log(tag, 'unsignedTx', unsignedTx); - // Update requestInfo with transaction details after buildTx resolves - requestInfo.inputs = unsignedTx.inputs; - requestInfo.outputs = unsignedTx.outputs; - requestInfo.memo = unsignedTx.memo; + const buildTx = async function () { + try { + const utxos = []; + for (let i = 0; i < pubkeys.length; i++) { + const pubkey = pubkeys[i]; + let utxosResp = await KEEPKEY_WALLET.pioneer.ListUnspent({ network: 'BTC', xpub: pubkey.pubkey }); + utxosResp = utxosResp.data; + console.log('utxosResp: ', utxosResp); + utxos.push(...utxosResp); + } + console.log(tag, 'utxos: ', utxos); + + //get new change address + let changeAddressIndex = await KEEPKEY_WALLET.pioneer.GetChangeAddress({ + network: 'BTC', + xpub: pubkeys[0].pubkey || pubkeys[0].xpub, + }); + changeAddressIndex = changeAddressIndex.data.changeIndex; + console.log(tag, 'changeAddressIndex: ', changeAddressIndex); + + const path = DerivationPath['BTC'].replace('/0/0', `/1/${changeAddressIndex}`); + console.log(tag, 'path: ', path); + const customAddressInfo = { + coin: 'Bitcoin', + script_type: 'p2pkh', + address_n: bip32ToAddressNList(path), + }; + const address = await wallet.getAddress(customAddressInfo); + console.log('address: ', address); + const changeAddress = { + address: address, + path: path, + index: changeAddressIndex, + addressNList: bip32ToAddressNList(path), + }; + + for (let i = 0; i < utxos.length; i++) { + const utxo = utxos[i]; + //@ts-ignore + utxo.value = Number(utxo.value); + } + console.log('utxos: ', utxos); + + const amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); + + console.log(tag, 'amountOut: ', amountOut); + const effectiveFeeRate = 10; + console.log('utxos: ', utxos); + const { inputs, outputs, fee } = coinSelect.default( + utxos, + [{ address: params[0].recipient, value: amountOut }], + effectiveFeeRate, + ); + console.log('inputs: ', inputs); + console.log('outputs: ', outputs); + console.log('fee: ', fee); + + const unsignedTx = await wallet.buildTx({ + inputs, + outputs, + memo: 'test', + changeAddress, + }); + //push to front chrome.runtime.sendMessage({ action: 'utxo_build_tx', - unsignedTx: unsignedTx, + unsignedTx: requestInfo, }); - const response = await requestStorage.getEventById(requestInfo.id); - response.unsignedTx = unsignedTx; - await requestStorage.updateEventById(requestInfo.id, response); - - // Push an event to the front-end that UTXOs are found - // This could be something like: sendUpdateToFrontend('UTXOs found', unsignedTx); - }) - .catch(error => { - console.error('Error building the transaction:', error); - // Handle buildTx failure appropriately, such as notifying the user - }); + + const storedEvent = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'storedEvent: ', storedEvent); + storedEvent.utxos = utxos; + storedEvent.changeAddress = changeAddress; + storedEvent.unsignedTx = unsignedTx; + await requestStorage.updateEventById(requestInfo.id, storedEvent); + } catch (e) { + console.error(e); + } + }; + + buildTx(); // Proceed with requiring approval without waiting for buildTx to resolve const result = await requireApproval(networkId, requestInfo, 'bitcoin', method, params[0]); + console.log(tag, 'result:', result); + + const response = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'response: ', response); - // Wait for the buildTx to complete (if not already completed) before signing - const unsignedTx = await buildTxPromise; + if (result.success && response.unsignedTx) { + const signedTx = await wallet.signTx( + response.unsignedTx.inputs, + response.unsignedTx.outputs, + response.unsignedTx.memo, + ); + + response.signedTx = signedTx; + await requestStorage.updateEventById(requestInfo.id, response); + + const txHash = await wallet.broadcastTx(signedTx); + + response.txid = txHash; + await requestStorage.updateEventById(requestInfo.id, response); + + //push event + chrome.runtime.sendMessage({ + action: 'transaction_complete', + txHash: txHash, + }); - if (result.success && unsignedTx) { - const signedTx = await wallet.signTransaction(unsignedTx.psbt, unsignedTx.inputs, unsignedTx.memo); - const txid = await wallet.broadcastTx(signedTx); - return txid; + return txHash; } else { throw createProviderRpcError(4200, 'User denied transaction'); } diff --git a/chrome-extension/src/background/chains/dashHandler.ts b/chrome-extension/src/background/chains/dashHandler.ts index 0947e66..2e7180f 100644 --- a/chrome-extension/src/background/chains/dashHandler.ts +++ b/chrome-extension/src/background/chains/dashHandler.ts @@ -1,7 +1,7 @@ const TAG = ' | dashHandler | '; import { requestStorage } from '@extension/storage/dist/lib'; import { JsonRpcProvider } from 'ethers'; -import { Chain } from '@coinmasters/types'; +import { Chain, DerivationPath } from '@coinmasters/types'; import { AssetValue } from '@pioneer-platform/helpers'; //@ts-ignore import * as coinSelect from 'coinselect'; @@ -41,11 +41,11 @@ export const handleDashRequest = async ( switch (method) { case 'request_accounts': { - let pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => e.networks.includes(ChainToNetworkId[Chain.Dash])); - let accounts = []; + const pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => e.networks.includes(ChainToNetworkId[Chain.Dash])); + const accounts = []; for (let i = 0; i < pubkeys.length; i++) { - let pubkey = pubkeys[i]; - let address = pubkey.master || pubkey.address; + const pubkey = pubkeys[i]; + const address = pubkey.master || pubkey.address; accounts.push(address); } console.log(tag, 'accounts: ', accounts); @@ -58,7 +58,7 @@ export const handleDashRequest = async ( console.log(tag, 'KEEPKEY_WALLET: ', KEEPKEY_WALLET); console.log(tag, 'KEEPKEY_WALLET.swapKit: ', KEEPKEY_WALLET.swapKit); console.log(tag, 'KEEPKEY_WALLET.swapKit: ', KEEPKEY_WALLET.balances); - let balance = KEEPKEY_WALLET.balances.find((balance: any) => balance.caip === shortListSymbolToCaip['BTC']); + const balance = KEEPKEY_WALLET.balances.find((balance: any) => balance.caip === shortListSymbolToCaip['BTC']); //let pubkeys = await KEEPKEY_WALLET.swapKit.getBalance(Chain.Bitcoin); console.log(tag, 'balance: ', balance); @@ -80,10 +80,10 @@ export const handleDashRequest = async ( if (!pubkeys || pubkeys.length === 0) throw Error('Failed to locate pubkeys for chain ' + Chain.Dash); console.log(tag, 'params[0]: ', params[0]); - let assetString = 'DASH.DASH'; + const assetString = 'DASH.DASH'; await AssetValue.loadStaticAssets(); console.log(tag, 'params[0].amount.amount: ', params[0].amount.amount); - let assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); + const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Dash); if (!wallet) throw new Error('Failed to init swapkit'); @@ -117,16 +117,16 @@ export const handleDashRequest = async ( changeAddressIndex = changeAddressIndex.data.changeIndex; console.log(tag, 'changeAddressIndex: ', changeAddressIndex); - let path = `m/44'/5'/0'/1/${changeAddressIndex}`; + const path = DerivationPath['DASH'].replace('/0/0', `/1/${changeAddressIndex}`); console.log(tag, 'path: ', path); - let customAddressInfo = { + const customAddressInfo = { coin: 'Dash', script_type: 'p2pkh', address_n: bip32ToAddressNList(path), }; - let address = await wallet.getAddress(customAddressInfo); + const address = await wallet.getAddress(customAddressInfo); console.log('address: ', address); - let changeAddress = { + const changeAddress = { address: address, path: path, index: changeAddressIndex, @@ -134,16 +134,16 @@ export const handleDashRequest = async ( }; for (let i = 0; i < utxos.length; i++) { - let utxo = utxos[i]; + const utxo = utxos[i]; //@ts-ignore utxo.value = Number(utxo.value); } console.log('utxos: ', utxos); - let amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); + const amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); console.log(tag, 'amountOut: ', amountOut); - let effectiveFeeRate = 10; + const effectiveFeeRate = 10; console.log('utxos: ', utxos); const { inputs, outputs, fee } = coinSelect.default( utxos, @@ -154,7 +154,7 @@ export const handleDashRequest = async ( console.log('outputs: ', outputs); console.log('fee: ', fee); - let unsignedTx = await wallet.buildTx({ + const unsignedTx = await wallet.buildTx({ inputs, outputs, memo: 'test', @@ -162,11 +162,6 @@ export const handleDashRequest = async ( }); //push to front - requestInfo.inputs = unsignedTx.inputs; - requestInfo.outputs = unsignedTx.outputs; - requestInfo.memo = unsignedTx.memo; - requestInfo.utxos = utxos; - requestInfo.changeAddress = changeAddress; chrome.runtime.sendMessage({ action: 'utxo_build_tx', @@ -177,6 +172,7 @@ export const handleDashRequest = async ( console.log(tag, 'storedEvent: ', storedEvent); storedEvent.utxos = utxos; storedEvent.changeAddress = changeAddress; + storedEvent.unsignedTx = unsignedTx; await requestStorage.updateEventById(requestInfo.id, storedEvent); } catch (e) { console.error(e); @@ -186,13 +182,34 @@ export const handleDashRequest = async ( buildTx(); // Proceed with requiring approval without waiting for buildTx to resolve - const result = await requireApproval(networkId, requestInfo, 'bitcoin', method, params[0]); + const result = await requireApproval(networkId, requestInfo, 'dash', method, params[0]); console.log(tag, 'result:', result); - if (result.success && unsignedTx) { - // const signedTx = await wallet.signTransaction(unsignedTx.psbt, unsignedTx.inputs, unsignedTx.memo); - // const txid = await wallet.broadcastTx(signedTx); - return 'FAKETXIDBRO'; + const response = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'response: ', response); + + if (result.success && response.unsignedTx) { + const signedTx = await wallet.signTx( + response.unsignedTx.inputs, + response.unsignedTx.outputs, + response.unsignedTx.memo, + ); + + response.signedTx = signedTx; + await requestStorage.updateEventById(requestInfo.id, response); + + const txHash = await wallet.broadcastTx(signedTx); + + response.txid = txHash; + await requestStorage.updateEventById(requestInfo.id, response); + + //push event + chrome.runtime.sendMessage({ + action: 'transaction_complete', + txHash: txHash, + }); + + return txHash; } else { throw createProviderRpcError(4200, 'User denied transaction'); } diff --git a/chrome-extension/src/background/chains/dogecoinHandler.ts b/chrome-extension/src/background/chains/dogecoinHandler.ts index db99c38..24d065c 100644 --- a/chrome-extension/src/background/chains/dogecoinHandler.ts +++ b/chrome-extension/src/background/chains/dogecoinHandler.ts @@ -1,11 +1,16 @@ +import { bip32ToAddressNList } from '@pioneer-platform/pioneer-coins'; + const TAG = ' | dogecoinHandler | '; import type { JsonRpcProvider } from 'ethers'; -import { Chain } from '@coinmasters/types'; +import { Chain, DerivationPath } from '@coinmasters/types'; import { AssetValue } from '@pioneer-platform/helpers'; // @ts-ignore import { ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '@pioneer-platform/pioneer-caip'; // @ts-ignore import { v4 as uuidv4 } from 'uuid'; +import { requestStorage } from '@extension/storage/dist/lib'; +// @ts-ignore +import * as coinSelect from 'coinselect'; interface ProviderRpcError extends Error { code: number; @@ -52,77 +57,155 @@ export const handleDogecoinRequest = async ( //get sum of all pubkeys configured const balance = KEEPKEY_WALLET.balances.find((balance: any) => balance.caip === shortListSymbolToCaip['DOGE']); - //let pubkeys = await KEEPKEY_WALLET.swapKit.getBalance(Chain.Bitcoin); + //let pubkeys = await KEEPKEY_WALLET.swapKit.getBalance(Chain.Doge); console.log(tag, 'balance: ', balance); return [balance]; } case 'transfer': { - //@VERIFY: Xdefi conpatability! missing caip! - const caip = params[0].caip || shortListSymbolToCaip[params[0].asset.symbol]; - if (!caip) throw Error('Unalbe to determine caip from asset.symbol: ' + params[0].asset.symbol); + const caip = shortListSymbolToCaip['DOGE']; console.log(tag, 'caip: ', caip); const networkId = caipToNetworkId(caip); - console.log(tag, 'networkId: ', networkId); - requestInfo.id = uuidv4(); + console.log(tag, 'assetContext: ', KEEPKEY_WALLET); + // eslint-disable-next-line no-constant-condition if (!KEEPKEY_WALLET.assetContext) { + // Set context to the chain, defaults to ETH await KEEPKEY_WALLET.setAssetContext({ caip }); } - const pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => e.networks.includes(ChainToNetworkId[Chain.Dogecoin])); - const accounts = pubkeys.map((pubkey: any) => pubkey.master || pubkey.address); - console.log(tag, 'accounts: ', accounts); - if (!accounts.length) throw createProviderRpcError(4200, 'No accounts found'); + console.log(tag, 'pubkeys: ', pubkeys); + if (!pubkeys || pubkeys.length === 0) throw Error('Failed to locate pubkeys for chain ' + Chain.Dogecoin); + + console.log(tag, 'params[0]: ', params[0]); const assetString = 'DOGE.DOGE'; await AssetValue.loadStaticAssets(); + console.log(tag, 'params[0].amount.amount: ', params[0].amount.amount); + const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); + + const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Dogecoin); + if (!wallet) throw new Error('Failed to init swapkit'); + const walletAddress = await wallet.getAddress(); + console.log(tag, 'walletAddress: ', walletAddress); - const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount) / 100000000); const sendPayload = { - from: accounts[0], // Select preference change address + from: walletAddress, // Select preference change address assetValue, memo: params[0].memo || '', recipient: params[0].recipient, }; - // Start building the transaction but don't wait for it to finish - const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Dogecoin); - if (!wallet) throw new Error('Failed to init swapkit'); - console.log(tag, '** wallet:', wallet); - console.log('CHECKPOINT DOGE 1'); - const buildTxPromise = wallet - .buildTx(sendPayload) - .then(async unsignedTx => { - console.log(tag, 'unsignedTx', unsignedTx); - // Update requestInfo with transaction details after buildTx resolves - requestInfo.inputs = unsignedTx.inputs; - requestInfo.outputs = unsignedTx.outputs; - requestInfo.memo = unsignedTx.memo; + const buildTx = async function () { + try { + const utxos = []; + for (let i = 0; i < pubkeys.length; i++) { + const pubkey = pubkeys[i]; + let utxosResp = await KEEPKEY_WALLET.pioneer.ListUnspent({ network: 'DOGE', xpub: pubkey.pubkey }); + utxosResp = utxosResp.data; + console.log('utxosResp: ', utxosResp); + utxos.push(...utxosResp); + } + console.log(tag, 'utxos: ', utxos); + + //get new change address + let changeAddressIndex = await KEEPKEY_WALLET.pioneer.GetChangeAddress({ + network: 'DOGE', + xpub: pubkeys[0].pubkey || pubkeys[0].xpub, + }); + changeAddressIndex = changeAddressIndex.data.changeIndex; + console.log(tag, 'changeAddressIndex: ', changeAddressIndex); + + const path = DerivationPath['DOGE'].replace('/0/0', `/1/${changeAddressIndex}`); + console.log(tag, 'path: ', path); + const customAddressInfo = { + coin: 'Dogecoin', + script_type: 'p2pkh', + address_n: bip32ToAddressNList(path), + }; + const address = await wallet.getAddress(customAddressInfo); + console.log('address: ', address); + const changeAddress = { + address: address, + path: path, + index: changeAddressIndex, + addressNList: bip32ToAddressNList(path), + }; + + for (let i = 0; i < utxos.length; i++) { + const utxo = utxos[i]; + //@ts-ignore + utxo.value = Number(utxo.value); + } + console.log('utxos: ', utxos); + + const amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); + + console.log(tag, 'amountOut: ', amountOut); + const effectiveFeeRate = 10; + console.log('utxos: ', utxos); + const { inputs, outputs, fee } = coinSelect.default( + utxos, + [{ address: params[0].recipient, value: amountOut }], + effectiveFeeRate, + ); + console.log('inputs: ', inputs); + console.log('outputs: ', outputs); + console.log('fee: ', fee); + + const unsignedTx = await wallet.buildTx({ + inputs, + outputs, + memo: 'test', + changeAddress, + }); + //push to front chrome.runtime.sendMessage({ action: 'utxo_build_tx', - unsignedTx: unsignedTx, + unsignedTx: requestInfo, }); - const response = await requestStorage.getEventById(requestInfo.id); - response.unsignedTx = unsignedTx; - await requestStorage.updateEventById(requestInfo.id, response); - // Push an event to the front-end that UTXOs are found - // This could be something like: sendUpdateToFrontend('UTXOs found', unsignedTx); - }) - .catch(error => { - console.error('Error building the transaction:', error); - // Handle buildTx failure appropriately, such as notifying the user - }); + + const storedEvent = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'storedEvent: ', storedEvent); + storedEvent.utxos = utxos; + storedEvent.changeAddress = changeAddress; + storedEvent.unsignedTx = unsignedTx; + await requestStorage.updateEventById(requestInfo.id, storedEvent); + } catch (e) { + console.error(e); + } + }; + + buildTx(); // Proceed with requiring approval without waiting for buildTx to resolve const result = await requireApproval(networkId, requestInfo, 'dogecoin', method, params[0]); + console.log(tag, 'result:', result); + + const response = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'response: ', response); + + if (result.success && response.unsignedTx) { + const signedTx = await wallet.signTx( + response.unsignedTx.inputs, + response.unsignedTx.outputs, + response.unsignedTx.memo, + ); + + response.signedTx = signedTx; + await requestStorage.updateEventById(requestInfo.id, response); - // Wait for the buildTx to complete (if not already completed) before signing - const unsignedTx = await buildTxPromise; + const txHash = await wallet.broadcastTx(signedTx); + + response.txid = txHash; + await requestStorage.updateEventById(requestInfo.id, response); + + //push event + chrome.runtime.sendMessage({ + action: 'transaction_complete', + txHash: txHash, + }); - if (result.success && unsignedTx) { - const signedTx = await wallet.signTransaction(unsignedTx.psbt, unsignedTx.inputs, unsignedTx.memo); - const txid = await wallet.broadcastTx(signedTx); - return txid; + return txHash; } else { throw createProviderRpcError(4200, 'User denied transaction'); } diff --git a/chrome-extension/src/background/chains/litecoinHandler.ts b/chrome-extension/src/background/chains/litecoinHandler.ts index bbe999f..e5a15c0 100644 --- a/chrome-extension/src/background/chains/litecoinHandler.ts +++ b/chrome-extension/src/background/chains/litecoinHandler.ts @@ -1,6 +1,8 @@ +import { bip32ToAddressNList } from '@pioneer-platform/pioneer-coins'; + const TAG = ' | litecoinHandler | '; import { JsonRpcProvider } from 'ethers'; -import { Chain } from '@coinmasters/types'; +import { Chain, DerivationPath } from '@coinmasters/types'; import { AssetValue } from '@pioneer-platform/helpers'; import { EIP155_CHAINS } from '../chains'; // @ts-ignore @@ -11,6 +13,8 @@ import { ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '@pione // @ts-expect-error import { v4 as uuidv4 } from 'uuid'; import { requestStorage, web3ProviderStorage, assetContextStorage } from '@extension/storage'; +// @ts-ignore +import * as coinSelect from 'coinselect'; interface ProviderRpcError extends Error { code: number; @@ -57,29 +61,141 @@ export const handleLitecoinRequest = async ( const caip = shortListSymbolToCaip['LTC']; console.log(tag, 'caip: ', caip); const networkId = caipToNetworkId(caip); - //verify context is bitcoin + requestInfo.id = uuidv4(); + //push event to ux + chrome.runtime.sendMessage({ + action: 'TRANSACTION_CONTEXT_UPDATED', + id: requestInfo.id, + }); + + // eslint-disable-next-line no-constant-condition if (!KEEPKEY_WALLET.assetContext) { + // Set context to the chain, defaults to ETH await KEEPKEY_WALLET.setAssetContext({ caip }); } - // Require user approval - const result = await requireApproval(networkId, requestInfo, 'bitcoin', method, params[0]); - console.log(tag, 'result:', result); - //send tx - console.log(tag, 'params[0]: ', params[0]); - const assetString = 'LTC.LTC'; - await AssetValue.loadStaticAssets(); - console.log(tag, 'params[0].amount.amount: ', params[0].amount.amount); - const assetValue = await AssetValue.fromString(assetString, parseFloat(params[0].amount.amount)); - const sendPayload = { - from: params[0].from, - assetValue, - memo: params[0].memo || '', - recipient: params[0].recipient, + const pubkeys = KEEPKEY_WALLET.pubkeys.filter((e: any) => e.networks.includes(ChainToNetworkId[Chain.Litecoin])); + console.log(tag, 'pubkeys: ', pubkeys); + if (!pubkeys || pubkeys.length === 0) throw Error('Failed to locate pubkeys for chain ' + Chain.Litecoin); + + const wallet = await KEEPKEY_WALLET.swapKit.getWallet(Chain.Litecoin); + if (!wallet) throw new Error('Failed to init swapkit'); + const walletAddress = await wallet.getAddress(); + console.log(tag, 'walletAddress: ', walletAddress); + + const buildTx = async function () { + try { + const utxos = []; + for (let i = 0; i < pubkeys.length; i++) { + const pubkey = pubkeys[i]; + let utxosResp = await KEEPKEY_WALLET.pioneer.ListUnspent({ network: 'LTC', xpub: pubkey.pubkey }); + utxosResp = utxosResp.data; + console.log('utxosResp: ', utxosResp); + utxos.push(...utxosResp); + } + console.log(tag, 'utxos: ', utxos); + + //get new change address + let changeAddressIndex = await KEEPKEY_WALLET.pioneer.GetChangeAddress({ + network: 'LTC', + xpub: pubkeys[0].pubkey || pubkeys[0].xpub, + }); + changeAddressIndex = changeAddressIndex.data.changeIndex; + console.log(tag, 'changeAddressIndex: ', changeAddressIndex); + + const path = DerivationPath['LTC'].replace('/0/0', `/1/${changeAddressIndex}`); + console.log(tag, 'path: ', path); + const customAddressInfo = { + coin: 'Litecoin', + script_type: 'p2pkh', + address_n: bip32ToAddressNList(path), + }; + const address = await wallet.getAddress(customAddressInfo); + console.log('address: ', address); + const changeAddress = { + address: address, + path: path, + index: changeAddressIndex, + addressNList: bip32ToAddressNList(path), + }; + + for (let i = 0; i < utxos.length; i++) { + const utxo = utxos[i]; + //@ts-ignore + utxo.value = Number(utxo.value); + } + console.log('utxos: ', utxos); + + const amountOut: number = Math.floor(Number(params[0].amount.amount) * 1e8); + + console.log(tag, 'amountOut: ', amountOut); + const effectiveFeeRate = 10; + console.log('utxos: ', utxos); + const { inputs, outputs, fee } = coinSelect.default( + utxos, + [{ address: params[0].recipient, value: amountOut }], + effectiveFeeRate, + ); + console.log('inputs: ', inputs); + console.log('outputs: ', outputs); + console.log('fee: ', fee); + + const unsignedTx = await wallet.buildTx({ + inputs, + outputs, + memo: 'test', + changeAddress, + }); + //push to front + chrome.runtime.sendMessage({ + action: 'utxo_build_tx', + unsignedTx: requestInfo, + }); + + const storedEvent = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'storedEvent: ', storedEvent); + storedEvent.utxos = utxos; + storedEvent.changeAddress = changeAddress; + storedEvent.unsignedTx = unsignedTx; + await requestStorage.updateEventById(requestInfo.id, storedEvent); + } catch (e) { + console.error(e); + } }; - console.log(tag, 'sendPayload: ', sendPayload); - const txHash = await KEEPKEY_WALLET.swapKit.transfer(sendPayload); - console.log(tag, 'txHash: ', txHash); - return txHash; + + buildTx(); + + // Proceed with requiring approval without waiting for buildTx to resolve + const result = await requireApproval(networkId, requestInfo, 'litecoin', method, params[0]); + console.log(tag, 'result:', result); + + const response = await requestStorage.getEventById(requestInfo.id); + console.log(tag, 'response: ', response); + + if (result.success && response.unsignedTx) { + const signedTx = await wallet.signTx( + response.unsignedTx.inputs, + response.unsignedTx.outputs, + response.unsignedTx.memo, + ); + + response.signedTx = signedTx; + await requestStorage.updateEventById(requestInfo.id, response); + + const txHash = await wallet.broadcastTx(signedTx); + + response.txid = txHash; + await requestStorage.updateEventById(requestInfo.id, response); + + //push event + chrome.runtime.sendMessage({ + action: 'transaction_complete', + txHash: txHash, + }); + + return txHash; + } else { + throw createProviderRpcError(4200, 'User denied transaction'); + } } default: { console.log(tag, `Method ${method} not supported`); diff --git a/pages/popup/package.json b/pages/popup/package.json index b3ccb0b..314b4fe 100644 --- a/pages/popup/package.json +++ b/pages/popup/package.json @@ -30,7 +30,8 @@ "ethers": "^6.13.2", "framer-motion": "^11.5.4", "react-code-blocks": "^0.1.6", - "react-confetti": "^6.1.0" + "react-confetti": "^6.1.0", + "react-json-view": "^1.21.3" }, "devDependencies": { "@extension/tailwindcss-config": "workspace:*", diff --git a/pages/popup/src/components/TxidPage.tsx b/pages/popup/src/components/TxidPage.tsx index 9635d69..e289a6f 100644 --- a/pages/popup/src/components/TxidPage.tsx +++ b/pages/popup/src/components/TxidPage.tsx @@ -1,5 +1,17 @@ import React, { useEffect, useState } from 'react'; -import { Box, Text, Icon, Button, Card, CardBody, Divider, IconButton, Tooltip, useClipboard } from '@chakra-ui/react'; +import { + Box, + Text, + Icon, + Button, + Card, + CardBody, + Divider, + IconButton, + Tooltip, + useClipboard, + Flex, +} from '@chakra-ui/react'; import { CheckCircleIcon, CopyIcon } from '@chakra-ui/icons'; import Confetti from 'react-confetti'; @@ -17,48 +29,61 @@ const TxidPage = ({ txHash, explorerUrl }: { txHash: string; explorerUrl: string }, []); return ( - - - {/* Conditionally show confetti for 5 seconds */} - {showConfetti && } + + + + {/* Conditionally show confetti for 5 seconds */} + {showConfetti && } - {/* Success message with a green checkmark */} - + {/* Success message with a green checkmark */} + - - Success! Your transaction is complete. - - - + + Success! Your transaction is complete. + - {/* Display the transaction hash in a large format */} - - Transaction Hash: - + - - - {txHash} + {/* Display the transaction hash in a large format */} + + Transaction Hash: - {/* Copy to clipboard button */} - - } - onClick={onCopy} - size="sm" - colorScheme={hasCopied ? 'green' : 'gray'} - /> - - + + + {txHash} + + + {/* Copy to clipboard button */} + + } + onClick={onCopy} + size="sm" + colorScheme={hasCopied ? 'green' : 'gray'} + /> + + - {/* Button to view the transaction on the explorer */} - - View on Explorer - - - + {/* Button to view the transaction on the explorer */} + + View on Explorer + + + + ); }; diff --git a/pages/popup/src/components/utxo/ProjectFeeCard.tsx b/pages/popup/src/components/utxo/ProjectFeeCard.tsx index 93e82b7..1ef2118 100644 --- a/pages/popup/src/components/utxo/ProjectFeeCard.tsx +++ b/pages/popup/src/components/utxo/ProjectFeeCard.tsx @@ -10,7 +10,6 @@ import { Card, CardBody, Divider, - Badge, Table, Tbody, Tr, @@ -88,8 +87,8 @@ const ProjectFeeCard = ({ transaction }) => { const toast = useToast(); const recommendedFees = getRecommendedFees(); - const inputs = transaction.unsignedTx.inputs; - const outputs = transaction.unsignedTx.outputs; + const inputs = transaction?.unsignedTx?.inputs || []; + const outputs = transaction?.unsignedTx?.outputs || []; // Calculate estimated transaction size useEffect(() => { @@ -180,7 +179,7 @@ const ProjectFeeCard = ({ transaction }) => { }; return ( - + @@ -222,10 +221,10 @@ const ProjectFeeCard = ({ transaction }) => { - + - Bitcoin Price + Asset Price ${btcPrice.toLocaleString()} USD diff --git a/pages/popup/src/components/utxo/RequestDataCard.tsx b/pages/popup/src/components/utxo/RequestDataCard.tsx index 977e7b4..bb2de33 100644 --- a/pages/popup/src/components/utxo/RequestDataCard.tsx +++ b/pages/popup/src/components/utxo/RequestDataCard.tsx @@ -1,5 +1,5 @@ import { Box, Heading, IconButton, Button, Spinner } from '@chakra-ui/react'; -import { CodeBlock, codepen } from 'react-code-blocks'; +import ReactJson from 'react-json-view'; import { useState } from 'react'; import { ChevronDownIcon, ChevronRightIcon } from '@chakra-ui/icons'; import { requestStorage } from '@extension/storage'; // Import the requestStorage @@ -68,13 +68,17 @@ export default function RequestDataCard({ transaction }: any) { )} - {/* Conditionally render the code block */} + {/* Render the collapsible JSON viewer */} - diff --git a/pages/popup/src/components/utxo/RequestDetailsCard.tsx b/pages/popup/src/components/utxo/RequestDetailsCard.tsx index b9f16d3..53ff1cb 100644 --- a/pages/popup/src/components/utxo/RequestDetailsCard.tsx +++ b/pages/popup/src/components/utxo/RequestDetailsCard.tsx @@ -1,12 +1,45 @@ -import { useState } from 'react'; -import { Box, Divider, Flex, Table, Tbody, Tr, Td, Badge, Fragment } from '@chakra-ui/react'; +import { useState, useEffect } from 'react'; +import { Box, Divider, Flex, Table, Tbody, Tr, Td, Badge, Avatar } from '@chakra-ui/react'; export default function RequestDetailsCard({ transaction }: any) { const [isNative, setIsNative] = useState(true); // Toggle for hex/native + const [assetContext, setAssetContext] = useState(null); + + // Function to get asset context + const requestAssetContext = () => { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ type: 'GET_ASSET_CONTEXT' }, response => { + if (chrome.runtime.lastError) { + return reject(chrome.runtime.lastError); + } + resolve(response); + }); + }); + }; + + // Fetch the asset context on component mount + useEffect(() => { + const fetchAssetContext = async () => { + try { + const context: any = await requestAssetContext(); + setAssetContext(context); + } catch (error) { + console.error('Failed to get asset context:', error); + } + }; + + fetchAssetContext(); + }, []); return ( + {/* Display the Avatar for the asset */} + {assetContext && ( + + + + )} @@ -20,7 +53,6 @@ export default function RequestDetailsCard({ transaction }: any) { Amount: - {/* Using formatAmount to handle cases where amount is an object */} {transaction?.request?.amount.amount || 'N/A'} diff --git a/pages/popup/src/components/utxo/index.tsx b/pages/popup/src/components/utxo/index.tsx index f044be1..98061ea 100644 --- a/pages/popup/src/components/utxo/index.tsx +++ b/pages/popup/src/components/utxo/index.tsx @@ -24,60 +24,74 @@ import RequestMethodCard from './RequestMethodCard'; import ProjectFeeCard from './ProjectFeeCard'; import ProjectInfoCard from './ProjectInfoCard'; +const openSidebar = () => { + chrome.runtime.sendMessage({ type: 'OPEN_SIDEBAR' }, response => { + if (response?.success) { + console.log('Sidebar opened successfully'); + } else { + console.error('Failed to open sidebar:', response?.error); + } + }); +}; + +const triggerTransactionContextUpdate = (transactionId: string) => { + chrome.runtime.sendMessage({ type: 'TRANSACTION_CONTEXT_UPDATED', id: transactionId }, response => { + if (response?.success) { + console.log(`Transaction context updated for ID: ${transactionId}`); + } else { + console.error('Failed to update transaction context:', response?.error); + } + }); +}; + export function UtxoTransaction({ transaction: initialTransaction, handleResponse }: any) { - // Initialize transaction state from prop const [transaction, setTransaction] = useState(initialTransaction); const [isLoading, setIsLoading] = useState(true); const [errorMessage, setErrorMessage] = useState(null); const [showMessage1, setShowMessage1] = useState(false); const [showMessage2, setShowMessage2] = useState(false); - // Delay the appearance of messages useEffect(() => { - setTimeout(() => setShowMessage1(true), 3000); // Show first message after 3 seconds - setTimeout(() => setShowMessage2(true), 6000); // Show second message after 6 seconds + setTimeout(() => setShowMessage1(true), 3000); + setTimeout(() => setShowMessage2(true), 6000); }, []); - // Fetch the unsigned transaction and stop loading when it's populated const fetchTransactionData = async (id: string) => { try { console.log(`Fetching transaction with id: ${id}`); const data = await requestStorage.getEventById(id); - console.log('Fetched transaction data:', data); // Log the data for debugging + console.log('Fetched transaction data:', data); if (data.utxos) { - setTransaction(data); // Update transaction state - setIsLoading(false); // Stop spinner when data is populated + setTransaction(data); + setIsLoading(false); } else { console.log('No unsigned transaction found in data.'); } } catch (error: any) { console.error('Error fetching transaction from storage:', error); setErrorMessage('Error loading transaction: ' + error.message); - setIsLoading(false); // Stop spinner if there is an error + setIsLoading(false); } }; useEffect(() => { - // Fetch transaction data when the component mounts fetchTransactionData(transaction.id); }, [transaction.id]); - // Listen for events that update the transaction state useEffect(() => { const handleMessage = (message: any) => { console.log('Message received:', message); if (message.action === 'utxo_build_tx') { console.log('Received utxo_build_tx event:', message); - // Add 1-second delay before fetching updated transaction data setTimeout(async () => { - await fetchTransactionData(transaction.id); // Fetch updated transaction + await fetchTransactionData(transaction.id); }, 1000); } else if (message.action === 'transaction_error') { const errorDetails = message.e?.message || JSON.stringify(message.e); setErrorMessage('Transaction failed: ' + errorDetails); - setIsLoading(false); // Stop spinner in case of error + setIsLoading(false); } }; @@ -88,11 +102,12 @@ export function UtxoTransaction({ transaction: initialTransaction, handleRespons }; }, [transaction.id]); - // Reload function to refetch the transaction without refreshing the page const handleReload = () => { console.log('Reloading transaction with id:', transaction.id); - setIsLoading(true); // Show the loading spinner again + setIsLoading(true); fetchTransactionData(transaction.id); + openSidebar(); + triggerTransactionContextUpdate(transaction.id); // Force the sidebar into the transaction history tab }; if (isLoading) { @@ -101,16 +116,16 @@ export function UtxoTransaction({ transaction: initialTransaction, handleRespons - Transaction ID: {transaction.id} {/* Show transaction ID while loading */} + Transaction ID: {transaction.id} {showMessage1 && Building transaction...} {showMessage2 && Applying UTXO selection method blackjack...} - Reload + View in Sidebar {errorMessage && {errorMessage}} - {/* Add bottom buffer */} + ); } @@ -120,44 +135,29 @@ export function UtxoTransaction({ transaction: initialTransaction, handleRespons - Basic - {/*Fees*/} - {/*Coin Control*/} - raw - {/*UTXO*/} + Fees + Raw - - {/**/} - {/* */} - {/**/} - - {/**/} - {/* */} - {/**/} - - {/**/} - {/* */} - {/**/} - + + + - - handleResponse('accept')} mr={2}> Approve diff --git a/pages/side-panel/src/SidePanel.tsx b/pages/side-panel/src/SidePanel.tsx index 4366b9a..cd2167a 100644 --- a/pages/side-panel/src/SidePanel.tsx +++ b/pages/side-panel/src/SidePanel.tsx @@ -39,6 +39,7 @@ const SidePanel = () => { const [keepkeyState, setKeepkeyState] = useState(null); const [assetContext, setAssetContext] = useState(null); const [transactionContext, setTransactionContext] = useState(null); + const [showBack, setShowBack] = useState(false); const [isConnecting, setIsConnecting] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); @@ -69,10 +70,12 @@ const SidePanel = () => { } if (message.type === 'ASSET_CONTEXT_UPDATED' && message.assetContext) { setAssetContext(message.assetContext); + setShowBack(true); } if (message.type === 'TRANSACTION_CONTEXT_UPDATED' && message.id) { console.log('TRANSACTION_CONTEXT_UPDATED', message.id); setTransactionContext(message.id); // Show Activity page on transaction event + setShowBack(true); // Ensure the "Back" button is shown } }; @@ -120,6 +123,37 @@ const SidePanel = () => { } }; + const handleSettingsClick = () => { + if (showBack) { + // Clear assetContext on the frontend + setAssetContext(null); + setTransactionContext(null); + setShowBack(false); // Hide the back button + + // Clear assetContext on the backend + chrome.runtime.sendMessage({ type: 'CLEAR_ASSET_CONTEXT' }, response => { + if (response?.success) { + console.log('Asset context cleared on backend'); + } else { + console.error('Failed to clear asset context on backend:', response?.error); + } + }); + } else { + // Open settings + onSettingsOpen(); + setShowBack(true); // Show the back button when settings are opened + } + }; + + const handleTransactionsClick = () => { + try { + setTransactionContext('none'); // Switch to the transaction context + setShowBack(true); // Show the back button + } catch (e) { + console.error(e); + } + }; + return ( @@ -130,33 +164,31 @@ const SidePanel = () => { {/* Row with left-aligned, centered, and right-aligned buttons */} - {/* Left-aligned button (Settings) */} - } aria-label="Settings" onClick={onSettingsOpen} /> + {/* Left-aligned button (Settings or Back depending on showBack) */} + : } + aria-label={showBack ? 'Back' : 'Settings'} + onClick={handleSettingsClick} + /> {/* Center-aligned Activity button */} } // Activity Icon aria-label="Activity" - onClick={() => setTransactionContext('none')} + onClick={handleTransactionsClick} // Handle transaction context /> - {/*{keepkeyState === 5 && (*/} - {/* */} - {/* } // Activity Icon*/} - {/* aria-label="Activity"*/} - {/* onClick={() => setTransactionContext([])}*/} - {/* />*/} - {/* */} - {/*)}*/} {/* Right-aligned button (Refresh) */} } aria-label="Refresh" onClick={() => refreshBalances()} /> + + {/* Render the appropriate content */} {renderContent()} + {/* Modal for Settings */} diff --git a/pages/side-panel/src/components/Balances.tsx b/pages/side-panel/src/components/Balances.tsx index 17726d4..1aedf8e 100644 --- a/pages/side-panel/src/components/Balances.tsx +++ b/pages/side-panel/src/components/Balances.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Flex, Spinner, Avatar, Box, Text, Badge, Card, Stack, Button } from '@chakra-ui/react'; import AssetSelect from './AssetSelect'; // Import AssetSelect component -const Balances = () => { +const Balances = ({ setShowBack }: any) => { const [balances, setBalances] = useState([]); const [assets, setAssets] = useState([]); const [assetContext, setAssetContext] = useState(null); @@ -65,6 +65,7 @@ const Balances = () => { } if (response && response.assetContext) { setAssetContext(response.assetContext); + setShowBack(true); } }); }; diff --git a/pages/side-panel/src/components/Transfer.tsx b/pages/side-panel/src/components/Transfer.tsx index 7ff265d..5a49e73 100644 --- a/pages/side-panel/src/components/Transfer.tsx +++ b/pages/side-panel/src/components/Transfer.tsx @@ -12,7 +12,6 @@ import { Spinner, Text, VStack, - Tooltip, useToast, useColorModeValue, Modal, @@ -24,7 +23,6 @@ import { ModalFooter, useDisclosure, } from '@chakra-ui/react'; -import { ChevronDownIcon, ChevronUpIcon, InfoOutlineIcon } from '@chakra-ui/icons'; import React, { useCallback, useEffect, useState } from 'react'; import { NetworkIdToChain } from '@pioneer-platform/pioneer-caip'; import { COIN_MAP_KEEPKEY_LONG } from '@pioneer-platform/pioneer-coins'; @@ -33,9 +31,9 @@ import confetti from 'canvas-confetti'; // Make sure to install the confetti pac const TAG = ' | Transfer | '; -const convertToHex = amountInEther => { +const convertToHex = (amountInEther: string) => { const weiMultiplier = BigInt(1e18); // 1 Ether = 1e18 Wei - const amountInWei = BigInt(parseFloat(amountInEther) * 1e18); // Convert Ether to Wei + const amountInWei = BigInt(parseFloat(amountInEther || '0') * 1e18); // Convert Ether to Wei // Convert the amount in Wei to a hex string return '0x' + amountInWei.toString(16); @@ -44,9 +42,8 @@ const convertToHex = amountInEther => { export function Transfer({}: any): JSX.Element { const toast = useToast(); const [isSubmitting, setIsSubmitting] = useState(false); - const [inputAmount, setInputAmount] = useState(0); - const [inputAmountUsd, setInputAmountUsd] = useState(0); - const [sendAmount, setSendAmount] = useState(); + const [inputAmount, setInputAmount] = useState(''); // Initialize as an empty string + const [inputAmountUsd, setInputAmountUsd] = useState(''); // Initialize as an empty string const [showAdvanced, setShowAdvanced] = useState(false); const [memo, setMemo] = useState(''); const [assetContext, setAssetContext] = useState({}); @@ -73,7 +70,6 @@ export function Transfer({}: any): JSX.Element { }, []); const onStart = async function () { - const tag = TAG + ' | onStart Transfer | '; chrome.runtime.sendMessage({ type: 'GET_MAX_SPENDABLE' }, maxSpendableResponse => { if (maxSpendableResponse && maxSpendableResponse.maxSpendable) { setMaxSpendable(maxSpendableResponse.maxSpendable); @@ -95,15 +91,43 @@ export function Transfer({}: any): JSX.Element { onStart(); }, []); + const handleInputFocus = () => { + // Optional: Clear input when focusing + // setInputAmount(''); + // setInputAmountUsd(''); + }; + const handleInputChange = (event: React.ChangeEvent) => { const value = event.target.value; + setIsMax(false); // Reset isMax if user manually changes input + if (useUsdInput) { setInputAmountUsd(value); - setInputAmount((parseFloat(value) / (priceUsd || 1)).toFixed(8)); + + const parsedValue = parseFloat(value); + if (!isNaN(parsedValue) && priceUsd) { + setInputAmount((parsedValue / priceUsd).toFixed(4)); // 4 decimal places for NATIVE + } else { + setInputAmount(''); + } } else { setInputAmount(value); - setInputAmountUsd((parseFloat(value) * (priceUsd || 1)).toFixed(2)); + + const parsedValue = parseFloat(value); + if (!isNaN(parsedValue) && priceUsd) { + setInputAmountUsd((parsedValue * priceUsd).toFixed(2)); // 2 decimal places for USD + } else { + setInputAmountUsd(''); + } + } + }; + + const handleInputBlur = () => { + if (!useUsdInput && inputAmount && !isNaN(parseFloat(inputAmount))) { + setInputAmount(parseFloat(inputAmount).toFixed(4)); // 4 decimal places for NATIVE + } else if (useUsdInput && inputAmountUsd && !isNaN(parseFloat(inputAmountUsd))) { + setInputAmountUsd(parseFloat(inputAmountUsd).toFixed(2)); // 2 decimal places for USD } }; @@ -111,14 +135,6 @@ export function Transfer({}: any): JSX.Element { setRecipient(event.target.value); }; - useEffect(() => { - if (useUsdInput) { - setInputAmountUsd((parseFloat(inputAmount) * (priceUsd || 1)).toFixed(2)); - } else { - setInputAmount((parseFloat(inputAmountUsd) / (priceUsd || 1)).toFixed(8)); - } - }, [useUsdInput, priceUsd]); - const handleSend = useCallback(async () => { try { if (!inputAmount || !recipient) { @@ -134,11 +150,27 @@ export function Transfer({}: any): JSX.Element { isMax: isMax, }; - let chain = assetContext?.networkId.includes('eip155') - ? 'ethereum' - : NetworkIdToChain[assetContext?.networkId].toLowerCase(); - - chain = COIN_MAP_KEEPKEY_LONG[chain.toUpperCase()].toLowerCase(); + let chain; + if (assetContext?.networkId) { + if (assetContext.networkId.includes('eip155')) { + chain = 'ethereum'; + } else { + const chainFromNetworkId = NetworkIdToChain[assetContext.networkId]; + if (chainFromNetworkId) { + chain = chainFromNetworkId.toLowerCase(); + const coinMapEntry = COIN_MAP_KEEPKEY_LONG[chain.toUpperCase()]; + if (coinMapEntry) { + chain = coinMapEntry.toLowerCase(); + } else { + throw new Error('Unsupported chain' + chain); + } + } else { + throw new Error('Unsupported network ID'); + } + } + } else { + throw new Error('Network ID is undefined'); + } const requestInfo = { method: 'transfer', @@ -175,6 +207,7 @@ export function Transfer({}: any): JSX.Element { }, ); } catch (error) { + console.error(error); toast({ title: 'Error', description: error.toString(), @@ -190,53 +223,13 @@ export function Transfer({}: any): JSX.Element { const setMaxAmount = () => { const maxAmount = maxSpendable; - setInputAmount(maxAmount); - setInputAmountUsd((parseFloat(maxAmount) * (priceUsd || 1)).toFixed(2)); + setInputAmount(parseFloat(maxAmount).toFixed(4)); // 4 decimal places for NATIVE + setInputAmountUsd((parseFloat(maxAmount) * (priceUsd || 1)).toFixed(2)); // 2 decimal places for USD setIsMax(true); // Set isMax to true when Max button is clicked }; const formatMaxSpendable = (amount: string) => { - const [integerPart, fractionalPart] = amount.toString().split('.'); - let formattedIntegerPart; - if (integerPart.length > 4) { - formattedIntegerPart = ( - <> - {integerPart.slice(0, 4)} - - {integerPart.slice(4)} - - > - ); - } else { - formattedIntegerPart = {integerPart}; - } - let formattedFractionalPart; - if (fractionalPart) { - if (fractionalPart.length > 4) { - formattedFractionalPart = ( - <> - .{fractionalPart.slice(0, 4)} - - {fractionalPart.slice(4)} - - > - ); - } else { - formattedFractionalPart = ( - <> - .{fractionalPart} - > - ); - } - } else { - formattedFractionalPart = null; - } - return ( - <> - {formattedIntegerPart} - {formattedFractionalPart} - > - ); + return parseFloat(amount).toFixed(4); }; if (loadingMaxSpendable) { @@ -290,7 +283,9 @@ export function Transfer({}: any): JSX.Element { setUseUsdInput(!useUsdInput)}> @@ -303,7 +298,12 @@ export function Transfer({}: any): JSX.Element { - + {isSubmitting ? 'Sending...' : 'Send'} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23a2371..9977bfb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,13 +107,13 @@ importers: version: 4.7.65 '@coinmasters/pioneer-sdk': specifier: ^4.7.70 - version: 4.7.70(@pioneer-platform/helpers@4.0.12)(@types/node@18.19.54) + version: 4.7.70(@pioneer-platform/helpers@4.0.12)(@types/node@20.16.10) '@coinmasters/types': specifier: ^4.7.66 version: 4.7.66 '@coinmasters/wallet-keepkey': specifier: ^13.0.70 - version: 13.0.70(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-ripple@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@keepkey/keepkey-sdk@0.2.57)(@types/node@18.19.54)(bitcoinjs-lib@5.2.0) + version: 13.0.70(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-ripple@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@keepkey/keepkey-sdk@0.2.57)(@types/node@20.16.10)(bitcoinjs-lib@5.2.0) '@extension/shared': specifier: workspace:* version: link:../packages/shared @@ -128,7 +128,7 @@ importers: version: 9.2.32 '@pioneer-platform/pioneer-coins': specifier: ^9.2.23 - version: 9.2.23(@types/node@18.19.54) + version: 9.2.23(@types/node@20.16.10) axios: specifier: ^1.7.7 version: 1.7.7 @@ -159,7 +159,7 @@ importers: version: link:../packages/vite-config '@laynezh/vite-plugin-lib-assets': specifier: ^0.5.24 - version: 0.5.24(vite@5.4.8) + version: 0.5.24(vite@5.4.3) '@types/ws': specifier: ^8.5.12 version: 8.5.12 @@ -174,7 +174,7 @@ importers: version: 0.30.11 ts-loader: specifier: ^9.5.1 - version: 9.5.1(typescript@5.6.2)(webpack@5.95.0) + version: 9.5.1(typescript@5.5.4)(webpack@5.95.0) packages/dev-utils: devDependencies: @@ -207,7 +207,7 @@ importers: version: 4.21.3 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.7.4)(typescript@5.6.2) + version: 10.9.2(@types/node@20.16.10)(typescript@5.5.4) ws: specifier: 8.18.0 version: 8.18.0 @@ -266,7 +266,7 @@ importers: version: link:../tsconfig '@vitejs/plugin-react-swc': specifier: ^3.6.0 - version: 3.7.0(vite@5.4.8) + version: 3.7.0(vite@5.4.3) deepmerge: specifier: ^4.3.1 version: 4.3.1 @@ -458,6 +458,9 @@ importers: react-confetti: specifier: ^6.1.0 version: 6.1.0(react@18.3.1) + react-json-view: + specifier: ^1.21.3 + version: 1.21.3(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) devDependencies: '@extension/tailwindcss-config': specifier: workspace:* @@ -506,7 +509,7 @@ importers: version: 9.2.32 '@pioneer-platform/pioneer-coins': specifier: ^9.2.23 - version: 9.2.23(@types/node@22.7.4) + version: 9.2.23(@types/node@20.16.10) axios: specifier: ^1.7.7 version: 1.7.7 @@ -1788,7 +1791,7 @@ packages: ky: 1.1.3 dev: false - /@coinmasters/core@11.0.70(@coinmasters/api@3.7.70)(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@types/node@18.19.54): + /@coinmasters/core@11.0.70(@coinmasters/api@3.7.70)(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@types/node@20.16.10): resolution: {integrity: sha512-UheiAR8c8UczYSDyQOAbbAb6KDGKDIA5TW9C1BuckDrtlfGc7Pcmj9FaAO8TcsDMlpKhNn/ngH1NhR/PDI4k2A==} peerDependencies: '@coinmasters/api': 3.7.70 @@ -1802,7 +1805,7 @@ packages: '@coinmasters/toolbox-utxo': 11.0.70 '@coinmasters/types': 4.7.70 '@pioneer-platform/helpers': 4.0.12 - '@pioneer-platform/loggerdog': 8.3.1(@types/node@18.19.54) + '@pioneer-platform/loggerdog': 8.3.1(@types/node@20.16.10) '@pioneer-platform/pioneer-caip': 9.2.32 transitivePeerDependencies: - '@swc/core' @@ -1816,22 +1819,22 @@ packages: idb: 8.0.0 dev: false - /@coinmasters/pioneer-sdk@4.7.70(@pioneer-platform/helpers@4.0.12)(@types/node@18.19.54): + /@coinmasters/pioneer-sdk@4.7.70(@pioneer-platform/helpers@4.0.12)(@types/node@20.16.10): resolution: {integrity: sha512-5qlFmEYhMRt3uNg8re9bdejVORuBdAdYHidqtcTeHX/XhqQvfQgOMDHX1fAqfWPYVpa4eIpuFB6g94Rz/nPlJQ==} dependencies: '@coinmasters/api': 3.7.70 - '@coinmasters/core': 11.0.70(@coinmasters/api@3.7.70)(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@types/node@18.19.54) + '@coinmasters/core': 11.0.70(@coinmasters/api@3.7.70)(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@types/node@20.16.10) '@coinmasters/tokens': 3.7.70 '@coinmasters/toolbox-cosmos': 11.0.70(@pioneer-platform/helpers@4.0.12) '@coinmasters/toolbox-evm': 11.0.70(@pioneer-platform/helpers@4.0.12) '@coinmasters/toolbox-ripple': 11.0.70(@pioneer-platform/helpers@4.0.12) '@coinmasters/toolbox-utxo': 11.0.70 '@coinmasters/types': 4.7.70 - '@pioneer-platform/loggerdog': 8.3.1(@types/node@18.19.54) + '@pioneer-platform/loggerdog': 8.3.1(@types/node@20.16.10) '@pioneer-platform/pioneer-caip': 9.2.32 '@pioneer-platform/pioneer-client': 9.2.5 - '@pioneer-platform/pioneer-coins': 9.2.23(@types/node@18.19.54) - '@pioneer-platform/pioneer-discovery': 0.0.15(@types/node@18.19.54) + '@pioneer-platform/pioneer-coins': 9.2.23(@types/node@20.16.10) + '@pioneer-platform/pioneer-discovery': 0.0.15(@types/node@20.16.10) transitivePeerDependencies: - '@pioneer-platform/helpers' - '@swc/core' @@ -1956,7 +1959,7 @@ packages: resolution: {integrity: sha512-qdL9V69/XneqRzXI0Hx23tnBQcEC/fGDntVXe5jwxTnnBYKeztY14SyHxSlh+PNUZe87ZHfVzObCWHbAIrUr2A==} dev: false - /@coinmasters/wallet-keepkey@13.0.70(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-ripple@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@keepkey/keepkey-sdk@0.2.57)(@types/node@18.19.54)(bitcoinjs-lib@5.2.0): + /@coinmasters/wallet-keepkey@13.0.70(@coinmasters/toolbox-cosmos@11.0.70)(@coinmasters/toolbox-evm@11.0.70)(@coinmasters/toolbox-ripple@11.0.70)(@coinmasters/toolbox-utxo@11.0.70)(@keepkey/keepkey-sdk@0.2.57)(@types/node@20.16.10)(bitcoinjs-lib@5.2.0): resolution: {integrity: sha512-rw5oe9gBmF8SK9sxlJ7jRnUq53lVtXfO6CA7RVjIU+m5arrgS35BtBGn+ef3UJGXEY9dJhZhhsKvj8OyyZkAUw==} peerDependencies: '@coinmasters/toolbox-cosmos': 11.0.70 @@ -1975,9 +1978,9 @@ packages: '@cosmjs/math': 0.32.4 '@cosmjs/stargate': 0.31.1 '@keepkey/keepkey-sdk': 0.2.57 - '@pioneer-platform/loggerdog': 8.3.1(@types/node@18.19.54) + '@pioneer-platform/loggerdog': 8.3.1(@types/node@20.16.10) '@pioneer-platform/pioneer-caip': 9.2.32 - '@pioneer-platform/pioneer-coins': 9.2.23(@types/node@18.19.54) + '@pioneer-platform/pioneer-coins': 9.2.23(@types/node@20.16.10) '@types/bchaddrjs': 0.4.0 bchaddrjs: 0.5.2 bitcoinjs-lib: 5.2.0 @@ -3152,7 +3155,7 @@ packages: resolution: {integrity: sha512-II6FT7CDuvp0LxIrwMyGRNvR5bxxxtgVzNrHvDrlJj1c+tzF3yK7Mkgz+rUO/KKhm3NSgRaEUVdph8sgj6XYng==} dev: false - /@laynezh/vite-plugin-lib-assets@0.5.24(vite@5.4.8): + /@laynezh/vite-plugin-lib-assets@0.5.24(vite@5.4.3): resolution: {integrity: sha512-z1M8reWte/xP7xw+aFN0Euh9OinCjCzJ8HhLkvpz+6h6MSTyyA54xrxGmECx5QNd3bCwQlkGd7WAgkozjublCg==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -3161,7 +3164,7 @@ packages: loader-utils: 3.3.1 mrmime: 1.0.1 semver: 7.6.3 - vite: 5.4.8(@types/node@18.19.54) + vite: 5.4.3(@types/node@20.16.10) dev: true /@noble/curves@1.2.0: @@ -3233,11 +3236,11 @@ packages: - '@types/node' dev: false - /@pioneer-platform/loggerdog@8.3.1(@types/node@22.7.4): + /@pioneer-platform/loggerdog@8.3.1(@types/node@20.16.10): resolution: {integrity: sha512-0JTucwEb1nG+MrPBRaIEdj9ptzjKatphh75/Zc3rJ19qJI9gUUZVx6PgjMYIF9cdi0saqytz291WRgge7qOW5g==} dependencies: os: 0.1.2 - ts-node: 10.9.2(@types/node@22.7.4)(typescript@5.6.2) + ts-node: 10.9.2(@types/node@20.16.10)(typescript@5.6.2) typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -3257,10 +3260,10 @@ packages: - debug dev: false - /@pioneer-platform/pioneer-coins@9.2.23(@types/node@18.19.54): + /@pioneer-platform/pioneer-coins@9.2.23(@types/node@20.16.10): resolution: {integrity: sha512-4UET5BANaOcMaYOhr9I//+6iIl2ffryljoxhqZDS6wb0HWPOqj/46oOcJ1/dt24JEr8+jJJxGWTVX36nCzwvyQ==} dependencies: - '@pioneer-platform/loggerdog': 8.3.1(@types/node@18.19.54) + '@pioneer-platform/loggerdog': 8.3.1(@types/node@20.16.10) bignumber.js: 9.1.2 bs58check: 3.0.1 transitivePeerDependencies: @@ -3269,24 +3272,12 @@ packages: - '@types/node' dev: false - /@pioneer-platform/pioneer-coins@9.2.23(@types/node@22.7.4): - resolution: {integrity: sha512-4UET5BANaOcMaYOhr9I//+6iIl2ffryljoxhqZDS6wb0HWPOqj/46oOcJ1/dt24JEr8+jJJxGWTVX36nCzwvyQ==} - dependencies: - '@pioneer-platform/loggerdog': 8.3.1(@types/node@22.7.4) - bignumber.js: 9.1.2 - bs58check: 3.0.1 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - dev: false - - /@pioneer-platform/pioneer-discovery@0.0.15(@types/node@18.19.54): + /@pioneer-platform/pioneer-discovery@0.0.15(@types/node@20.16.10): resolution: {integrity: sha512-4o5g7cmf/bx+pzE9i+qC/QyCRv5oyqO9KzH199hr2fXwcpsfiMAjIXMWC0akasVXjsSWdJ6EnWeY6fWifwpDLg==} dependencies: '@coinmasters/tokens': 3.7.70 '@coinmasters/types': 4.7.66 - '@pioneer-platform/loggerdog': 8.3.1(@types/node@18.19.54) + '@pioneer-platform/loggerdog': 8.3.1(@types/node@20.16.10) '@pioneer-platform/pioneer-caip': 9.2.32 transitivePeerDependencies: - '@swc/core' @@ -4381,12 +4372,12 @@ packages: resolution: {integrity: sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==} dependencies: undici-types: 5.26.5 + dev: false /@types/node@20.16.10: resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==} dependencies: undici-types: 6.19.8 - dev: true /@types/node@22.7.4: resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} @@ -4605,13 +4596,13 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitejs/plugin-react-swc@3.7.0(vite@5.4.8): + /@vitejs/plugin-react-swc@3.7.0(vite@5.4.3): resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} peerDependencies: vite: ^4 || ^5 dependencies: '@swc/core': 1.7.26 - vite: 5.4.8(@types/node@20.16.10) + vite: 5.4.3(@types/node@20.16.10) transitivePeerDependencies: - '@swc/helpers' dev: true @@ -5284,6 +5275,10 @@ packages: is-shared-array-buffer: 1.0.3 dev: true + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: false + /ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} dev: true @@ -5430,6 +5425,10 @@ packages: resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} dev: false + /base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + dev: false + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -6073,6 +6072,14 @@ packages: cross-spawn: 7.0.3 dev: true + /cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -7334,6 +7341,32 @@ packages: format: 0.2.2 dev: false + /fbemitter@3.0.0: + resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} + dependencies: + fbjs: 3.0.5 + transitivePeerDependencies: + - encoding + dev: false + + /fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + dev: false + + /fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + dependencies: + cross-fetch: 3.1.8 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.39 + transitivePeerDependencies: + - encoding + dev: false + /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: @@ -7429,6 +7462,18 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true + /flux@4.0.4(react@18.3.1): + resolution: {integrity: sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==} + peerDependencies: + react: ^15.0.2 || ^16.0.0 || ^17.0.0 + dependencies: + fbemitter: 3.0.0 + fbjs: 3.0.5 + react: 18.3.1 + transitivePeerDependencies: + - encoding + dev: false + /focus-lock@1.3.5: resolution: {integrity: sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==} engines: {node: '>=10'} @@ -8428,7 +8473,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 22.7.4 + '@types/node': 20.16.10 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -8660,10 +8705,18 @@ packages: requiresBuild: true dev: true + /lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + dev: false + /lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} dev: true + /lodash.flow@3.5.0: + resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} + dev: false + /lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} requiresBuild: true @@ -8996,6 +9049,18 @@ packages: web-streams-polyfill: 3.3.3 dev: false + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + /node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -9582,6 +9647,12 @@ packages: engines: {node: '>=0.4.0'} dev: true + /promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + dependencies: + asap: 2.0.6 + dev: false + /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -9672,6 +9743,10 @@ packages: engines: {node: '>=6'} dev: true + /pure-color@1.3.0: + resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} + dev: false + /pushdata-bitcoin@1.0.1: resolution: {integrity: sha512-hw7rcYTJRAl4olM8Owe8x0fBuJJ+WGbMhQuLWOXEMN3PxPCKQHRkhfL+XG0+iXUmSHjkMmb3Ba55Mt21cZc9kQ==} dependencies: @@ -9732,6 +9807,15 @@ packages: dev: false optional: true + /react-base16-styling@0.6.0: + resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} + dependencies: + base16: 1.0.0 + lodash.curry: 4.1.1 + lodash.flow: 3.5.0 + pure-color: 1.3.0 + dev: false + /react-clientside-effect@1.2.6(react@18.3.1): resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==} peerDependencies: @@ -9807,6 +9891,27 @@ packages: requiresBuild: true dev: true + /react-json-view@1.21.3(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==} + peerDependencies: + react: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + dependencies: + flux: 4.0.4(react@18.3.1) + react: 18.3.1 + react-base16-styling: 0.6.0 + react-dom: 18.3.1(react@18.3.1) + react-lifecycles-compat: 3.0.4 + react-textarea-autosize: 8.5.3(@types/react@18.3.11)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - encoding + dev: false + + /react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + /react-remove-scroll-bar@2.3.6(@types/react@18.3.11)(react@18.3.1): resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} engines: {node: '>=10'} @@ -9872,6 +9977,20 @@ packages: refractor: 3.6.0 dev: false + /react-textarea-autosize@8.5.3(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.25.6 + react: 18.3.1 + use-composed-ref: 1.3.0(react@18.3.1) + use-latest: 1.2.1(@types/react@18.3.11)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + dev: false + /react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -10260,7 +10379,6 @@ packages: /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} requiresBuild: true - dev: true /sha.js@2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} @@ -10922,6 +11040,10 @@ packages: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} dev: false + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -10965,7 +11087,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-loader@9.5.1(typescript@5.6.2)(webpack@5.95.0): + /ts-loader@9.5.1(typescript@5.5.4)(webpack@5.95.0): resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -10977,7 +11099,7 @@ packages: micromatch: 4.0.8 semver: 7.6.3 source-map: 0.7.4 - typescript: 5.6.2 + typescript: 5.5.4 webpack: 5.95.0(esbuild@0.23.1) dev: true @@ -11016,7 +11138,7 @@ packages: yn: 3.1.1 dev: false - /ts-node@10.9.2(@types/node@22.7.4)(typescript@5.6.2): + /ts-node@10.9.2(@types/node@20.16.10)(typescript@5.5.4): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -11035,7 +11157,38 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.7.4 + '@types/node': 20.16.10 + acorn: 8.12.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.5.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.16.10 acorn: 8.12.1 acorn-walk: 8.3.4 arg: 4.1.3 @@ -11045,6 +11198,7 @@ packages: typescript: 5.6.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: false /ts-node@8.10.2(typescript@5.6.2): resolution: {integrity: sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==} @@ -11270,6 +11424,12 @@ packages: resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} hasBin: true + dev: false + + /ua-parser-js@1.0.39: + resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==} + hasBin: true + dev: false /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -11289,6 +11449,7 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: false /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -11345,6 +11506,41 @@ packages: tslib: 2.7.0 dev: false + /use-composed-ref@1.3.0(react@18.3.1): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.3.1 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.11 + react: 18.3.1 + dev: false + + /use-latest@1.2.1(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.11 + react: 18.3.1 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.11)(react@18.3.1) + dev: false + /use-sidecar@1.1.2(@types/react@18.3.11)(react@18.3.1): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} @@ -11433,84 +11629,6 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.4.8(@types/node@18.19.54): - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 18.19.54 - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.0 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vite@5.4.8(@types/node@20.16.10): - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 20.16.10 - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.0 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /wait-port@1.1.0: resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} engines: {node: '>=10'} @@ -11614,6 +11732,10 @@ packages: resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} dev: false + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + /webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -11672,6 +11794,13 @@ packages: requiresBuild: true dev: true + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies:
Transaction ID: {transaction.id}
Building transaction...
Applying UTXO selection method blackjack...
{errorMessage}