diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8f827f4b231..f966cc163f6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -18,11 +18,11 @@ PODS: - disklet (0.5.2): - React - DoubleConversion (1.1.6) - - edge-core-js (1.14.0): + - edge-core-js (2.0.0): - React-Core - edge-currency-accountbased (2.18.8): - React-Core - - edge-exchange-plugins (1.3.0): + - edge-exchange-plugins (2.0.0): - React-Core - edge-login-ui-rn (2.20.0): - React @@ -1266,9 +1266,9 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 disklet: e7ed3e673ccad9d175a1675f9f3589ffbf69a5fd DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 - edge-core-js: e13715f9addd8f181edec0306169bc172f41065c + edge-core-js: 9c9b75bf90e628968a9ae34cf8ee93a066b6be51 edge-currency-accountbased: 3b43dcfa1f97746bd537e48ef124b8929cdf2855 - edge-exchange-plugins: 37158221eba37ad789238089f4ba2cb0ef8d24a4 + edge-exchange-plugins: 81379538938c0767658cf693236e177dfdfe7f6e edge-login-ui-rn: 7b78901664d8cb2dc41c76d1bd07c7e4cb77bd2a EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9 diff --git a/package.json b/package.json index c30e7e76dfd..8d09df56677 100644 --- a/package.json +++ b/package.json @@ -140,11 +140,11 @@ "deepmerge": "^4.3.1", "detect-bundler": "^1.1.0", "disklet": "^0.5.2", - "edge-core-js": "^1.14.0", + "edge-core-js": "^2.0.0", "edge-currency-accountbased": "^2.18.8", "edge-currency-monero": "^1.1.1", "edge-currency-plugins": "^2.5.1", - "edge-exchange-plugins": "^1.3.0", + "edge-exchange-plugins": "^2.0.0", "edge-info-server": "^1.2.0", "edge-login-ui-rn": "^2.20.0", "ethers": "^5.7.2", diff --git a/src/__tests__/components/BuyCrypto.test.tsx b/src/__tests__/components/BuyCrypto.test.tsx index 1bfed2a01bc..427b1a9e8ea 100644 --- a/src/__tests__/components/BuyCrypto.test.tsx +++ b/src/__tests__/components/BuyCrypto.test.tsx @@ -27,7 +27,7 @@ describe('BuyCrypto', () => { const renderer = TestRenderer.create( - + ) diff --git a/src/__tests__/components/CreateWalletSelectCryptoRow.test.tsx b/src/__tests__/components/CreateWalletSelectCryptoRow.test.tsx index 6f4c6c38303..169fc112fe4 100644 --- a/src/__tests__/components/CreateWalletSelectCryptoRow.test.tsx +++ b/src/__tests__/components/CreateWalletSelectCryptoRow.test.tsx @@ -31,7 +31,7 @@ describe('WalletListRow', () => { const renderer = TestRenderer.create( - + ) diff --git a/src/__tests__/components/TransactionListRow.test.tsx b/src/__tests__/components/TransactionListRow.test.tsx index 681001695c4..41fa87575ad 100644 --- a/src/__tests__/components/TransactionListRow.test.tsx +++ b/src/__tests__/components/TransactionListRow.test.tsx @@ -46,7 +46,6 @@ describe('TransactionListRow', () => { { } const fakeWallet: any = { - balances: { BTC: '123123' }, + balanceMap: new Map([[null, '123123']]), blockHeight: 12345, currencyConfig: fakeCurrencyConfig, currencyInfo, @@ -68,7 +68,7 @@ describe('TransactionListTop', () => { isEmpty={false} navigation={fakeNavigation} searching={false} - tokenId={undefined} + tokenId={null} wallet={fakeWallet} onSearchingChange={() => undefined} onSearchTextChange={() => undefined} @@ -89,7 +89,7 @@ describe('TransactionListTop', () => { isEmpty={false} navigation={fakeNavigation} searching={false} - tokenId={undefined} + tokenId={null} wallet={fakeWallet} onSearchingChange={() => undefined} onSearchTextChange={() => undefined} diff --git a/src/__tests__/components/WalletListSortableRow.test.tsx b/src/__tests__/components/WalletListSortableRow.test.tsx index df7072c8ba6..b72950520ce 100644 --- a/src/__tests__/components/WalletListSortableRow.test.tsx +++ b/src/__tests__/components/WalletListSortableRow.test.tsx @@ -39,7 +39,7 @@ describe('WalletListSortableRow', () => { const fakeWallet: any = { currencyInfo: fakeCurrencyInfo, name: 'Test wallet', - balances: {} + balanceMap: new Map() } const renderer = TestRenderer.create( diff --git a/src/__tests__/edgeProvider.test.ts b/src/__tests__/edgeProvider.test.ts index 8e97b5889e3..bc926601f84 100644 --- a/src/__tests__/edgeProvider.test.ts +++ b/src/__tests__/edgeProvider.test.ts @@ -23,12 +23,16 @@ describe('upgradeExtendedCurrencyCodes', () => { // As well, BSC is not an actual currencyCode so it won't be included either. BSC can only be specified using object // params with pluginId = binancesmartchain const result = upgradeExtendedCurrencyCodes(currencyConfig, undefined, ['BTC', 'ETH', 'MATIC', 'BNB', 'DOGE', 'BSC']) - expect(result).toEqual([{ pluginId: 'bitcoin' }, { pluginId: 'ethereum' }, { pluginId: 'dogecoin' }]) + expect(result).toEqual([ + { pluginId: 'bitcoin', tokenId: null }, + { pluginId: 'ethereum', tokenId: null }, + { pluginId: 'dogecoin', tokenId: null } + ]) }) test('single code tokens', () => { const result = upgradeExtendedCurrencyCodes(currencyConfig, undefined, ['BTC', 'USDC', 'REP', 'USDT']) expect(result).toEqual([ - { pluginId: 'bitcoin' }, + { pluginId: 'bitcoin', tokenId: null }, { pluginId: 'ethereum', tokenId: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, { pluginId: 'ethereum', tokenId: '1985365e9f78359a9b6ad760e32412f4a445e862' }, { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' } @@ -37,7 +41,7 @@ describe('upgradeExtendedCurrencyCodes', () => { test('double code tokens', () => { const result = upgradeExtendedCurrencyCodes(currencyConfig, undefined, ['BTC', 'MATIC-USDC', 'ETH-REP', 'ETH-USDT']) expect(result).toEqual([ - { pluginId: 'bitcoin' }, + { pluginId: 'bitcoin', tokenId: null }, { pluginId: 'polygon', tokenId: '2791bca1f2de4661ed88a30c99a7a9449aa84174' }, { pluginId: 'ethereum', tokenId: '1985365e9f78359a9b6ad760e32412f4a445e862' }, { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' } @@ -45,14 +49,14 @@ describe('upgradeExtendedCurrencyCodes', () => { }) test('object list', () => { const result = upgradeExtendedCurrencyCodes(currencyConfig, undefined, [ - { pluginId: 'bitcoin', tokenId: undefined, currencyCode: undefined }, - { pluginId: 'polygon', tokenId: undefined, currencyCode: 'USDC' }, - { pluginId: 'ethereum', tokenId: undefined, currencyCode: 'REP' }, - { pluginId: 'ethereum', tokenId: undefined, currencyCode: 'USDT' }, - { pluginId: 'ethereum', tokenId: undefined, currencyCode: 'MATIC' } + { pluginId: 'bitcoin', tokenId: null }, + { pluginId: 'polygon', currencyCode: 'USDC' }, + { pluginId: 'ethereum', currencyCode: 'REP' }, + { pluginId: 'ethereum', currencyCode: 'USDT' }, + { pluginId: 'ethereum', currencyCode: 'MATIC' } ]) expect(result).toEqual([ - { pluginId: 'bitcoin' }, + { pluginId: 'bitcoin', tokenId: null }, { pluginId: 'polygon', tokenId: '2791bca1f2de4661ed88a30c99a7a9449aa84174' }, { pluginId: 'ethereum', tokenId: '1985365e9f78359a9b6ad760e32412f4a445e862' }, { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' }, @@ -66,7 +70,7 @@ describe('upgradeExtendedCurrencyCodes', () => { ['BTC', 'USDC', 'REP', 'USDTERC20'] ) expect(result).toEqual([ - { pluginId: 'bitcoin' }, + { pluginId: 'bitcoin', tokenId: null }, { pluginId: 'ethereum', tokenId: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, { pluginId: 'ethereum', tokenId: '1985365e9f78359a9b6ad760e32412f4a445e862' }, { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' } diff --git a/src/__tests__/modals/WalletListModal.test.tsx b/src/__tests__/modals/WalletListModal.test.tsx index 1a4382d3bbb..4e3b9a22931 100644 --- a/src/__tests__/modals/WalletListModal.test.tsx +++ b/src/__tests__/modals/WalletListModal.test.tsx @@ -23,8 +23,11 @@ describe('WalletListModal', () => { it("Should upgrade currency codes to token ID's", () => { const data: { [code: string]: EdgeAsset[] } = { - ETH: [{ pluginId: 'ethereum' }], - BNB: [{ pluginId: 'binance' }, { pluginId: 'ethereum', tokenId: '1234abcd' }] + ETH: [{ pluginId: 'ethereum', tokenId: null }], + BNB: [ + { pluginId: 'binance', tokenId: null }, + { pluginId: 'ethereum', tokenId: '1234abcd' } + ] } function lookup(currencyCode: string): EdgeAsset[] { return data[currencyCode.toUpperCase()] ?? [] @@ -32,15 +35,18 @@ describe('WalletListModal', () => { // Check ambiguous currency codes: expect(upgradeCurrencyCodes(lookup, ['ETH', 'BNB'])).toEqual([ - { pluginId: 'ethereum' }, - { pluginId: 'binance' }, + { pluginId: 'ethereum', tokenId: null }, + { pluginId: 'binance', tokenId: null }, { pluginId: 'ethereum', tokenId: '1234abcd' } ]) // Check scoped currency codes: - expect(upgradeCurrencyCodes(lookup, ['ETH', 'ETH-BNB'])).toEqual([{ pluginId: 'ethereum' }, { pluginId: 'ethereum', tokenId: '1234abcd' }]) + expect(upgradeCurrencyCodes(lookup, ['ETH', 'ETH-BNB'])).toEqual([ + { pluginId: 'ethereum', tokenId: null }, + { pluginId: 'ethereum', tokenId: '1234abcd' } + ]) // Check missing currency codes: - expect(upgradeCurrencyCodes(lookup, ['ETH', 'LOL'])).toEqual([{ pluginId: 'ethereum' }]) + expect(upgradeCurrencyCodes(lookup, ['ETH', 'LOL'])).toEqual([{ pluginId: 'ethereum', tokenId: null }]) }) }) diff --git a/src/__tests__/reducers/__snapshots__/RootReducer.test.ts.snap b/src/__tests__/reducers/__snapshots__/RootReducer.test.ts.snap index a5664e5461c..5fb92f24256 100644 --- a/src/__tests__/reducers/__snapshots__/RootReducer.test.ts.snap +++ b/src/__tests__/reducers/__snapshots__/RootReducer.test.ts.snap @@ -48,7 +48,7 @@ exports[`initialState 1`] = ` "multiplier": "1", "name": "", }, - "tokenId": undefined, + "tokenId": null, "walletId": "", }, "genericShapeShiftError": null, @@ -70,7 +70,7 @@ exports[`initialState 1`] = ` "multiplier": "1", "name": "", }, - "tokenId": undefined, + "tokenId": null, "walletId": "", }, }, diff --git a/src/__tests__/reducers/__snapshots__/cryptoExchangeReducer.test.ts.snap b/src/__tests__/reducers/__snapshots__/cryptoExchangeReducer.test.ts.snap index c9f31f97a3b..bee3f992dc4 100644 --- a/src/__tests__/reducers/__snapshots__/cryptoExchangeReducer.test.ts.snap +++ b/src/__tests__/reducers/__snapshots__/cryptoExchangeReducer.test.ts.snap @@ -18,7 +18,7 @@ exports[`initialState 1`] = ` "multiplier": "1", "name": "", }, - "tokenId": undefined, + "tokenId": null, "walletId": "", }, "genericShapeShiftError": null, @@ -40,7 +40,7 @@ exports[`initialState 1`] = ` "multiplier": "1", "name": "", }, - "tokenId": undefined, + "tokenId": null, "walletId": "", }, } diff --git a/src/__tests__/scenes/CreateWalletAccountSetupScene.test.tsx b/src/__tests__/scenes/CreateWalletAccountSetupScene.test.tsx index 8ec2e0abb9d..a61631382b6 100644 --- a/src/__tests__/scenes/CreateWalletAccountSetupScene.test.tsx +++ b/src/__tests__/scenes/CreateWalletAccountSetupScene.test.tsx @@ -16,6 +16,7 @@ describe('CreateWalletAccountSelect', () => { selectedWalletType: { currencyName: 'bitcoin', walletType: 'wallet:bitcoin', + pluginId: 'bitcoin', currencyCode: 'BTC' } as any, isReactivation: true, diff --git a/src/__tests__/scenes/CreateWalletImportScene.test.tsx b/src/__tests__/scenes/CreateWalletImportScene.test.tsx index b6fc60a44e4..6ea7c1a0a0d 100644 --- a/src/__tests__/scenes/CreateWalletImportScene.test.tsx +++ b/src/__tests__/scenes/CreateWalletImportScene.test.tsx @@ -40,6 +40,7 @@ describe('CreateWalletImportScene', () => { currencyCode: 'BTC', displayName: 'Bitcoin', pluginId: 'bitcoin', + tokenId: null, walletType: 'wallet:bitcoin-bip49' } ], diff --git a/src/__tests__/scenes/CreateWalletSelectFiatScene.test.tsx b/src/__tests__/scenes/CreateWalletSelectFiatScene.test.tsx index 6cc7cbcd9f4..f903663dd1b 100644 --- a/src/__tests__/scenes/CreateWalletSelectFiatScene.test.tsx +++ b/src/__tests__/scenes/CreateWalletSelectFiatScene.test.tsx @@ -56,9 +56,17 @@ describe('CreateWalletSelectFiatComponent', () => { currencyCode: 'BTC', displayName: 'Bitcoin (no Segwit)', pluginId: 'bitcoin', + tokenId: null, walletType: 'wallet:bitcoin-bip44' }, - { key: 'create-wallet:ethereum-ethereum', currencyCode: 'ETH', displayName: 'Ethereum', pluginId: 'ethereum', walletType: 'wallet:ethereum' }, + { + key: 'create-wallet:ethereum-ethereum', + currencyCode: 'ETH', + displayName: 'Ethereum', + pluginId: 'ethereum', + tokenId: null, + walletType: 'wallet:ethereum' + }, { key: 'create-ethereum-9992ec3cf6a55b00978cddf2b27bc6882d88d1ec', currencyCode: 'POLY', diff --git a/src/__tests__/scenes/CryptoExchangeScene.test.tsx b/src/__tests__/scenes/CryptoExchangeScene.test.tsx index f2b63d51b12..8713562cdf6 100644 --- a/src/__tests__/scenes/CryptoExchangeScene.test.tsx +++ b/src/__tests__/scenes/CryptoExchangeScene.test.tsx @@ -20,7 +20,8 @@ describe('CryptoExchangeComponent', () => { account={fakeAccount} exchangeInfo={initialState} fromWalletId="" - fromWalletBalances={[''] as any} + fromTokenId={null} + fromWalletBalanceMap={new Map()} fromWalletName="BTC Wallet" fromExchangeAmount="1000" fromWalletPrimaryInfo={ diff --git a/src/__tests__/scenes/RequestScene.test.tsx b/src/__tests__/scenes/RequestScene.test.tsx index c2e65655a8f..d6e73a3b14e 100644 --- a/src/__tests__/scenes/RequestScene.test.tsx +++ b/src/__tests__/scenes/RequestScene.test.tsx @@ -13,7 +13,6 @@ describe('Request', () => { const actual = renderer.render( { const fakeWallet: any = { currencyInfo: { pluginId: 'bitcoin', displayName: 'Bitcoin' }, - balances: { BTC: '1234' } + balanceMap: new Map([[null, '1234']]) } const actual = renderer.render( {}} onSelectWallet={async (walletId, currencyCode) => {}} diff --git a/src/__tests__/scenes/SendScene2.ui.test.tsx b/src/__tests__/scenes/SendScene2.ui.test.tsx index cc3ff7afc3e..b8cb8c13a48 100644 --- a/src/__tests__/scenes/SendScene2.ui.test.tsx +++ b/src/__tests__/scenes/SendScene2.ui.test.tsx @@ -88,6 +88,7 @@ describe('SendScene2', () => { @@ -107,7 +108,9 @@ describe('SendScene2', () => { { { label: 'info tile label 2', value: 'info tile value 2' } ], walletId: btcWallet.id, + tokenId: null, spendInfo: { + tokenId: null, spendTargets: [{ publicAddress: 'some pub address', nativeAmount: '1234' }] }, doCheckAndShowGetCryptoModal: false @@ -155,7 +160,9 @@ describe('SendScene2', () => { { const params: SendScene2Params = { hiddenFeaturesMap: { address: true }, walletId: btcWallet.id, + tokenId: null, spendInfo: { + tokenId: null, spendTargets: [ { publicAddress: 'some pub address', nativeAmount: '1234' }, { publicAddress: 'some pub address 2', nativeAmount: '12345' } @@ -222,7 +231,9 @@ describe('SendScene2', () => { const params: SendScene2Params = { lockTilesMap: { address: true }, walletId: btcWallet.id, + tokenId: null, spendInfo: { + tokenId: null, spendTargets: [ { publicAddress: 'some pub address', nativeAmount: '1234' }, { publicAddress: 'some pub address 2', nativeAmount: '12345' } diff --git a/src/__tests__/scenes/TransactionDetailsScene.test.tsx b/src/__tests__/scenes/TransactionDetailsScene.test.tsx index b36a4f19668..8a03c261862 100644 --- a/src/__tests__/scenes/TransactionDetailsScene.test.tsx +++ b/src/__tests__/scenes/TransactionDetailsScene.test.tsx @@ -35,7 +35,7 @@ const fakeCurrencyConfig: any = { } const fakeCoreWallet: any = { - balances: { BTC: '123123' }, + balanceMap: new Map([[null, '123123']]), blockHeight: 12345, currencyConfig: fakeCurrencyConfig, currencyInfo, diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap index 624963774ae..c5fc3f6e7fc 100644 --- a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap @@ -72,7 +72,42 @@ exports[`CreateWalletAccountSelect renders 1`] = ` "width": 90, } } - /> + > + + + + - You have 0 undefined + You have 0.000012 undefined "1234", }, "currencyInfo": { "displayName": "Bitcoin", @@ -121,6 +122,7 @@ exports[`Request should render with loaded props 1`] = ` keyboardVisible={false} onAmountChanged={[Function]} returnKeyType="done" + tokenId={null} /> diff --git a/src/actions/CategoriesActions.ts b/src/actions/CategoriesActions.ts index e7b52f668e6..85514cbd20d 100644 --- a/src/actions/CategoriesActions.ts +++ b/src/actions/CategoriesActions.ts @@ -1,4 +1,4 @@ -import { EdgeAccount, EdgeAssetAmount, EdgeCurrencyWallet, EdgeTransaction } from 'edge-core-js' +import { EdgeAccount, EdgeAssetAmount, EdgeCurrencyWallet, EdgeTokenId, EdgeTransaction } from 'edge-core-js' import { sprintf } from 'sprintf-js' import { showError } from '../components/services/AirshipInstance' @@ -265,96 +265,117 @@ export const defaultCategories = [ export const getTxActionDisplayInfo = ( tx: EdgeTransaction, wallet: EdgeCurrencyWallet, - tokenId?: string + tokenId: EdgeTokenId ): { splitCategory: SplitCategory; notes?: string; direction: 'send' | 'receive' } | undefined => { - const { action } = tx - if (action == null) return - const { type } = action + const { assetAction, chainAction, chainAssetAction, savedAction } = tx + const action = savedAction ?? chainAction + const assetAct = assetAction ?? chainAssetAction + + if (action == null || assetAct == null) return + const { actionType } = action + const { assetActionType } = assetAct const getCurrencyCodes = (assets: EdgeAssetAmount[]) => assets.map(asset => getCurrencyCode(wallet, asset.tokenId)) - switch (type) { - case 'swap': - case 'swapOrderFill': { - // Determine if the swap destination was to a different asset or if the - // swap source was from a different asset. - const txSrcSameAsset = action.sourceAsset.tokenId === tokenId && action.sourceAsset.pluginId === wallet.currencyInfo.pluginId - const toFromStr = txSrcSameAsset ? lstrings.transaction_details_swap_to_subcat_1s : lstrings.transaction_details_swap_from_subcat_1s - const otherAsset = txSrcSameAsset ? action.destAsset : action.sourceAsset + switch (actionType) { + case 'swap': { + switch (assetActionType) { + case 'swap': + case 'swapOrderFill': { + // Determine if the swap destination was to a different asset or if the + // swap source was from a different asset. + const txSrcSameAsset = action.fromAsset.tokenId === tokenId && action.fromAsset.pluginId === wallet.currencyInfo.pluginId + const toFromStr = txSrcSameAsset ? lstrings.transaction_details_swap_to_subcat_1s : lstrings.transaction_details_swap_from_subcat_1s + const otherAsset = txSrcSameAsset ? action.toAsset : action.fromAsset - return { - splitCategory: { - category: 'exchange', - subcategory: sprintf(toFromStr, getCurrencyCode(wallet, otherAsset?.tokenId)) - }, - direction: txSrcSameAsset ? 'receive' : 'send' + return { + splitCategory: { + category: 'exchange', + subcategory: sprintf(toFromStr, getCurrencyCode(wallet, otherAsset?.tokenId)) + }, + direction: txSrcSameAsset ? 'receive' : 'send' + } + } + case 'swapOrderPost': + return { + splitCategory: { + category: 'expense', + subcategory: sprintf(lstrings.transaction_details_swap_order_post) + }, + direction: 'send' + } + case 'swapOrderCancel': + return { + splitCategory: { + category: 'expense', + subcategory: sprintf(lstrings.transaction_details_swap_order_cancel) + }, + direction: 'send' + } + default: + console.error(`Unsupported EdgeTxAction type: '${assetActionType}'`) } + break } - case 'swapOrderPost': - return { - splitCategory: { - category: 'expense', - subcategory: sprintf(lstrings.transaction_details_swap_order_post) - }, - direction: 'send' - } - case 'swapOrderCancel': - return { - splitCategory: { - category: 'expense', - subcategory: sprintf(lstrings.transaction_details_swap_order_cancel) - }, - direction: 'send' - } case 'stake': { - let subcategory - if (action.stakeAssets.length === 1) subcategory = sprintf(lstrings.transaction_details_stake_subcat_1s, ...getCurrencyCodes(action.stakeAssets)) - else if (action.stakeAssets.length === 2) subcategory = sprintf(lstrings.transaction_details_stake_subcat_2s, ...getCurrencyCodes(action.stakeAssets)) - else { - console.warn(`Unsupported number of assets for '${type}' EdgeTxActionSwapType`) - return - } - return { splitCategory: { category: 'transfer', subcategory }, direction: 'send' } - } - case 'stakeOrder': { - let notes - if (action.stakeAssets.length === 1) notes = sprintf(lstrings.transaction_details_unstake_order_notes_1s, ...getCurrencyCodes(action.stakeAssets)) - else if (action.stakeAssets.length === 2) notes = sprintf(lstrings.transaction_details_unstake_order_notes_2s, ...getCurrencyCodes(action.stakeAssets)) - else { - console.error(`Unsupported number of assets for '${type}' EdgeTxActionSwapType`) - return - } - return { - splitCategory: { category: 'expense', subcategory: lstrings.transaction_details_stake_order_subcat }, - notes, - direction: 'send' - } - } - case 'unstake': { - let subcategory - if (action.stakeAssets.length === 1) subcategory = sprintf(lstrings.transaction_details_unstake_subcat_1s, ...getCurrencyCodes(action.stakeAssets)) - else if (action.stakeAssets.length === 2) subcategory = sprintf(lstrings.transaction_details_unstake_subcat_2s, ...getCurrencyCodes(action.stakeAssets)) - else { - console.error(`Unsupported number of assets for '${type}' EdgeTxActionSwapType`) - return - } - return { splitCategory: { category: 'transfer', subcategory }, direction: 'receive' } - } - case 'unstakeOrder': { - let notes - if (action.stakeAssets.length === 1) notes = sprintf(lstrings.transaction_details_unstake_order_notes_1s, ...getCurrencyCodes(action.stakeAssets)) - else if (action.stakeAssets.length === 2) notes = sprintf(lstrings.transaction_details_unstake_order_notes_2s, ...getCurrencyCodes(action.stakeAssets)) - else { - console.error(`Unsupported number of assets for '${type}' EdgeTxActionSwapType`) - return - } - return { - splitCategory: { category: 'expense', subcategory: lstrings.transaction_details_unstake_order }, - notes, - direction: 'send' + switch (assetActionType) { + case 'stake': { + let subcategory + if (action.stakeAssets.length === 1) subcategory = sprintf(lstrings.transaction_details_stake_subcat_1s, ...getCurrencyCodes(action.stakeAssets)) + else if (action.stakeAssets.length === 2) subcategory = sprintf(lstrings.transaction_details_stake_subcat_2s, ...getCurrencyCodes(action.stakeAssets)) + else { + console.warn(`Unsupported number of assets for '${assetActionType}' EdgeTxActionSwapType`) + return + } + return { splitCategory: { category: 'transfer', subcategory }, direction: 'send' } + } + case 'stakeOrder': { + let notes + if (action.stakeAssets.length === 1) notes = sprintf(lstrings.transaction_details_unstake_order_notes_1s, ...getCurrencyCodes(action.stakeAssets)) + else if (action.stakeAssets.length === 2) + notes = sprintf(lstrings.transaction_details_unstake_order_notes_2s, ...getCurrencyCodes(action.stakeAssets)) + else { + console.error(`Unsupported number of assets for '${assetActionType}' EdgeTxActionSwapType`) + return + } + return { + splitCategory: { category: 'expense', subcategory: lstrings.transaction_details_stake_order_subcat }, + notes, + direction: 'send' + } + } + case 'unstake': { + let subcategory + if (action.stakeAssets.length === 1) subcategory = sprintf(lstrings.transaction_details_unstake_subcat_1s, ...getCurrencyCodes(action.stakeAssets)) + else if (action.stakeAssets.length === 2) + subcategory = sprintf(lstrings.transaction_details_unstake_subcat_2s, ...getCurrencyCodes(action.stakeAssets)) + else { + console.error(`Unsupported number of assets for '${assetActionType}' EdgeTxActionSwapType`) + return + } + return { splitCategory: { category: 'transfer', subcategory }, direction: 'receive' } + } + case 'unstakeOrder': { + let notes + if (action.stakeAssets.length === 1) notes = sprintf(lstrings.transaction_details_unstake_order_notes_1s, ...getCurrencyCodes(action.stakeAssets)) + else if (action.stakeAssets.length === 2) + notes = sprintf(lstrings.transaction_details_unstake_order_notes_2s, ...getCurrencyCodes(action.stakeAssets)) + else { + console.error(`Unsupported number of assets for '${assetActionType}' EdgeTxActionSwapType`) + return + } + return { + splitCategory: { category: 'expense', subcategory: lstrings.transaction_details_unstake_order }, + notes, + direction: 'send' + } + } + default: + console.error(`Unsupported EdgeTxAction type: '${assetActionType}'`) } + break } default: - console.error(`Unsupported EdgeTxAction type: '${type}'`) + console.error(`Unsupported EdgeTxAction actionType: '${actionType}'`) } } diff --git a/src/actions/CreateWalletActions.tsx b/src/actions/CreateWalletActions.tsx index d7686461b86..4516e918201 100644 --- a/src/actions/CreateWalletActions.tsx +++ b/src/actions/CreateWalletActions.tsx @@ -15,6 +15,7 @@ import { config } from '../theme/appConfig' import { ThunkAction } from '../types/reduxTypes' import { NavigationBase } from '../types/routerTypes' import { EdgeAsset } from '../types/types' +import { getWalletTokenId } from '../util/CurrencyInfoHelpers' import { logActivity } from '../util/logger' import { filterNull } from '../util/safeFilters' import { logEvent, TrackingEventName } from '../util/tracking' @@ -75,14 +76,14 @@ export function fetchAccountActivationInfo(walletType: string): ThunkAction account.currencyConfig[pluginId].currencyInfo.currencyCode === currency) if (pluginId != null) { - supportedAssets.push({ pluginId }) + supportedAssets.push({ pluginId, tokenId: null }) continue } @@ -158,10 +159,13 @@ export function createAccountTransaction( const paymentDenom = getExchangeDenomination(state, paymentWallet.currencyInfo.pluginId, currencyCode) let nativeAmount = mul(amount, paymentDenom.multiplier) nativeAmount = toFixed(nativeAmount, 0, 0) + const tokenId = getWalletTokenId(paymentWallet, currencyCode) + if (handleAvailability.result === 'AccountAvailable') { navigation.push('send2', { + tokenId, spendInfo: { - currencyCode, + tokenId, spendTargets: [ { nativeAmount, @@ -196,7 +200,7 @@ export function createAccountTransaction( category: 'Expense:' + sprintf(lstrings.create_wallet_account_metadata_category, createdWalletCurrencyCode), notes: sprintf(lstrings.create_wallet_account_metadata_notes, createdWalletCurrencyCode, createdWalletCurrencyCode, config.supportEmail) } - paymentWallet.saveTxMetadata(edgeTransaction.txid, currencyCode, edgeMetadata).catch(err => console.warn(err)) + paymentWallet.saveTxMetadata({ txid: edgeTransaction.txid, tokenId, metadata: edgeMetadata }).catch(err => console.warn(err)) navigation.navigate('walletsTab', { screen: 'walletList' }) setTimeout(() => { Alert.alert(lstrings.create_wallet_account_payment_sent_title, lstrings.create_wallet_account_payment_sent_message) diff --git a/src/actions/CryptoExchangeActions.tsx b/src/actions/CryptoExchangeActions.tsx index 7d357ced6fc..e1740ff5f45 100644 --- a/src/actions/CryptoExchangeActions.tsx +++ b/src/actions/CryptoExchangeActions.tsx @@ -24,7 +24,7 @@ import { convertCurrency } from '../selectors/WalletSelectors' import { RootState, ThunkAction } from '../types/reduxTypes' import { NavigationBase } from '../types/routerTypes' import { GuiCurrencyInfo, GuiSwapInfo } from '../types/types' -import { getTokenId } from '../util/CurrencyInfoHelpers' +import { getCurrencyCode, getTokenIdForced, getWalletTokenId } from '../util/CurrencyInfoHelpers' import { getWalletName } from '../util/CurrencyWalletHelpers' import { logActivity } from '../util/logger' import { bestOfPlugins } from '../util/ReferralHelpers' @@ -52,12 +52,16 @@ export function getQuoteForTransaction(navigation: NavigationBase, info: SetNati const { currencyWallets } = state.core.account const fromCoreWallet: EdgeCurrencyWallet = currencyWallets[fromWalletId] const toCoreWallet: EdgeCurrencyWallet = currencyWallets[toWalletId] + + const fromTokenId = getWalletTokenId(fromCoreWallet, fromCurrencyCode) + const toTokenId = getWalletTokenId(toCoreWallet, toCurrencyCode) + const request: EdgeSwapRequest = { - fromCurrencyCode, + fromTokenId, fromWallet: fromCoreWallet, nativeAmount: info.primaryNativeAmount, quoteFor: info.whichWallet, - toCurrencyCode, + toTokenId, toWallet: toCoreWallet } @@ -78,20 +82,18 @@ export function getQuoteForTransaction(navigation: NavigationBase, info: SetNati navigation.navigate('exchangeTab', { screen: 'exchange' }) const insufficientFunds = asMaybeInsufficientFundsError(error) - if ( - insufficientFunds != null && - insufficientFunds.currencyCode != null && - fromCurrencyCode !== insufficientFunds.currencyCode && - fromWalletId != null - ) { - const { currencyCode } = insufficientFunds + if (insufficientFunds != null && fromWalletId != null && fromTokenId !== insufficientFunds.tokenId) { + const { tokenId } = insufficientFunds const { currencyWallets } = state.core.account + const fromWallet = currencyWallets[fromWalletId] + const currencyCode = getCurrencyCode(fromWallet, tokenId) + await Airship.show(bridge => ( { dispatch({ type: 'SHIFT_COMPLETE' }) dispatch(selectWalletForExchange(fromWalletId, currencyCode, 'to')).catch(err => showError(err)) @@ -163,7 +165,9 @@ export const getSwapInfo = async (state: RootState, quote: EdgeSwapQuote): Promi // Currency conversion tools: // Both fromCurrencyCode and toCurrencyCode will exist, since we set them: const { request } = quote - const { fromWallet, toWallet, fromCurrencyCode = '', toCurrencyCode = '' } = request + const { fromWallet, toWallet, fromTokenId, toTokenId } = request + const fromCurrencyCode = getCurrencyCode(fromWallet, fromTokenId) + const toCurrencyCode = getCurrencyCode(toWallet, toTokenId) // Format from amount: const fromPrimaryInfo = state.cryptoExchange.fromWalletPrimaryInfo @@ -300,7 +304,9 @@ export function shiftCryptoCurrency(navigation: NavigationBase, quote: EdgeSwapQ const { fromDisplayAmount, fee, fromFiat, fromTotalFiat, toDisplayAmount, toFiat } = await getSwapInfo(state, quote) const { isEstimate, fromNativeAmount, toNativeAmount, networkFee, pluginId, expirationDate, request } = quote // Both fromCurrencyCode and toCurrencyCode will exist, since we set them: - const { toWallet, fromCurrencyCode = '', toCurrencyCode = '' } = request + const { fromWallet, toWallet, fromTokenId, toTokenId } = request + const fromCurrencyCode = getCurrencyCode(fromWallet, fromTokenId) + const toCurrencyCode = getCurrencyCode(toWallet, toTokenId) try { logEvent('Exchange_Shift_Start') const { swapInfo } = account.swapConfig[pluginId] @@ -373,7 +379,7 @@ export function selectWalletForExchange(walletId: string, currencyCode: string, const primaryExchangeDenomination = getExchangeDenomination(state, wallet.currencyInfo.pluginId, cc) const primaryInfo: GuiCurrencyInfo = { walletId, - tokenId: getTokenId(state.core.account, wallet.currencyInfo.pluginId, cc), + tokenId: getTokenIdForced(state.core.account, wallet.currencyInfo.pluginId, cc), displayCurrencyCode: cc, exchangeCurrencyCode: cc, displayDenomination: primaryDisplayDenomination, @@ -419,7 +425,9 @@ async function getBalanceMessage(state: RootState, walletId: string, currencyCod const { account } = state.core const { currencyWallets } = account const wallet = currencyWallets[walletId] - const balanceInCrypto = wallet.balances[currencyCode] ?? '0' + const tokenId = getWalletTokenId(wallet, currencyCode) + + const balanceInCrypto = wallet.balanceMap.get(tokenId) ?? '0' const isoFiatCurrencyCode = wallet.fiatCurrencyCode const exchangeDenomination = getExchangeDenomination(state, wallet.currencyInfo.pluginId, currencyCode) const balanceInCryptoDisplay = convertNativeToExchange(exchangeDenomination.multiplier)(balanceInCrypto) diff --git a/src/actions/DeepLinkingActions.ts b/src/actions/DeepLinkingActions.ts index 2d7b696281c..b6353ad296d 100644 --- a/src/actions/DeepLinkingActions.ts +++ b/src/actions/DeepLinkingActions.ts @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeParsedUri } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeParsedUri, EdgeTokenId } from 'edge-core-js' import { launchPriceChangeBuySellSwapModal } from '../components/modals/PriceChangeBuySellSwapModal' import { pickWallet } from '../components/modals/WalletListModal' @@ -10,7 +10,6 @@ import { DeepLink } from '../types/DeepLinkTypes' import { Dispatch, RootState, ThunkAction } from '../types/reduxTypes' import { NavigationBase } from '../types/routerTypes' import { EdgeAsset } from '../types/types' -import { getTokenId } from '../util/CurrencyInfoHelpers' import { base58ToUuid } from '../util/utils' import { activatePromotion } from './AccountReferralActions' import { launchPaymentProto } from './PaymentProtoActions' @@ -154,7 +153,7 @@ export async function handleLink(navigation: NavigationBase, dispatch: Dispatch, case 'azteco': { if (!allWalletsLoaded) return false - const result = await pickWallet({ account, assets: [{ pluginId: 'bitcoin' }], navigation, showCreateWallet: true }) + const result = await pickWallet({ account, assets: [{ pluginId: 'bitcoin', tokenId: null }], navigation, showCreateWallet: true }) if (result?.type !== 'wallet') { // pickWallet returning undefined means user has no matching wallet. // This should never happen. Even if the user doesn't have a bitcoin wallet, they will be presented with @@ -189,21 +188,14 @@ export async function handleLink(navigation: NavigationBase, dispatch: Dispatch, } case 'other': { - const matchingWalletIdsAndUris: Array<{ walletId: string; parsedUri: EdgeParsedUri; currencyCode?: string; tokenId?: string }> = [] + const matchingWalletIdsAndUris: Array<{ walletId: string; parsedUri: EdgeParsedUri; currencyCode?: string; tokenId: EdgeTokenId }> = [] // Try to parse with all wallets for (const wallet of Object.values(currencyWallets)) { const parsedUri = await wallet.parseUri(link.uri).catch(e => undefined) if (parsedUri != null) { - if (parsedUri.currencyCode != null && parsedUri.currencyCode !== wallet.currencyInfo.currencyCode) { - // Check if the user has this token enabled - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, parsedUri.currencyCode) - if (tokenId != null) { - matchingWalletIdsAndUris.push({ currencyCode: parsedUri.currencyCode, walletId: wallet.id, parsedUri, tokenId }) - } - } else { - matchingWalletIdsAndUris.push({ currencyCode: parsedUri.currencyCode, walletId: wallet.id, parsedUri }) - } + const { tokenId = null } = parsedUri + matchingWalletIdsAndUris.push({ currencyCode: parsedUri.currencyCode, walletId: wallet.id, parsedUri, tokenId }) } } @@ -225,7 +217,6 @@ export async function handleLink(navigation: NavigationBase, dispatch: Dispatch, const wallet = currencyWallets[walletId] const { pluginId } = wallet.currencyInfo - if (cc == null) return { pluginId } return { pluginId, tokenId } }) const walletListResult = await pickWallet({ account, allowedWalletIds, assets, navigation }) @@ -260,7 +251,7 @@ export async function handleLink(navigation: NavigationBase, dispatch: Dispatch, } async function launchAzteco(navigation: NavigationBase, edgeWallet: EdgeCurrencyWallet, uri: string): Promise { - const address = await edgeWallet.getReceiveAddress() + const address = await edgeWallet.getReceiveAddress({ tokenId: null }) const response = await fetch(`${uri}${address.publicAddress}`) if (response.ok) { showToast(lstrings.azteco_success) diff --git a/src/actions/FioActions.tsx b/src/actions/FioActions.tsx index 80cdbb81dcb..d23de4b8782 100644 --- a/src/actions/FioActions.tsx +++ b/src/actions/FioActions.tsx @@ -76,6 +76,7 @@ export function checkFioObtData(wallet: EdgeCurrencyWallet, transactions: EdgeTr const obtDataRecords = await getFioObtData(fioWallets) for (const transaction of transactions) { + const { tokenId } = transaction const edgeMetadata: EdgeMetadata = transaction.metadata != null ? transaction.metadata : { notes: '' } try { const { name } = edgeMetadata @@ -97,7 +98,7 @@ export function checkFioObtData(wallet: EdgeCurrencyWallet, transactions: EdgeTr await addToFioAddressCache(state.core.account, [obtForTx.payer_fio_address]) try { - await wallet.saveTxMetadata(transaction.txid, transaction.currencyCode, edgeMetadata) + await wallet.saveTxMetadata({ txid: transaction.txid, tokenId, metadata: edgeMetadata }) } catch (err: any) { console.warn(err) } diff --git a/src/actions/LogActions.tsx b/src/actions/LogActions.tsx index 3d946480872..2b73c1d2ee3 100644 --- a/src/actions/LogActions.tsx +++ b/src/actions/LogActions.tsx @@ -13,6 +13,7 @@ import { asActionProgram, asActionProgramState } from '../controllers/action-que import { ActionProgram, ActionProgramState } from '../controllers/action-queue/types' import { lstrings } from '../locales/strings' import { ThunkAction } from '../types/reduxTypes' +import { getCurrencyCode } from '../util/CurrencyInfoHelpers' import { clearLogs, logWithType, readLogs } from '../util/logger' const logsUri = 'https://logs1.edge.app/v1/log/' @@ -162,13 +163,11 @@ export function getLogOutput(): ThunkAction> { // Wallet TX summary const wallet = currencyWallets[walletId] const { enabledTokenIds } = wallet - const codes = enabledTokenIds.map(id => wallet.currencyConfig.allTokens[id].currencyCode) - if (codes.length === 0) { - codes.push(wallet.currencyInfo.currencyCode) - } - for (const code of codes) { - const txs = await wallet.getNumTransactions({ currencyCode: code }) - logOutput.data += `${code}: ${txs} txs\n` + const tokenIds = [null, ...enabledTokenIds] + for (const tokenId of tokenIds) { + const txs = await wallet.getNumTransactions({ tokenId }) + const currencyCode = getCurrencyCode(wallet, tokenId) + logOutput.data += `${currencyCode}: ${txs} txs\n` } const { imported, syncKey } = await account.getRawPrivateKey(wallet.id) diff --git a/src/actions/LoginActions.tsx b/src/actions/LoginActions.tsx index 01dc49b18ec..9eb2f06c4a4 100644 --- a/src/actions/LoginActions.tsx +++ b/src/actions/LoginActions.tsx @@ -276,7 +276,7 @@ const currencyCodesToEdgeTokenIds = (account: EdgeAccount, currencyCodes: string // Add the mainnet EdgeAsset if we haven't yet if (edgeTokenIds.find(edgeTokenId => edgeTokenId.tokenId == null && edgeTokenId.pluginId === pluginId) == null) { - edgeTokenIds.push({ pluginId }) + edgeTokenIds.push({ pluginId, tokenId: null }) } // Add tokens diff --git a/src/actions/PaymentProtoActions.tsx b/src/actions/PaymentProtoActions.tsx index 310d4f059c9..e20af2b92a6 100644 --- a/src/actions/PaymentProtoActions.tsx +++ b/src/actions/PaymentProtoActions.tsx @@ -20,7 +20,7 @@ import { } from '../types/PaymentProtoTypes' import { NavigationBase } from '../types/routerTypes' import { EdgeAsset, StringMap } from '../types/types' -import { getTokenId } from '../util/CurrencyInfoHelpers' +import { getTokenId, getTokenIdForced } from '../util/CurrencyInfoHelpers' export interface LaunchPaymentProtoParams { wallet?: EdgeCurrencyWallet @@ -130,7 +130,7 @@ export async function launchPaymentProto(navigation: NavigationBase, account: Ed if (paymentProtoSupportedPluginIds.find(id => id === pluginId) == null) continue if (chain === currency) { - paymentAssets.push({ pluginId }) + paymentAssets.push({ pluginId, tokenId: null }) paymentCurrencies.push(chain) } else { const edgeCurrencyCode = CURRENCY_MAP[currency] ?? currency @@ -206,9 +206,11 @@ export async function launchPaymentProto(navigation: NavigationBase, account: Ed // This is an additional buffer because the protocol doesn't discount segwit // transactions and we want to make sure the transaction succeeds. const { pluginId } = selectedWallet.currencyInfo + const tokenId = getTokenIdForced(account, selectedWallet.currencyInfo.pluginId, selectedCurrencyCode ?? selectedWallet.currencyInfo.currencyCode) + if (typeof requiredFeeRate === 'number' && SPECIAL_CURRENCY_INFO[pluginId].hasSegwit) requiredFeeRate *= 1.8 const spendInfo: EdgeSpendInfo = { - currencyCode: selectedCurrencyCode, + tokenId, // Reverse the outputs since Anypay puts the merchant amount first. Making it last will have // amount shown in a large Amount Tile. Anypay fee will show compressed in a combined // address/amount Tile @@ -231,7 +233,7 @@ export async function launchPaymentProto(navigation: NavigationBase, account: Ed scamWarning: hideScamWarning }, spendInfo, - tokenId: getTokenId(account, selectedWallet.currencyInfo.pluginId, selectedCurrencyCode ?? selectedWallet.currencyInfo.currencyCode), + tokenId, lockTilesMap: { amount: true, address: true, fee: requiredFeeRate != null }, onBack, onDone: async (error: Error | null, edgeTransaction?: EdgeTransaction) => { diff --git a/src/actions/ReceiveDropdown.tsx b/src/actions/ReceiveDropdown.tsx index 3bfb5bf44e0..0fb8b07f187 100644 --- a/src/actions/ReceiveDropdown.tsx +++ b/src/actions/ReceiveDropdown.tsx @@ -9,7 +9,6 @@ import { lstrings } from '../locales/strings' import { getDisplayDenomination, getExchangeDenomination } from '../selectors/DenominationSelectors' import { ThunkAction } from '../types/reduxTypes' import { NavigationBase } from '../types/routerTypes' -import { getTokenId } from '../util/CurrencyInfoHelpers' import { calculateSpamThreshold, convertNativeToDisplay, zeroString } from '../util/utils' import { playReceiveSound } from './SoundActions' import { selectWalletToken } from './WalletActions' @@ -21,7 +20,7 @@ let receiveDropdownShowing = false */ export function showReceiveDropdown(navigation: NavigationBase, transaction: EdgeTransaction): ThunkAction { return (dispatch, getState) => { - const { currencyCode, nativeAmount, walletId } = transaction + const { currencyCode, nativeAmount, tokenId, walletId } = transaction // Grab the matching wallet: const state = getState() @@ -30,7 +29,6 @@ export function showReceiveDropdown(navigation: NavigationBase, transaction: Edg if (wallet == null) return const { currencyInfo, fiatCurrencyCode } = wallet - const tokenId = getTokenId(account, currencyInfo.pluginId, currencyCode) // Never stack dropdowns: if (receiveDropdownShowing) return diff --git a/src/actions/ScanActions.tsx b/src/actions/ScanActions.tsx index 5dcb06531dc..21b75eb93ea 100644 --- a/src/actions/ScanActions.tsx +++ b/src/actions/ScanActions.tsx @@ -1,4 +1,4 @@ -import { EdgeAccount, EdgeCurrencyWallet, EdgeParsedUri, EdgeSpendInfo } from 'edge-core-js' +import { EdgeAccount, EdgeCurrencyWallet, EdgeParsedUri, EdgeSpendInfo, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { sprintf } from 'sprintf-js' import URL from 'url-parse' @@ -14,7 +14,7 @@ import { config } from '../theme/appConfig' import { RequestAddressLink } from '../types/DeepLinkTypes' import { Dispatch, ThunkAction } from '../types/reduxTypes' import { NavigationBase } from '../types/routerTypes' -import { getTokenId } from '../util/CurrencyInfoHelpers' +import { getCurrencyCode, getWalletTokenId } from '../util/CurrencyInfoHelpers' import { parseDeepLink } from '../util/DeepLinkParser' import { logActivity } from '../util/logger' import { makeCurrencyCodeTable, upgradeCurrencyCodes } from '../util/tokenIdTools' @@ -92,18 +92,19 @@ export const doRequestAddress = async (navigation: NavigationBase, account: Edge // Show wallet picker(s) for supported assets const jsonPayloadMap: { [currencyAndTokenCode: string]: string | null } = {} for (const supportedAsset of supportedAssets) { - const tokenId = upgradeCurrencyCodes(lookup, [`${supportedAsset.nativeCode}-${supportedAsset.tokenCode}`]) + const edgeAssets = upgradeCurrencyCodes(lookup, [`${supportedAsset.nativeCode}-${supportedAsset.tokenCode}`]) await Airship.show(bridge => ( - + )).then(async result => { if (result?.type === 'wallet') { const { walletId, currencyCode } = result const { currencyWallets } = account const wallet = currencyWallets[walletId] + const tokenId = getWalletTokenId(wallet, currencyCode) // TODO: Extend getReceiveAddress() to generate the full bitcion:XXXX address instead of using raw addresses here - const { publicAddress } = await wallet.getReceiveAddress({ currencyCode }) + const { publicAddress } = await wallet.getReceiveAddress({ tokenId }) jsonPayloadMap[`${currencyWallets[walletId].currencyInfo.currencyCode}_${currencyCode}`] = publicAddress } }) @@ -174,8 +175,7 @@ export function handleWalletUris( fioAddress?: string ): ThunkAction> { return async (dispatch, getState) => { - const { account } = getState().core - const { legacyAddress, metadata, minNativeAmount, nativeAmount, publicAddress, uniqueIdentifier } = parsedUri + const { legacyAddress, metadata, minNativeAmount, nativeAmount, publicAddress, uniqueIdentifier, tokenId = null } = parsedUri const currencyCode: string = parsedUri.currencyCode ?? wallet.currencyInfo.currencyCode // Coin operations @@ -185,9 +185,10 @@ export function handleWalletUris( if (parsedUri.token) { // TOKEN URI - const { contractAddress, currencyName, denominations, currencyCode } = parsedUri.token + const { contractAddress, currencyName, denominations } = parsedUri.token return navigation.push('editToken', { - currencyCode: currencyCode.toUpperCase(), + currencyCode: parsedUri.token.currencyCode.toUpperCase(), + tokenId, multiplier: denominations[0]?.multiplier, displayName: currencyName, networkLocation: { contractAddress }, @@ -201,10 +202,6 @@ export function handleWalletUris( } // PUBLIC ADDRESS URI - let tokenId: string | undefined - if (currencyCode !== wallet.currencyInfo.currencyCode) { - tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) - } const spendInfo: EdgeSpendInfo = { metadata, spendTargets: [ @@ -222,7 +219,7 @@ export function handleWalletUris( // React navigation doesn't like passing non-serializable objects as params. Convert date to string first // https://github.com/react-navigation/react-navigation/issues/7925 const isoExpireDate = parsedUri?.expireDate?.toISOString() - navigation.push('send2', { walletId: wallet.id, minNativeAmount, spendInfo, isoExpireDate, hiddenFeaturesMap: { scamWarning: false } }) + navigation.push('send2', { walletId: wallet.id, minNativeAmount, spendInfo, tokenId, isoExpireDate, hiddenFeaturesMap: { scamWarning: false } }) } catch (error: any) { // INVALID URI await Airship.show<'ok' | undefined>(bridge => ( @@ -263,6 +260,7 @@ async function privateKeyModalActivated(wallet: EdgeCurrencyWallet, privateKeys: async function sweepPrivateKeys(wallet: EdgeCurrencyWallet, privateKeys: string[]) { const unsignedTx = await wallet.sweepPrivateKeys({ + tokenId: null, privateKeys, spendTargets: [] }) @@ -300,15 +298,16 @@ async function sweepPrivateKeys(wallet: EdgeCurrencyWallet, privateKeys: string[ const shownWalletGetCryptoModals: string[] = [] -export function checkAndShowGetCryptoModal(navigation: NavigationBase, selectedWalletId?: string, selectedCurrencyCode?: string): ThunkAction> { +export function checkAndShowGetCryptoModal(navigation: NavigationBase, selectedWalletId?: string, tokenId?: EdgeTokenId): ThunkAction> { return async (dispatch, getState) => { try { const state = getState() - const currencyCode = selectedCurrencyCode ?? state.ui.wallets.selectedCurrencyCode const { currencyWallets } = state.core.account const wallet: EdgeCurrencyWallet = currencyWallets[selectedWalletId ?? state.ui.wallets.selectedWalletId] + tokenId = tokenId === undefined ? getWalletTokenId(wallet, state.ui.wallets.selectedCurrencyCode) : tokenId + const currencyCode = getCurrencyCode(wallet, tokenId) // check if balance is zero - const balance = wallet.balances[currencyCode] + const balance = wallet.balanceMap.get(tokenId) if (!zeroString(balance) || shownWalletGetCryptoModals.includes(wallet.id)) return // if there's a balance then early exit shownWalletGetCryptoModals.push(wallet.id) // add to list of wallets with modal shown this session let threeButtonModal diff --git a/src/actions/WalletActions.tsx b/src/actions/WalletActions.tsx index 86c9e49ef33..e444d1c3011 100644 --- a/src/actions/WalletActions.tsx +++ b/src/actions/WalletActions.tsx @@ -1,6 +1,6 @@ import { div, log10, lt, round } from 'biggystring' import { asArray, asBoolean, asMaybe, asObject, asString, asUnknown } from 'cleaners' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import hashjs from 'hash.js' import * as React from 'react' import { sprintf } from 'sprintf-js' @@ -23,7 +23,7 @@ import { refreshConnectedWallets } from './FioActions' export interface SelectWalletTokenParams { navigation: NavigationBase walletId: string - tokenId?: string + tokenId: EdgeTokenId alwaysActivate?: boolean } @@ -95,7 +95,7 @@ function selectEOSWallet(navigation: NavigationBase, walletId: string, currencyC currencyInfo: { currencyCode, pluginId } } = wallet const walletName = name ?? '' - const { publicAddress } = await wallet.getReceiveAddress() + const { publicAddress } = await wallet.getReceiveAddress({ tokenId: null }) if (publicAddress !== '') { // already activated @@ -222,8 +222,10 @@ const activateWalletTokens = async ( const activationQuote = await account.activateWallet({ activateWalletId: wallet.id, activateTokenIds: tokenIds, - paymentWalletId, - paymentTokenId: tokenId ?? undefined + paymentInfo: { + walletId: paymentWalletId, + tokenId + } }) const tokensText = tokenIds.map(tokenId => { const { currencyCode, displayName } = getToken(wallet, tokenId) ?? {} @@ -260,7 +262,7 @@ const activateWalletTokens = async ( { label: lstrings.mining_fee, value: feeString } ], onConfirm: (resetSlider: () => void) => { - if (lt(wallet.balances[paymentCurrencyCode] ?? '0', nativeFee)) { + if (lt(wallet.balanceMap.get(feeTokenId) ?? '0', nativeFee)) { const msg = tokenIds.length > 1 ? lstrings.activate_wallet_tokens_insufficient_funds_s : lstrings.activate_wallet_token_insufficient_funds_s Airship.show<'ok' | undefined>(bridge => ( | NavigationProp<'transactionList'>, walletId: string, option: WalletListMenuKey, - tokenId?: string + tokenId: EdgeTokenId ): ThunkAction> { const switchString = option.startsWith('split') ? 'split' : option @@ -97,7 +98,7 @@ export function walletListMenuAction( if (tokenId == null) { if (fioAddress) { additionalMsg = lstrings.fragmet_wallets_delete_fio_extra_message_mobile - } else if (wallet.currencyInfo.metaTokens.length > 0) { + } else if (Object.keys(wallet.currencyConfig.allTokens).length > 0) { additionalMsg = lstrings.fragmet_wallets_delete_eth_extra_message } } else { diff --git a/src/components/FioAddress/ConnectWallets.tsx b/src/components/FioAddress/ConnectWallets.tsx index e913d45594a..8cf71a651b0 100644 --- a/src/components/FioAddress/ConnectWallets.tsx +++ b/src/components/FioAddress/ConnectWallets.tsx @@ -12,7 +12,7 @@ import { lstrings } from '../../locales/strings' import { connect } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' import { FioConnectionWalletItem } from '../../types/types' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { convertFIOToEdgeCodes, makeConnectWallets } from '../../util/FioAddressUtils' interface LocalState { @@ -159,7 +159,7 @@ class ConnectWallets extends React.Component { const pluginId = wallet.edgeWallet.currencyInfo.pluginId const { tokenCode: currencyCode } = convertFIOToEdgeCodes(pluginId, wallet.chainCode, wallet.currencyCode) - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) return ( diff --git a/src/components/FioAddress/FioActionSubmit.tsx b/src/components/FioAddress/FioActionSubmit.tsx index e698d2a490d..e982c7a62de 100644 --- a/src/components/FioAddress/FioActionSubmit.tsx +++ b/src/components/FioAddress/FioActionSubmit.tsx @@ -117,7 +117,12 @@ class FioActionSubmitComponent extends React.Component { handleWalletPress = () => { Airship.show(bridge => ( - + )) .then(result => { if (result?.type === 'wallet') { diff --git a/src/components/cards/FiatAmountInputCard.tsx b/src/components/cards/FiatAmountInputCard.tsx index 1b9d12ecc28..48854b2db8e 100644 --- a/src/components/cards/FiatAmountInputCard.tsx +++ b/src/components/cards/FiatAmountInputCard.tsx @@ -1,5 +1,5 @@ import { div, mul } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { formatFiatString } from '../../hooks/useFiatText' @@ -15,7 +15,7 @@ interface Props { iconUri: string inputModalMessage: string title: string - tokenId?: string + tokenId: EdgeTokenId onAmountChanged: (fiatAmount: string, nativeCryptoAmount: string) => void } diff --git a/src/components/cards/TappableAccountCard.tsx b/src/components/cards/TappableAccountCard.tsx index 30fa6ea7ca4..a9c17887eb2 100644 --- a/src/components/cards/TappableAccountCard.tsx +++ b/src/components/cards/TappableAccountCard.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { View } from 'react-native' import { cacheStyles } from 'react-native-patina' @@ -21,7 +21,7 @@ interface TappableAccountCardProps { export interface SelectableAsset { customAsset?: CustomAsset paymentMethod?: PaymentMethod - tokenId?: string + tokenId: EdgeTokenId wallet?: EdgeCurrencyWallet } diff --git a/src/components/cards/VisaCardCard.tsx b/src/components/cards/VisaCardCard.tsx index 88b8037442c..f6f9f7ed6ed 100644 --- a/src/components/cards/VisaCardCard.tsx +++ b/src/components/cards/VisaCardCard.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { TouchableOpacity } from 'react-native' import FastImage from 'react-native-fast-image' @@ -24,7 +24,7 @@ export const ioniaPluginIds = Object.keys(SPECIAL_CURRENCY_INFO).filter(pluginId interface Props { wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId navigation: NavigationProp<'transactionList'> } diff --git a/src/components/common/ExchangeRate2.tsx b/src/components/common/ExchangeRate2.tsx index 817ffa4d33c..fcc49a0b039 100644 --- a/src/components/common/ExchangeRate2.tsx +++ b/src/components/common/ExchangeRate2.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { memo } from 'react' @@ -10,7 +10,7 @@ import { EdgeText } from '../themed/EdgeText' interface Props { wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId } export const ExchangeRate2 = memo((props: Props) => { diff --git a/src/components/data/row/CryptoFiatAmountRow.tsx b/src/components/data/row/CryptoFiatAmountRow.tsx index 582b637a0c8..6a2306b0efc 100644 --- a/src/components/data/row/CryptoFiatAmountRow.tsx +++ b/src/components/data/row/CryptoFiatAmountRow.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { View } from 'react-native' @@ -13,7 +13,7 @@ import { EdgeText } from '../../themed/EdgeText' interface Props { marginRem?: number[] | number nativeAmount: string - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } diff --git a/src/components/data/row/CurrencyRow.tsx b/src/components/data/row/CurrencyRow.tsx index c5eb4b8bf57..91d589cf5e8 100644 --- a/src/components/data/row/CurrencyRow.tsx +++ b/src/components/data/row/CurrencyRow.tsx @@ -19,7 +19,7 @@ interface Props { nativeAmount?: string showRate?: boolean token?: EdgeToken - tokenId?: EdgeTokenId + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } diff --git a/src/components/icons/CryptoIcon.tsx b/src/components/icons/CryptoIcon.tsx index f92bf7aff4f..44c5a91ee9f 100644 --- a/src/components/icons/CryptoIcon.tsx +++ b/src/components/icons/CryptoIcon.tsx @@ -14,7 +14,7 @@ import { cacheStyles, Theme, useTheme } from '../services/ThemeContext' interface Props { // Main props - If non is specified, would just render an empty view pluginId?: string // Needed when walletId is not supplied and we still want to get an icon - tokenId?: EdgeTokenId // Needed when it's a token (not the plugin's native currency) + tokenId: EdgeTokenId // Needed when it's a token (not the plugin's native currency) walletId?: string // To allow showing the progress ratio sync circle // Image props @@ -50,7 +50,7 @@ const CryptoIconComponent = (props: Props) => { if (pluginId == null) return null // Get Currency Icon URI - const icon = getCurrencyIconUris(pluginId, tokenId ?? undefined) + const icon = getCurrencyIconUris(pluginId, tokenId) const source = { uri: mono ? icon.symbolImageDarkMono : icon.symbolImage } // Return Currency logo from the edge server @@ -69,7 +69,7 @@ const CryptoIconComponent = (props: Props) => { } // Get Parent Icon URI - const icon = getCurrencyIconUris(pluginId) + const icon = getCurrencyIconUris(pluginId, null) const source = { uri: mono ? icon.symbolImageDarkMono : icon.symbolImage } // Return Parent logo from the edge server diff --git a/src/components/icons/FiatIcon.tsx b/src/components/icons/FiatIcon.tsx index 970ff74e7a2..29ba3218aad 100644 --- a/src/components/icons/FiatIcon.tsx +++ b/src/components/icons/FiatIcon.tsx @@ -26,7 +26,7 @@ export const FiatIconComponent = (props: Props) => { const theme = useTheme() const styles = getStyles(theme) - const fiatBackgroundIcon = getCurrencyIconUris('fiat') + const fiatBackgroundIcon = getCurrencyIconUris('fiat', null) const source = { uri: mono ? fiatBackgroundIcon.symbolImageDarkMono : fiatBackgroundIcon.symbolImage } const fiatSymbol = getSymbolFromCurrency(fixFiatCurrencyCode(fiatCurrencyCode)) const fiatSymbolSizing = { fontSize: theme.rem(sizeRem * 0.625) } diff --git a/src/components/modals/FlipInputModal2.tsx b/src/components/modals/FlipInputModal2.tsx index 456cb6bdbfb..b399026013b 100644 --- a/src/components/modals/FlipInputModal2.tsx +++ b/src/components/modals/FlipInputModal2.tsx @@ -1,5 +1,5 @@ import { div, log10, toFixed } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { memo, useState } from 'react' import { Dimensions, Platform, TouchableWithoutFeedback, View } from 'react-native' @@ -32,7 +32,7 @@ export interface FlipInputModalResult { } export interface SetFeesParams { - feeTokenId?: string + feeTokenId: EdgeTokenId feeNativeAmount: string } export interface FlipInputModalRef { @@ -46,11 +46,11 @@ type FeeStyleTypes = 'dangerText' | 'warningText' interface Props { bridge: AirshipBridge wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId startNativeAmount?: string forceField?: ExchangeFlipInputFields // Fees - feeTokenId?: string + feeTokenId: EdgeTokenId feeNativeAmount?: string feeStyle?: FeeStyleTypes onFeesChange?: () => void @@ -82,9 +82,9 @@ const FlipInputModal2Component = React.forwardRef((pro const exchangedFlipInputRef = React.useRef(null) - const balances = useWatch(wallet, 'balances') + const balanceMap = useWatch(wallet, 'balanceMap') const currencyCode = getCurrencyCode(wallet, tokenId) - const [feeTokenId, setFeeTokenId] = useState(startingFeeTokenId) + const [feeTokenId, setFeeTokenId] = useState(startingFeeTokenId) const [feeNativeAmount, setFeeNativeAmount] = useState(startingFeeNativeAmount) const [errorMessage, setErrorMessage] = useState(null) const [amounts, setAmounts] = useState({ @@ -151,7 +151,7 @@ const FlipInputModal2Component = React.forwardRef((pro const renderBalance = () => { const { multiplier, name } = displayDenom - const balanceCrypto = balances[currencyCode] ?? '0' + const balanceCrypto = balanceMap.get(tokenId) ?? '0' const balance = `${formatNumber(div(balanceCrypto, multiplier, DECIMAL_PRECISION))} ${name} (` const parenString = ')' return ( @@ -181,7 +181,7 @@ const FlipInputModal2Component = React.forwardRef((pro {feeCryptoText} - + {parenString} diff --git a/src/components/modals/InsufficientFeesModal.tsx b/src/components/modals/InsufficientFeesModal.tsx index 265207790c5..2de550c6cb4 100644 --- a/src/components/modals/InsufficientFeesModal.tsx +++ b/src/components/modals/InsufficientFeesModal.tsx @@ -9,6 +9,7 @@ import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { useDispatch } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' +import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { roundedFee } from '../../util/utils' import { MainButton } from '../themed/MainButton' import { ModalMessage, ModalTitle } from '../themed/ModalParts' @@ -35,7 +36,8 @@ export function InsufficientFeesModal(props: Props) { // Get the display amount: const { currencyInfo } = wallet - const { currencyCode = currencyInfo.currencyCode, networkFee = '' } = coreError + const { tokenId, networkFee = '' } = coreError + const currencyCode = getCurrencyCode(wallet, tokenId) const { multiplier, name } = useDisplayDenom(currencyInfo.pluginId, currencyCode) const amountString = roundedFee(networkFee, 2, multiplier) diff --git a/src/components/modals/WalletListMenuModal.tsx b/src/components/modals/WalletListMenuModal.tsx index 75d7e5114f0..03bde3b6c45 100644 --- a/src/components/modals/WalletListMenuModal.tsx +++ b/src/components/modals/WalletListMenuModal.tsx @@ -1,3 +1,4 @@ +import { EdgeTokenId } from 'edge-core-js' import React from 'react' import { Text, TouchableOpacity, View } from 'react-native' import { AirshipBridge } from 'react-native-airship' @@ -30,7 +31,7 @@ interface Props { navigation: NavigationProp<'walletList'> | NavigationProp<'transactionList'> // Wallet identity: - tokenId?: string + tokenId: EdgeTokenId walletId: string } diff --git a/src/components/modals/WalletListModal.tsx b/src/components/modals/WalletListModal.tsx index 7d9379ebc09..0c5cde1f03d 100644 --- a/src/components/modals/WalletListModal.tsx +++ b/src/components/modals/WalletListModal.tsx @@ -1,5 +1,5 @@ import { FlashList, ListRenderItem } from '@shopify/flash-list' -import { EdgeAccount } from 'edge-core-js' +import { EdgeAccount, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { TouchableOpacity, View } from 'react-native' import { AirshipBridge } from 'react-native-airship' @@ -14,7 +14,7 @@ import { config } from '../../theme/appConfig' import { useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' import { BooleanMap, EdgeAsset } from '../../types/types' -import { getCurrencyCode, getTokenId, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' +import { getCurrencyCode, getTokenIdForced, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' import { CustomAsset } from '../data/row/CustomAssetRow' import { PaymentMethodRow } from '../data/row/PaymentMethodRow' import { SearchIconAnimated } from '../icons/ThemedIcons' @@ -34,7 +34,7 @@ export type WalletListResult = | { type: 'wallet' walletId: string - tokenId: string | undefined + tokenId: EdgeTokenId /** @deprecated Use tokenId instead */ currencyCode: string } @@ -68,7 +68,8 @@ interface Props { const keysOnlyModeAssets: EdgeAsset[] = Object.keys(SPECIAL_CURRENCY_INFO) .filter(pluginId => isKeysOnlyPlugin(pluginId)) .map(pluginId => ({ - pluginId + pluginId, + tokenId: null })) export function WalletListModal(props: Props) { @@ -97,6 +98,7 @@ export function WalletListModal(props: Props) { const showCustomAssets = customAssets != null && customAssets.length > 0 const account = useSelector(state => state.core.account) + const currencyWallets = useSelector(state => state.core.account.currencyWallets) const theme = useTheme() const styles = getStyles(theme) @@ -132,13 +134,15 @@ export function WalletListModal(props: Props) { const handlePaymentMethodPress = useHandler((fiatAccountId: string) => () => { bridge.resolve({ type: 'wyre', fiatAccountId }) }) - const handleWalletListPress = useHandler((walletId: string, currencyCode: string, tokenId?: string, customAsset?: CustomAsset) => { + const handleWalletListPress = useHandler((walletId: string, tokenId: EdgeTokenId, customAsset?: CustomAsset) => { if (walletId === '') { handleCancel() showError(lstrings.network_alert_title) } else if (customAsset != null) { bridge.resolve({ type: 'custom', customAsset }) - } else if (walletId != null && currencyCode != null) { + } else { + const wallet = currencyWallets[walletId] + const currencyCode = getCurrencyCode(wallet, tokenId) bridge.resolve({ type: 'wallet', walletId, currencyCode, tokenId }) } }) @@ -324,7 +328,7 @@ export const pickWallet = async ({ if (assets != null && matchingAssets.length === 1 && Object.keys(walletIdMap).length === 1) { // Only one matching wallet and asset. Auto pick the wallet const [walletId, currencyCode] = Object.keys(walletIdMap)[0].split(':') - const tokenId = getTokenId(account, currencyWallets[walletId].currencyInfo.pluginId, currencyCode) + const tokenId = getTokenIdForced(account, currencyWallets[walletId].currencyInfo.pluginId, currencyCode) return { type: 'wallet', walletId, currencyCode, tokenId } } else { const walletListResult = await Airship.show(bridge => ( diff --git a/src/components/modals/WcSmartContractModal.tsx b/src/components/modals/WcSmartContractModal.tsx index f7e056df5b2..f0ca6098e6f 100644 --- a/src/components/modals/WcSmartContractModal.tsx +++ b/src/components/modals/WcSmartContractModal.tsx @@ -11,6 +11,7 @@ import { FlashNotification } from '../../components/navigation/FlashNotification import { useDisplayDenom } from '../../hooks/useDisplayDenom' import { useWalletConnect } from '../../hooks/useWalletConnect' import { lstrings } from '../../locales/strings' +import { asEdgeTokenId } from '../../types/types' import { getCurrencyIconUris } from '../../util/CdnUris' import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' @@ -43,10 +44,10 @@ export const WcSmartContractModal = (props: Props) => { const amountCurrencyCode = getCurrencyCode(wallet, tokenId) - const { currencyCode: feeCurrencyCode, displayName: feeDisplayName, pluginId, metaTokens } = wallet.currencyInfo + const { currencyCode: feeCurrencyCode, displayName: feeDisplayName, pluginId } = wallet.currencyInfo const feeCurrencyStr = `${feeDisplayName} (${feeCurrencyCode})` - const feeCurrencyBalance = wallet.balances[feeCurrencyCode] + const feeCurrencyBalance = wallet.balanceMap.get(null) ?? '0' const amountDenom = useDisplayDenom(pluginId, amountCurrencyCode) const feeDenom = useDisplayDenom(pluginId, feeCurrencyCode) @@ -134,8 +135,7 @@ export const WcSmartContractModal = (props: Props) => { } } - const contractAddress = metaTokens.find(token => token.currencyCode === amountCurrencyCode)?.contractAddress - const walletImageUri = getCurrencyIconUris(pluginId, contractAddress).symbolImage + const walletImageUri = getCurrencyIconUris(pluginId, tokenId).symbolImage const slider = isInsufficientBal ? null : ( ) @@ -164,10 +164,16 @@ export const WcSmartContractModal = (props: Props) => { {dAppName} {zeroString(networkFee) ? null : ( - + )} {zeroString(totalNativeCrypto) ? null : ( - + )} {slider} @@ -245,7 +251,7 @@ export const asWcSmartContractModalProps = asObject({ }), nativeAmount: asString, networkFee: asString, - tokenId: asOptional(asString), + tokenId: asEdgeTokenId, topic: asString, requestId: asNumber, payload: asUnknown diff --git a/src/components/navigation/CurrencySettingsTitle.tsx b/src/components/navigation/CurrencySettingsTitle.tsx index e9b10c968c6..b88a7dea3d1 100644 --- a/src/components/navigation/CurrencySettingsTitle.tsx +++ b/src/components/navigation/CurrencySettingsTitle.tsx @@ -15,7 +15,7 @@ export function CurrencySettingsTitle() { const styles = getStyles(useTheme()) return ( - + {displayName} ) diff --git a/src/components/scenes/AssetSettingsScene.tsx b/src/components/scenes/AssetSettingsScene.tsx index 888ad26cede..ff270640bd5 100644 --- a/src/components/scenes/AssetSettingsScene.tsx +++ b/src/components/scenes/AssetSettingsScene.tsx @@ -28,7 +28,7 @@ export function AssetSettingsScene(props: Props) { return ( - + ) })} diff --git a/src/components/scenes/CreateWalletAccountSelectScene.tsx b/src/components/scenes/CreateWalletAccountSelectScene.tsx index 109b4674c21..2e138b54a22 100644 --- a/src/components/scenes/CreateWalletAccountSelectScene.tsx +++ b/src/components/scenes/CreateWalletAccountSelectScene.tsx @@ -12,7 +12,7 @@ import { getExchangeDenomination } from '../../selectors/DenominationSelectors' import { config } from '../../theme/appConfig' import { useDispatch, useSelector } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced, getWalletTokenId } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { logEvent } from '../../util/tracking' import { ButtonsContainer } from '../buttons/ButtonsContainer' @@ -62,13 +62,14 @@ export const CreateWalletAccountSelectScene = (props: Props) => { `${activationCost} ${selectedWalletType.currencyCode}` ) const confirmMessageSyntax = sprintf(lstrings.create_wallet_account_make_payment_2s, selectedWalletType.currencyCode, existingCoreWallet.name) - const tokenId = getTokenId(account, existingCoreWallet.currencyInfo.pluginId, selectedWalletType.currencyCode) + const tokenId = getTokenIdForced(account, existingCoreWallet.currencyInfo.pluginId, selectedWalletType.currencyCode) const [isCreatingWallet, setIsCreatingWallet] = React.useState(true) const [walletId, setWalletId] = React.useState('') const paymentWallet = account.currencyWallets[walletId] const isRenderSelect = walletId === '' || walletAccountActivationQuoteError + const paymentTokenId = getWalletTokenId(paymentWallet, paymentCurrencyCode) const handleRenameAndReturnWallet = useHandler(async () => { await existingCoreWallet.renameWallet(accountName) @@ -132,9 +133,9 @@ export const CreateWalletAccountSelectScene = (props: Props) => { ) : ( } + icon={} leftText={getWalletName(paymentWallet)} - leftSubtext={`${lstrings.send_confirmation_balance}: ${paymentWallet.balances[paymentCurrencyCode]} ${paymentCurrencyCode}`} + leftSubtext={`${lstrings.send_confirmation_balance}: ${paymentWallet.balanceMap.get(paymentTokenId)} ${paymentCurrencyCode}`} rightText={`${paymentDenominationSymbol} ${amount} ${paymentCurrencyCode}`} rightSubText={`≈ ${activationCost} ${selectedWalletType.currencyCode}`} /> diff --git a/src/components/scenes/CreateWalletAccountSetupScene.tsx b/src/components/scenes/CreateWalletAccountSetupScene.tsx index 2fac7387a3f..c43c5bc5e4e 100644 --- a/src/components/scenes/CreateWalletAccountSetupScene.tsx +++ b/src/components/scenes/CreateWalletAccountSetupScene.tsx @@ -63,7 +63,7 @@ export function CreateWalletAccountSetupScene(props: Props): JSX.Element { .finally(() => setSpinning(false)) } - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenId(account, pluginId, currencyCode) ?? null useMount(() => logEvent('Activate_Wallet_Start')) diff --git a/src/components/scenes/CreateWalletCompletionScene.tsx b/src/components/scenes/CreateWalletCompletionScene.tsx index 4ae61fefac0..48b55629907 100644 --- a/src/components/scenes/CreateWalletCompletionScene.tsx +++ b/src/components/scenes/CreateWalletCompletionScene.tsx @@ -122,7 +122,7 @@ const CreateWalletCompletionComponent = (props: Props) => { const renderRow = useHandler(({ item }) => { if (item.walletType != null) { // Mainnet - return + return } else if (item.key === tokenKey) { // Single token row const tokenNameString = newTokenItems.map(item => item.currencyCode).join(', ') diff --git a/src/components/scenes/CreateWalletImportOptionsScene.tsx b/src/components/scenes/CreateWalletImportOptionsScene.tsx index 21b04a973f6..73a8599a896 100644 --- a/src/components/scenes/CreateWalletImportOptionsScene.tsx +++ b/src/components/scenes/CreateWalletImportOptionsScene.tsx @@ -145,7 +145,7 @@ const CreateWalletImportOptionsComponent = (props: Props) => { return ( - + {currencyConfig[pluginId].currencyInfo.displayName} {arr.map(opt => { diff --git a/src/components/scenes/CreateWalletSelectCryptoScene.tsx b/src/components/scenes/CreateWalletSelectCryptoScene.tsx index 7a7741ed901..59d7976f623 100644 --- a/src/components/scenes/CreateWalletSelectCryptoScene.tsx +++ b/src/components/scenes/CreateWalletSelectCryptoScene.tsx @@ -143,6 +143,7 @@ const CreateWalletSelectCryptoComponent = (props: Props) => { return ( { bridge.resolve(PLACEHOLDER_WALLET_ID) @@ -153,7 +154,7 @@ const CreateWalletSelectCryptoComponent = (props: Props) => { } const wallet = currencyWallets[walletId] - return bridge.resolve(walletId)} /> + return bridge.resolve(walletId)} /> } const displayNames = newTokenItems diff --git a/src/components/scenes/CreateWalletSelectFiatScene.tsx b/src/components/scenes/CreateWalletSelectFiatScene.tsx index 780002d0cb0..a8f75deae57 100644 --- a/src/components/scenes/CreateWalletSelectFiatScene.tsx +++ b/src/components/scenes/CreateWalletSelectFiatScene.tsx @@ -184,6 +184,7 @@ const CreateWalletSelectFiatComponent = (props: Props) => { return ( await handleEditWalletName(key, walletName)} rightSide={chevron} diff --git a/src/components/scenes/CryptoExchangeScene.tsx b/src/components/scenes/CryptoExchangeScene.tsx index 77343d9088f..ee729c26a7b 100644 --- a/src/components/scenes/CryptoExchangeScene.tsx +++ b/src/components/scenes/CryptoExchangeScene.tsx @@ -1,5 +1,5 @@ import { div, gt, gte } from 'biggystring' -import { EdgeAccount } from 'edge-core-js' +import { EdgeAccount, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Keyboard, View } from 'react-native' import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' @@ -14,7 +14,7 @@ import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' import { EdgeSceneProps, NavigationBase } from '../../types/routerTypes' import { emptyCurrencyInfo, GuiCurrencyInfo } from '../../types/types' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenId, getWalletTokenId } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { DECIMAL_PRECISION, zeroString } from '../../util/utils' import { InsetStyles, SceneWrapper } from '../common/SceneWrapper' @@ -36,8 +36,9 @@ interface StateProps { exchangeInfo: ExchangeInfo // The following props are used to populate the CryptoExchangeFlipInputs + fromTokenId: EdgeTokenId fromWalletId: string - fromWalletBalances: { [code: string]: string } + fromWalletBalanceMap: Map fromWalletName: string fromExchangeAmount: string fromWalletPrimaryInfo: GuiCurrencyInfo @@ -77,8 +78,9 @@ interface State { } const defaultFromWalletInfo = { + fromTokenId: null, fromCurrencyCode: '', - fromWalletBalances: {}, + fromWalletBalanceMap: new Map(), fromWalletName: '', fromWalletPrimaryInfo: emptyCurrencyInfo, fromExchangeAmount: '', @@ -192,9 +194,9 @@ export class CryptoExchangeComponent extends React.Component { } checkExceedsAmount(): boolean { - const { fromCurrencyCode, fromWalletBalances } = this.props + const { fromTokenId, fromWalletBalanceMap } = this.props const { fromAmountNative, whichWalletFocus } = this.state - const fromNativeBalance = fromWalletBalances[fromCurrencyCode] ?? '0' + const fromNativeBalance = fromWalletBalanceMap.get(fromTokenId) ?? '0' return whichWalletFocus === 'from' && gte(fromNativeBalance, '0') && gt(fromAmountNative, fromNativeBalance) } @@ -240,10 +242,10 @@ export class CryptoExchangeComponent extends React.Component { } renderAlert = () => { - const { fromWalletBalances, fromCurrencyCode, insufficient, genericError, pluginId } = this.props + const { fromWalletBalanceMap, fromTokenId, insufficient, genericError, pluginId } = this.props const { minimumPopupModals } = getSpecialCurrencyInfo(pluginId) - const primaryNativeBalance = fromWalletBalances[fromCurrencyCode] ?? '0' + const primaryNativeBalance = fromWalletBalanceMap.get(fromTokenId) ?? '0' if (minimumPopupModals != null && primaryNativeBalance < minimumPopupModals.minimumNativeBalance) { return @@ -375,22 +377,25 @@ export const CryptoExchangeScene = (props: OwnProps) => { } if (fromWalletId != null && currencyWallets[fromWalletId] != null) { + const fromWallet = currencyWallets[fromWalletId] const { fromNativeAmount, fromWalletPrimaryInfo } = cryptoExchange const { exchangeDenomination: { multiplier }, exchangeCurrencyCode } = fromWalletPrimaryInfo + const fromTokenId = getWalletTokenId(fromWallet, exchangeCurrencyCode) const fromWalletName = getWalletName(currencyWallets[fromWalletId]) const { currencyInfo: { pluginId }, - balances: fromWalletBalances + balanceMap: fromWalletBalanceMap } = currencyWallets[fromWalletId] Object.assign(result, { + fromTokenId, fromWalletId, fromWalletName, - fromWalletBalances, + fromWalletBalanceMap, fromCurrencyCode: exchangeCurrencyCode, fromWalletPrimaryInfo, fromExchangeAmount: div(fromNativeAmount, multiplier, DECIMAL_PRECISION), diff --git a/src/components/scenes/ExchangedFlipInputTester.tsx b/src/components/scenes/ExchangedFlipInputTester.tsx index e9489613b2f..51aa42d7156 100644 --- a/src/components/scenes/ExchangedFlipInputTester.tsx +++ b/src/components/scenes/ExchangedFlipInputTester.tsx @@ -24,7 +24,7 @@ export function ExchangedFlipInputTester(props: {}) { const [value0, setValue0] = useState('') const [value1, setValue1] = useState('') const walletId = selectedWallet?.wallet.id ?? '' - const tokenId = selectedWallet?.tokenId + const tokenId = selectedWallet?.tokenId ?? null const exchangedFlipInputRef = React.useRef(null) const onAmountChanged = (amounts: ExchangedFlipInputAmounts): void => { @@ -52,13 +52,12 @@ export function ExchangedFlipInputTester(props: {}) { if (selectedWallet == null) return Airship.show(bridge => { if (selectedWallet == null) return null - return + return }).catch(error => console.log(error)) } const coreWallet = selectedWallet?.wallet - const currencyCode = coreWallet?.currencyInfo.currencyCode ?? '' - let balance = coreWallet?.balances[currencyCode] ?? '' + let balance = coreWallet?.balanceMap.get(tokenId) ?? '' if (eq(balance, '0')) balance = '' const headerText = 'Select Wallet' const headerCallback = () => console.log('Header pressed') diff --git a/src/components/scenes/Fio/FioAddressRegisterScene.tsx b/src/components/scenes/Fio/FioAddressRegisterScene.tsx index 958a600215c..a1f0a39a47c 100644 --- a/src/components/scenes/Fio/FioAddressRegisterScene.tsx +++ b/src/components/scenes/Fio/FioAddressRegisterScene.tsx @@ -92,7 +92,7 @@ export class FioAddressRegister extends React.Component { checkFreeAddress = async () => { try { const { fioPlugin } = this.props - const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings.freeAddressRef) + const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings?.freeAddressRef) if (publicDomains.findIndex((publicDomain: FioPublicDomain) => publicDomain.free) > -1) { this.setState({ showFreeAddressLink: true }) } @@ -105,7 +105,7 @@ export class FioAddressRegister extends React.Component { getPublicDomains = async () => { const { fioPlugin } = this.props try { - const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings.fallbackRef) + const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings?.fallbackRef) const publicDomainsConverted = publicDomains // @ts-expect-error .sort(publicDomain => (publicDomain.domain === FIO_DOMAIN_DEFAULT.name ? -1 : 1)) @@ -149,7 +149,7 @@ export class FioAddressRegister extends React.Component { if (!fioWallets.length) return if (!selectedWallet) return const publicKey = selectedWallet.publicWalletInfo.keys.publicKey - const url = `${fioPlugin.currencyInfo.defaultSettings.fioAddressRegUrl}${fioPlugin.currencyInfo.defaultSettings.freeAddressRef}?publicKey=${publicKey}` + const url = `${fioPlugin.currencyInfo.defaultSettings?.fioAddressRegUrl}${fioPlugin.currencyInfo.defaultSettings?.freeAddressRef}?publicKey=${publicKey}` try { await openLink(url) } catch (e: any) { @@ -294,7 +294,12 @@ export class FioAddressRegister extends React.Component { selectFioWallet = async () => { await Airship.show(bridge => ( - + )).then(result => { if (result?.type === 'wallet') { const { walletId } = result diff --git a/src/components/scenes/Fio/FioAddressRegisterSelectWalletScene.tsx b/src/components/scenes/Fio/FioAddressRegisterSelectWalletScene.tsx index 89633dcdeb2..9e79afa3f89 100644 --- a/src/components/scenes/Fio/FioAddressRegisterSelectWalletScene.tsx +++ b/src/components/scenes/Fio/FioAddressRegisterSelectWalletScene.tsx @@ -12,7 +12,7 @@ import { connect } from '../../../types/reactRedux' import { RootState } from '../../../types/reduxTypes' import { EdgeSceneProps } from '../../../types/routerTypes' import { EdgeAsset } from '../../../types/types' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { getWalletName } from '../../../util/CurrencyWalletHelpers' import { getRegInfo } from '../../../util/FioAddressUtils' import { SceneWrapper } from '../../common/SceneWrapper' @@ -154,7 +154,7 @@ export class FioAddressRegisterSelectWallet extends React.Component { walletId: wallet.id, tokenCode: wallet.currencyCode, chainCode: wallet.chainCode, - publicAddress: (await wallet.edgeWallet.getReceiveAddress()).publicAddress + publicAddress: (await wallet.edgeWallet.getReceiveAddress({ tokenId: null })).publicAddress })) let publicAddresses = await Promise.all(promiseArray) @@ -70,7 +70,7 @@ export class FioConnectWalletConfirm extends React.Component { walletId: wallet.id, tokenCode: wallet.currencyCode, chainCode: wallet.chainCode, - publicAddress: (await wallet.edgeWallet.getReceiveAddress()).publicAddress + publicAddress: (await wallet.edgeWallet.getReceiveAddress({ tokenId: null })).publicAddress })) publicAddresses = await Promise.all(promiseArray) diff --git a/src/components/scenes/Fio/FioCreateHandleScene.tsx b/src/components/scenes/Fio/FioCreateHandleScene.tsx index e56e41e49e1..81b239e5cf9 100644 --- a/src/components/scenes/Fio/FioCreateHandleScene.tsx +++ b/src/components/scenes/Fio/FioCreateHandleScene.tsx @@ -112,7 +112,7 @@ export const FioCreateHandleScene = (props: Props) => { body: JSON.stringify({ address: fioAccountName, referralCode: freeRegRefCode, - publicKey: (await wallet.getReceiveAddress()).publicAddress, + publicKey: (await wallet.getReceiveAddress({ tokenId: null })).publicAddress, redirectUrl: '', apiToken: freeRegApiToken }) diff --git a/src/components/scenes/Fio/FioDomainRegisterScene.tsx b/src/components/scenes/Fio/FioDomainRegisterScene.tsx index 9d3d2168e56..f5aa0b9c931 100644 --- a/src/components/scenes/Fio/FioDomainRegisterScene.tsx +++ b/src/components/scenes/Fio/FioDomainRegisterScene.tsx @@ -183,7 +183,12 @@ export class FioDomainRegister extends React.PureComponent { selectFioWallet = async () => { const result = await Airship.show(bridge => ( - + )) if (result?.type === 'wallet') { const { walletId } = result diff --git a/src/components/scenes/Fio/FioDomainRegisterSelectWalletScene.tsx b/src/components/scenes/Fio/FioDomainRegisterSelectWalletScene.tsx index cec05110269..7103b9b2781 100644 --- a/src/components/scenes/Fio/FioDomainRegisterSelectWalletScene.tsx +++ b/src/components/scenes/Fio/FioDomainRegisterSelectWalletScene.tsx @@ -13,7 +13,7 @@ import { connect } from '../../../types/reactRedux' import { RootState } from '../../../types/reduxTypes' import { EdgeSceneProps } from '../../../types/routerTypes' import { EdgeAsset } from '../../../types/types' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { getWalletName } from '../../../util/CurrencyWalletHelpers' import { getDomainRegInfo } from '../../../util/FioAddressUtils' import { SceneWrapper } from '../../common/SceneWrapper' @@ -142,7 +142,7 @@ class FioDomainRegisterSelectWallet extends React.PureComponent { const { fioDomainName, fioWallet } = route.params const sendParams: SendScene2Params = { + tokenId: null, spendInfo: { + tokenId: null, spendTargets: [{ nativeAmount: '', publicAddress: '' }], otherParams: { action: { diff --git a/src/components/scenes/Fio/FioNameConfirmScene.tsx b/src/components/scenes/Fio/FioNameConfirmScene.tsx index 0defdff64b8..11319eaa7bc 100644 --- a/src/components/scenes/Fio/FioNameConfirmScene.tsx +++ b/src/components/scenes/Fio/FioNameConfirmScene.tsx @@ -54,14 +54,14 @@ class FioNameConfirm extends React.PureComponent { const response = await fioPlugin.otherMethods.buyAddressRequest( { address: fioName, - referralCode: fioPlugin.currencyInfo.defaultSettings.defaultRef, + referralCode: fioPlugin.currencyInfo.defaultSettings?.defaultRef, publicKey: ownerPublicKey }, true ) if (response.error) { if (response.errorCode && response.errorCode === ONE_FREE_ADDRESS_PER_DOMAIN_ERROR && response.code === 400) { - const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings.fallbackRef) + const publicDomains = await fioPlugin.otherMethods.getDomains(fioPlugin.currencyInfo.defaultSettings?.fallbackRef) // @ts-expect-error const domainExists = publicDomains.find(domain => domain.domain === fioName.split(FIO_ADDRESS_DELIMITER)[1]) if (domainExists && !domainExists.free) { diff --git a/src/components/scenes/Fio/FioRequestConfirmationScene.tsx b/src/components/scenes/Fio/FioRequestConfirmationScene.tsx index 6121868a347..5c1b6fab323 100644 --- a/src/components/scenes/Fio/FioRequestConfirmationScene.tsx +++ b/src/components/scenes/Fio/FioRequestConfirmationScene.tsx @@ -11,7 +11,7 @@ import { getExchangeRate, getSelectedCurrencyWallet } from '../../../selectors/W import { connect } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' import { emptyCurrencyInfo, GuiCurrencyInfo } from '../../../types/types' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { addToFioAddressCache, checkPubAddress, @@ -115,7 +115,7 @@ export class FioRequestConfirmationConnected extends React.Component fioAddress === fioAddressFrom) - const { publicAddress } = await edgeWallet.getReceiveAddress() + const { publicAddress } = await edgeWallet.getReceiveAddress({ tokenId: null }) if (walletAddress && fioPlugin) { const { fioWallet } = walletAddress @@ -355,7 +355,7 @@ export const FioRequestConfirmationScene = connect( const primaryCurrencyInfo: GuiCurrencyInfo = { walletId: state.ui.wallets.selectedWalletId, - tokenId: getTokenId(account, selectedWallet.currencyInfo.pluginId, currencyCode), + tokenId: getTokenIdForced(account, selectedWallet.currencyInfo.pluginId, currencyCode), displayCurrencyCode: currencyCode, displayDenomination: primaryDisplayDenomination, exchangeCurrencyCode: primaryExchangeCurrencyCode, diff --git a/src/components/scenes/Fio/FioRequestListScene.tsx b/src/components/scenes/Fio/FioRequestListScene.tsx index a32f4ade8aa..f41cb008234 100644 --- a/src/components/scenes/Fio/FioRequestListScene.tsx +++ b/src/components/scenes/Fio/FioRequestListScene.tsx @@ -12,7 +12,7 @@ import { getExchangeDenominationFromState } from '../../../selectors/Denominatio import { connect } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' import { FioAddress, FioRequest } from '../../../types/types' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { addToFioAddressCache, cancelFioRequest, @@ -373,7 +373,7 @@ class FioRequestList extends React.Component { } const { tokenCode } = convertFIOToEdgeCodes(pluginId, content.chain_code.toUpperCase(), content.token_code.toUpperCase()) - const tokenId = getTokenId(account, pluginId, tokenCode) + const tokenId = getTokenIdForced(account, pluginId, tokenCode) const allowedAssets = [{ pluginId, tokenId }] const result = await Airship.show(bridge => ( @@ -398,7 +398,7 @@ class FioRequestList extends React.Component { const parsedUri = await currencyWallet.parseUri(pendingRequest.content.payee_public_address, currencyCode) const { pluginId } = currencyWallet.currencyInfo - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) const memos: EdgeMemo[] | undefined = parsedUri.uniqueIdentifier != null ? [{ type: 'text', value: parsedUri.uniqueIdentifier }] : undefined const sendParams: SendScene2Params = { @@ -406,6 +406,7 @@ class FioRequestList extends React.Component { fioPendingRequest: pendingRequest, tokenId, spendInfo: { + tokenId, metadata: parsedUri.metadata, spendTargets: [ { diff --git a/src/components/scenes/Fio/FioStakingChangeScene.tsx b/src/components/scenes/Fio/FioStakingChangeScene.tsx index 51799939265..7a6dd040966 100644 --- a/src/components/scenes/Fio/FioStakingChangeScene.tsx +++ b/src/components/scenes/Fio/FioStakingChangeScene.tsx @@ -15,6 +15,7 @@ import { getDisplayDenomination, getExchangeDenomination } from '../../../select import { convertCurrencyFromExchangeRates } from '../../../selectors/WalletSelectors' import { useDispatch, useSelector } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' +import { getCurrencyCode } from '../../../util/CurrencyInfoHelpers' import { FioStakingBalanceType, getFioStakingBalances } from '../../../util/stakeUtils' import { convertNativeToDenomination } from '../../../util/utils' import { SceneWrapper } from '../../common/SceneWrapper' @@ -41,7 +42,7 @@ export const FioStakingChangeScene = withWallet((props: Props) => { const { wallet: currencyWallet, route: { - params: { change, currencyCode, walletId } + params: { change, tokenId, walletId } }, navigation } = props @@ -62,7 +63,7 @@ export const FioStakingChangeScene = withWallet((props: Props) => { const dispatch = useDispatch() const currencyPlugin = useSelector(state => state.core.account.currencyConfig[pluginId]) - + const currencyCode = getCurrencyCode(currencyWallet, tokenId) const currencyDenomination = useSelector(state => getDisplayDenomination(state, pluginId, currencyCode)) const defaultDenomination = useSelector(state => getExchangeDenomination(state, pluginId, currencyCode)) const exchangeRates = useSelector(state => state.exchangeRates) @@ -116,7 +117,7 @@ export const FioStakingChangeScene = withWallet((props: Props) => { case 'add': { await currencyWallet .getMaxSpendable({ - currencyCode, + tokenId, spendTargets: [{ publicAddress: '' }], otherParams: { action: { @@ -171,6 +172,8 @@ export const FioStakingChangeScene = withWallet((props: Props) => { { const { [change]: actionName } = SPECIAL_CURRENCY_INFO[pluginId]?.stakeActions ?? { [change]: '' } currencyWallet .makeSpend({ + tokenId: null, spendTargets: [ { nativeAmount, diff --git a/src/components/scenes/Fio/FioStakingOverviewScene.tsx b/src/components/scenes/Fio/FioStakingOverviewScene.tsx index 69c11b84a71..16ce2300f33 100644 --- a/src/components/scenes/Fio/FioStakingOverviewScene.tsx +++ b/src/components/scenes/Fio/FioStakingOverviewScene.tsx @@ -15,6 +15,7 @@ import { getDisplayDenomination, getExchangeDenomination } from '../../../select import { convertCurrency } from '../../../selectors/WalletSelectors' import { connect } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' +import { getCurrencyCode } from '../../../util/CurrencyInfoHelpers' import { getFioStakingBalances } from '../../../util/stakeUtils' import { convertNativeToDenomination } from '../../../util/utils' import { SceneWrapper } from '../../common/SceneWrapper' @@ -50,7 +51,7 @@ export const FioStakingOverviewSceneComponent = (props: Props) => { navigation, theme, route: { - params: { currencyCode, walletId } + params: { tokenId, walletId } }, currencyWallet, stakingCryptoAmountFormat, @@ -63,6 +64,7 @@ export const FioStakingOverviewSceneComponent = (props: Props) => { const styles = getStyles(theme) const [locks, setLocks] = React.useState([]) const stakingStatus = useWatch(currencyWallet, 'stakingStatus') + const currencyCode = getCurrencyCode(currencyWallet, tokenId) useAsyncEffect(async () => { await refreshAllFioAddresses() @@ -87,10 +89,10 @@ export const FioStakingOverviewSceneComponent = (props: Props) => { }, [stakingStatus, currencyDenomination]) const handlePressStake = () => { - navigation.navigate('fioStakingChange', { change: 'add', currencyCode, walletId }) + navigation.navigate('fioStakingChange', { change: 'add', tokenId, walletId }) } const handlePressUnstake = () => { - navigation.navigate('fioStakingChange', { change: 'remove', currencyCode, walletId }) + navigation.navigate('fioStakingChange', { change: 'remove', tokenId, walletId }) } const renderItems = () => @@ -163,10 +165,11 @@ export const FioStakingOverviewScene = connect { const { route: { - params: { walletId, currencyCode } + params: { walletId, tokenId } } } = ownProps const currencyWallet = state.core.account.currencyWallets[walletId] + const currencyCode = getCurrencyCode(currencyWallet, tokenId) const { staked } = getFioStakingBalances(currencyWallet.stakingStatus) const stakedNativeAmount = staked diff --git a/src/components/scenes/Loans/LoanCreateConfirmationScene.tsx b/src/components/scenes/Loans/LoanCreateConfirmationScene.tsx index 894135266a8..0d6a359f80e 100644 --- a/src/components/scenes/Loans/LoanCreateConfirmationScene.tsx +++ b/src/components/scenes/Loans/LoanCreateConfirmationScene.tsx @@ -50,7 +50,7 @@ export const LoanCreateConfirmationScene = (props: Props) => { const clientId = useSelector(state => state.core.context.clientId) const executionContext = useExecutionContext() - const borrowWalletNativeBalance = useWalletBalance(borrowEngineWallet) + const borrowWalletNativeBalance = useWalletBalance(borrowEngineWallet, null) const isCrossChainSrc = srcWallet.id !== borrowEngineWallet.id const existingLoanAccount = useSelector(state => selectLoanAccount(state, borrowEngineWallet.id)) @@ -73,7 +73,7 @@ export const LoanCreateConfirmationScene = (props: Props) => { const source: LoanAsset = { wallet: srcWallet, nativeAmount: nativeSrcAmount, - ...(srcTokenId != null ? { tokenId: srcTokenId } : {}) + tokenId: srcTokenId } const destination: LoanAsset = { @@ -127,6 +127,7 @@ export const LoanCreateConfirmationScene = (props: Props) => { fromWalletId: srcWallet.id, fromTokenId: srcTokenId, toWalletId: borrowEngineWallet.id, + toTokenId: null, nativeAmount: feeNativeAmount, expectedPayoutNativeAmount: feeDeficitNativeAmount, amountFor: 'to' diff --git a/src/components/scenes/Loans/LoanCreateScene.tsx b/src/components/scenes/Loans/LoanCreateScene.tsx index cd508e69156..0194182cf24 100644 --- a/src/components/scenes/Loans/LoanCreateScene.tsx +++ b/src/components/scenes/Loans/LoanCreateScene.tsx @@ -1,5 +1,5 @@ import { div, lt, max, mul } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator, TouchableOpacity } from 'react-native' import { AirshipBridge } from 'react-native-airship' @@ -26,7 +26,7 @@ import { useSelector } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' import { getWalletPickerExcludeWalletIds } from '../../../util/borrowUtils' import { getBorrowPluginIconUri } from '../../../util/CdnUris' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenId, getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { enableToken } from '../../../util/CurrencyWalletHelpers' import { DECIMAL_PRECISION, truncateDecimals, zeroString } from '../../../util/utils' import { Card } from '../../cards/Card' @@ -83,9 +83,12 @@ export const LoanCreateScene = (props: Props) => { // user selected src/dest that don't involve the borrowEngineWallet. // Currently, the only use case is selecting fiat (bank) as a src/dest. const hardCollateralCurrencyCode = 'WBTC' - const hardSrcTokenAddr = React.useMemo(() => getTokenId(account, borrowEnginePluginId, hardCollateralCurrencyCode), [account, borrowEnginePluginId]) - const hardDestTokenAddr = React.useMemo(() => getTokenId(account, borrowEnginePluginId, 'USDC'), [account, borrowEnginePluginId]) - const hardAllowedSrcAsset = [{ pluginId: borrowEnginePluginId, tokenId: hardSrcTokenAddr }, { pluginId: 'bitcoin' }] + const hardSrcTokenAddr = React.useMemo(() => getTokenIdForced(account, borrowEnginePluginId, hardCollateralCurrencyCode), [account, borrowEnginePluginId]) + const hardDestTokenAddr = React.useMemo(() => getTokenIdForced(account, borrowEnginePluginId, 'USDC'), [account, borrowEnginePluginId]) + const hardAllowedSrcAsset = [ + { pluginId: borrowEnginePluginId, tokenId: hardSrcTokenAddr }, + { pluginId: 'bitcoin', tokenId: null } + ] const hardAllowedDestAsset = [{ pluginId: borrowEnginePluginId, tokenId: hardDestTokenAddr }] const ltvRatio = borrowPlugin.borrowInfo.maxLtvRatio.toString() @@ -112,7 +115,7 @@ export const LoanCreateScene = (props: Props) => { // #region Source Wallet Data const [srcWalletId, setSrcWalletId] = React.useState(undefined) - const [srcTokenId, setSrcTokenId] = React.useState(undefined) + const [srcTokenId, setSrcTokenId] = React.useState(null) const [srcCurrencyCode, setSrcCurrencyCode] = React.useState(undefined) const srcWallet = srcWalletId == null ? undefined : wallets[srcWalletId] @@ -131,7 +134,7 @@ export const LoanCreateScene = (props: Props) => { // #region Destination Wallet/Bank Data const [destWallet, setDestWallet] = React.useState(undefined) - const [destTokenId, setDestTokenId] = React.useState(undefined) + const [destTokenId, setDestTokenId] = React.useState(null) const [destBankId, setDestBankId] = React.useState(undefined) const [bankAccountsMap, setBankAccountsMap] = React.useState<{ [paymentMethodId: string]: PaymentMethod } | undefined>(undefined) diff --git a/src/components/scenes/Loans/LoanDashboardScene.tsx b/src/components/scenes/Loans/LoanDashboardScene.tsx index b6c91bfdf63..54ea757840a 100644 --- a/src/components/scenes/Loans/LoanDashboardScene.tsx +++ b/src/components/scenes/Loans/LoanDashboardScene.tsx @@ -95,7 +95,7 @@ export const LoanDashboardScene = (props: Props) => { let newLoanWallet if (hardPluginWalletIds.length > 1) { - const allowedAssets = SUPPORTED_WALLET_PLUGIN_IDS.map(pluginId => ({ pluginId })) + const allowedAssets = SUPPORTED_WALLET_PLUGIN_IDS.map(pluginId => ({ pluginId, tokenId: null })) // Only show the wallet picker if the user owns more than one polygon wallet. const result = await Airship.show(bridge => ( diff --git a/src/components/scenes/Loans/LoanDetailsScene.tsx b/src/components/scenes/Loans/LoanDetailsScene.tsx index f5903d0020b..b70fc151ab8 100644 --- a/src/components/scenes/Loans/LoanDetailsScene.tsx +++ b/src/components/scenes/Loans/LoanDetailsScene.tsx @@ -1,5 +1,5 @@ import { add, div, gt, max, mul, sub } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator, TouchableOpacity } from 'react-native' import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' @@ -304,7 +304,7 @@ const getStyles = cacheStyles((theme: Theme) => ({ export const LoanDetailsScene = withLoanAccount(LoanDetailsSceneComponent) -export const useFiatTotal = (wallet: EdgeCurrencyWallet, tokenAmounts: Array<{ tokenId?: string; nativeAmount: string }>): string => { +export const useFiatTotal = (wallet: EdgeCurrencyWallet, tokenAmounts: Array<{ tokenId: EdgeTokenId; nativeAmount: string }>): string => { const exchangeRates = useSelector(state => state.exchangeRates) return tokenAmounts.reduce((sum, tokenAmount) => { @@ -320,7 +320,7 @@ export const displayFiatTotal = (wallet: EdgeCurrencyWallet, fiatAmount: string) return `${fiatSymbol}${formatFiatString({ autoPrecision: true, fiatAmount })}` } -export const calculateFiatAmount = (wallet: EdgeCurrencyWallet, exchangeRates: GuiExchangeRates, tokenId: string | undefined, nativeAmount: string): string => { +export const calculateFiatAmount = (wallet: EdgeCurrencyWallet, exchangeRates: GuiExchangeRates, tokenId: EdgeTokenId, nativeAmount: string): string => { if (tokenId == null) return '0' // TODO: Support wrapped native token const token = getToken(wallet, tokenId) diff --git a/src/components/scenes/Loans/LoanManageScene.tsx b/src/components/scenes/Loans/LoanManageScene.tsx index ac3681a1123..27e841e803c 100644 --- a/src/components/scenes/Loans/LoanManageScene.tsx +++ b/src/components/scenes/Loans/LoanManageScene.tsx @@ -30,7 +30,7 @@ import { EdgeSceneProps } from '../../../types/routerTypes' import { LoanAsset, makeAaveBorrowAction, makeAaveDepositAction } from '../../../util/ActionProgramUtils' import { getWalletPickerExcludeWalletIds } from '../../../util/borrowUtils' import { getBorrowPluginIconUri } from '../../../util/CdnUris' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { getExecutionNetworkFees } from '../../../util/networkFeeUtils' import { zeroString } from '../../../util/utils' import { FiatAmountInputCard } from '../../cards/FiatAmountInputCard' @@ -135,10 +135,10 @@ export const LoanManageSceneComponent = (props: Props) => { // Src/dest Wallet Picker const wallets = useWatch(account, 'currencyWallets') - const hardDebtTokenId = React.useMemo(() => getTokenId(account, borrowEnginePluginId, 'USDC'), [account, borrowEnginePluginId]) - const hardCollateralTokenId = React.useMemo(() => getTokenId(account, borrowEnginePluginId, 'WBTC'), [account, borrowEnginePluginId]) + const hardDebtTokenId = React.useMemo(() => getTokenIdForced(account, borrowEnginePluginId, 'USDC'), [account, borrowEnginePluginId]) + const hardCollateralTokenId = React.useMemo(() => getTokenIdForced(account, borrowEnginePluginId, 'WBTC'), [account, borrowEnginePluginId]) const hardAllowedCollateralAssets = [{ pluginId: borrowEnginePluginId, tokenId: hardCollateralTokenId }] - if (loanManageType === 'loan-manage-deposit') hardAllowedCollateralAssets.push({ pluginId: 'bitcoin', tokenId: undefined }) + if (loanManageType === 'loan-manage-deposit') hardAllowedCollateralAssets.push({ pluginId: 'bitcoin', tokenId: null }) const hardAllowedDebtAssets = [{ pluginId: borrowEnginePluginId, tokenId: hardDebtTokenId }] // Selected debt/collateral @@ -261,7 +261,7 @@ export const LoanManageSceneComponent = (props: Props) => { nativeAmount: actionNativeAmount, walletId: borrowEngineWallet.id, tokenId: hardAllowedDebtAssets[0].tokenId, - fromTokenId: selectedAsset.customAsset != null ? hardAllowedCollateralAssets[0].tokenId : undefined + fromTokenId: selectedAsset.customAsset != null ? hardAllowedCollateralAssets[0].tokenId : null } ] } diff --git a/src/components/scenes/ManageTokensScene.tsx b/src/components/scenes/ManageTokensScene.tsx index 1682af2577f..a1051768f5d 100644 --- a/src/components/scenes/ManageTokensScene.tsx +++ b/src/components/scenes/ManageTokensScene.tsx @@ -123,7 +123,7 @@ function ManageTokensSceneComponent(props: Props) { return ( - } text={walletName} /> + <Title leftIcon={<CryptoIcon sizeRem={1.5} tokenId={null} walletId={wallet.id} />} text={walletName} /> <EdgeText style={styles.subTitle}>{lstrings.managetokens_top_instructions}</EdgeText> <Space top={1}> <FilledTextInput diff --git a/src/components/scenes/MigrateWalletCalculateFeeScene.tsx b/src/components/scenes/MigrateWalletCalculateFeeScene.tsx index 6fddc9b2d6c..aa07f49b5b2 100644 --- a/src/components/scenes/MigrateWalletCalculateFeeScene.tsx +++ b/src/components/scenes/MigrateWalletCalculateFeeScene.tsx @@ -90,7 +90,7 @@ const MigrateWalletCalculateFeeComponent = (props: Props) => { networkFee: fee, ourReceiveAddresses: [], signedTx: '', - tokenId: tokenId ?? null, + tokenId: null, txid: '', walletId } @@ -153,9 +153,9 @@ const MigrateWalletCalculateFeeComponent = (props: Props) => { const assetPromises = bundle.map((asset, i) => { return async () => { - const publicAddress = SPECIAL_CURRENCY_INFO[pluginId].dummyPublicAddress ?? (await wallet.getReceiveAddress()).publicAddress + const publicAddress = SPECIAL_CURRENCY_INFO[pluginId].dummyPublicAddress ?? (await wallet.getReceiveAddress({ tokenId: null })).publicAddress const spendInfo: EdgeSpendInfo = { - currencyCode: asset.currencyCode, + tokenId: asset.tokenId, spendTargets: [{ publicAddress }], networkFeeOption: 'standard' } @@ -163,7 +163,7 @@ const MigrateWalletCalculateFeeComponent = (props: Props) => { try { const maxAmount = await wallet.getMaxSpendable(spendInfo) if (maxAmount === '0') { - throw new InsufficientFundsError({ currencyCode: asset.currencyCode }) + throw new InsufficientFundsError({ tokenId: asset.tokenId }) } const maxSpendInfo = { ...spendInfo, spendTargets: [{ publicAddress, nativeAmount: maxAmount }] } const edgeTransaction = await wallet.makeSpend(maxSpendInfo) @@ -172,8 +172,8 @@ const MigrateWalletCalculateFeeComponent = (props: Props) => { feeTotal = add(feeTotal, txFee) // While imperfect, sanity check that the total fee spent so far to send tokens + fee to send mainnet currency is under the total mainnet balance - if (i === bundle.length - 1 && lt(wallet.balances[wallet.currencyInfo.currencyCode], feeTotal)) { - throw new InsufficientFundsError({ currencyCode: asset.currencyCode, networkFee: feeTotal }) + if (i === bundle.length - 1 && lt(wallet.balanceMap.get(null) ?? '0', feeTotal)) { + throw new InsufficientFundsError({ tokenId: null, networkFee: feeTotal }) } } catch (e: any) { for (const key of bundlesFeeTotals.keys()) { diff --git a/src/components/scenes/MigrateWalletCompletionScene.tsx b/src/components/scenes/MigrateWalletCompletionScene.tsx index 616b76743b6..320757619c0 100644 --- a/src/components/scenes/MigrateWalletCompletionScene.tsx +++ b/src/components/scenes/MigrateWalletCompletionScene.tsx @@ -125,7 +125,7 @@ const MigrateWalletCompletionComponent = (props: Props) => { // Change old wallet name if (createdNewWallet) await oldWallet.renameWallet(`${oldWalletName}${lstrings.migrate_wallet_old_fragment}`) - const addressInfo = await newWallet.getReceiveAddress() + const addressInfo = await newWallet.getReceiveAddress({ tokenId: null }) const newPublicAddress = addressInfo.segwitAddress ?? addressInfo.publicAddress const tokenItems = bundle.filter((pair: any): pair is MigrateWalletTokenItem => pair.tokenId != null) @@ -140,7 +140,7 @@ const MigrateWalletCompletionComponent = (props: Props) => { const successfullyTransferredTokenIds: string[] = [] for (const item of tokenItems) { let tokenSpendInfo: EdgeSpendInfo = { - currencyCode: item.currencyCode, + tokenId: item.tokenId, spendTargets: [{ publicAddress: newPublicAddress }], networkFeeOption: 'standard' } @@ -164,7 +164,7 @@ const MigrateWalletCompletionComponent = (props: Props) => { if (!hasError) { // Send mainnet let spendInfo: EdgeSpendInfo = { - currencyCode: oldWallet.currencyInfo.currencyCode, + tokenId: null, spendTargets: [{ publicAddress: newPublicAddress }], metadata: { category: 'Transfer', diff --git a/src/components/scenes/MigrateWalletSelectCryptoScene.tsx b/src/components/scenes/MigrateWalletSelectCryptoScene.tsx index 8fba4701644..3a7be907c98 100644 --- a/src/components/scenes/MigrateWalletSelectCryptoScene.tsx +++ b/src/components/scenes/MigrateWalletSelectCryptoScene.tsx @@ -8,7 +8,7 @@ import { useWatch } from '../../hooks/useWatch' import { lstrings } from '../../locales/strings' import { useSelector } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' -import { isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' +import { getCurrencyCode, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { zeroString } from '../../util/utils' import { SceneWrapper } from '../common/SceneWrapper' @@ -41,9 +41,8 @@ const MigrateWalletSelectCryptoComponent = (props: Props) => { Object.keys(currencyWallets).forEach(walletId => { const wallet = currencyWallets[walletId] const { - currencyInfo: { currencyCode, pluginId, walletType }, - currencyConfig: { allTokens }, - balances, + currencyInfo: { pluginId, walletType }, + balanceMap, enabledTokenIds } = wallet @@ -52,32 +51,19 @@ const MigrateWalletSelectCryptoComponent = (props: Props) => { if (pluginId === 'ripple') return // ignore currencies with token approval since they can't do bulk approvals const walletAssetList: MigrateWalletItem[] = [] - for (const [cc, bal] of Object.entries(balances)) { - if (cc === currencyCode) { - if (zeroString(bal)) return // ignore wallet - walletAssetList.unshift({ - createWalletIds: [walletId], - currencyCode: cc, - displayName: getWalletName(wallet), - key: walletId, - pluginId: pluginId, - walletType - }) - } else { - if (zeroString(bal)) continue // ignore token - const tokenId = Object.keys(allTokens).find(tokenId => cc === allTokens[tokenId].currencyCode) - - if (tokenId == null || !enabledTokenIds.includes(tokenId)) continue // ignore token - walletAssetList.push({ - createWalletIds: [walletId], - currencyCode: cc, - displayName: getWalletName(wallet), - key: `${walletId}:${cc}`, - pluginId: pluginId, - tokenId, - walletType - }) - } + for (const [tokenId, bal] of Array.from(balanceMap.entries())) { + if (zeroString(bal)) continue // ignore token + if (tokenId != null && !enabledTokenIds.includes(tokenId)) continue // ignore token + const currencyCode = getCurrencyCode(wallet, tokenId) + walletAssetList.push({ + createWalletIds: [walletId], + currencyCode, + displayName: getWalletName(wallet), + key: `${walletId}:${tokenId ?? 'PARENT_TOKEN'}`, + pluginId: pluginId, + tokenId, + walletType + }) } list = [...list, ...walletAssetList] }) diff --git a/src/components/scenes/NotificationScene.tsx b/src/components/scenes/NotificationScene.tsx index 020aefe1102..6bf8f7e6641 100644 --- a/src/components/scenes/NotificationScene.tsx +++ b/src/components/scenes/NotificationScene.tsx @@ -73,7 +73,7 @@ export const NotificationScene = (props: Props) => { return ( <SettingsTappableRow disabled={settings.ignorePriceChanges} key={pluginId} label={currencyInfo.displayName} onPress={handlePress}> - <CryptoIcon pluginId={pluginId} /> + <CryptoIcon pluginId={pluginId} tokenId={null} /> </SettingsTappableRow> ) })} diff --git a/src/components/scenes/RequestScene.tsx b/src/components/scenes/RequestScene.tsx index 7eb06566ab6..4f0071edcc7 100644 --- a/src/components/scenes/RequestScene.tsx +++ b/src/components/scenes/RequestScene.tsx @@ -1,6 +1,6 @@ import Clipboard from '@react-native-clipboard/clipboard' import { lt } from 'biggystring' -import { EdgeAccount, EdgeCurrencyWallet, EdgeEncodeUri } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeEncodeUri, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator, Linking, Platform, Text, TouchableOpacity, View } from 'react-native' import Share, { ShareOptions } from 'react-native-share' @@ -19,7 +19,7 @@ import { config } from '../../theme/appConfig' import { connect } from '../../types/reactRedux' import { EdgeSceneProps, NavigationBase } from '../../types/routerTypes' import { GuiCurrencyInfo } from '../../types/types' -import { getTokenId, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' import { getAvailableBalance, getWalletName } from '../../util/CurrencyWalletHelpers' import { triggerHaptic } from '../../util/haptic' import { convertNativeToDenomination, truncateDecimals, zeroString } from '../../util/utils' @@ -45,7 +45,6 @@ import { ShareButtons } from '../themed/ShareButtons' interface OwnProps extends EdgeSceneProps<'request'> {} interface StateProps { - account: EdgeAccount currencyCode?: string wallet?: EdgeCurrencyWallet exchangeSecondaryToPrimaryRatio?: string @@ -57,7 +56,7 @@ interface StateProps { interface DispatchProps { refreshAllFioAddresses: () => Promise<void> - onSelectWallet: (navigation: NavigationBase, walletId: string, tokenId?: string) => Promise<void> + onSelectWallet: (navigation: NavigationBase, walletId: string, tokenId: EdgeTokenId) => Promise<void> toggleAccountBalanceVisibility: () => void } type ModalState = 'NOT_YET_SHOWN' | 'VISIBLE' | 'SHOWN' @@ -128,7 +127,7 @@ export class RequestSceneComponent extends React.Component<Props, State> { if (wallet == null) return if (isKeysOnlyPlugin(wallet.currencyInfo.pluginId)) return - const receiveAddress = await wallet.getReceiveAddress() + const receiveAddress = await wallet.getReceiveAddress({ tokenId: null }) const addresses: AddressInfo[] = [] // Handle segwitAddress @@ -243,13 +242,10 @@ export class RequestSceneComponent extends React.Component<Props, State> { } handleOpenWalletListModal = () => { - const { account } = this.props Airship.show<WalletListResult>(bridge => <WalletListModal bridge={bridge} headerTitle={lstrings.select_wallet} navigation={this.props.navigation} />) .then(async result => { if (result?.type === 'wallet') { - const { walletId, currencyCode } = result - const wallet = account.currencyWallets[walletId] - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + const { walletId, tokenId } = result await this.props.onSelectWallet(this.props.navigation, walletId, tokenId) if (this.flipInputRef.current != null) { @@ -413,7 +409,7 @@ export class RequestSceneComponent extends React.Component<Props, State> { if (this.state.minimumPopupModalState[pluginId] === 'NOT_YET_SHOWN') { const { minimumPopupModals } = getSpecialCurrencyInfo(pluginId) const minBalance = minimumPopupModals != null ? minimumPopupModals.minimumNativeBalance : '0' - if (lt(wallet.balances[currencyCode] ?? '0', minBalance)) { + if (lt(wallet.balanceMap.get(null) ?? '0', minBalance)) { return true } } @@ -553,7 +549,6 @@ export const RequestScene = connect<StateProps, DispatchProps, OwnProps>( if (currencyCode == null || wallet == null) { return { - account, publicAddress: '', fioAddressesExist: false, isConnected: state.network.isConnected, @@ -565,7 +560,7 @@ export const RequestScene = connect<StateProps, DispatchProps, OwnProps>( const primaryDisplayDenomination = getDisplayDenomination(state, wallet.currencyInfo.pluginId, currencyCode) const primaryExchangeDenomination = getExchangeDenomination(state, wallet.currencyInfo.pluginId, currencyCode) const primaryExchangeCurrencyCode: string = primaryExchangeDenomination.name - const tokenId = getTokenId(state.core.account, pluginId, currencyCode) + const tokenId = getTokenIdForced(state.core.account, pluginId, currencyCode) const primaryCurrencyInfo: GuiCurrencyInfo = { walletId: walletId, @@ -580,7 +575,6 @@ export const RequestScene = connect<StateProps, DispatchProps, OwnProps>( const fioAddressesExist = !!state.ui.fioAddress.fioAddresses.length return { - account, currencyCode, wallet, exchangeSecondaryToPrimaryRatio, @@ -594,7 +588,7 @@ export const RequestScene = connect<StateProps, DispatchProps, OwnProps>( async refreshAllFioAddresses() { await dispatch(refreshAllFioAddresses()) }, - async onSelectWallet(navigation: NavigationBase, walletId: string, tokenId?: string) { + async onSelectWallet(navigation: NavigationBase, walletId: string, tokenId: EdgeTokenId) { await dispatch(selectWalletToken({ navigation, walletId, tokenId })) }, toggleAccountBalanceVisibility() { diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 3dc3e37a20f..02f99b435e7 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -8,6 +8,7 @@ import { EdgeMemo, EdgeSpendInfo, EdgeSpendTarget, + EdgeTokenId, EdgeTransaction } from 'edge-core-js' import * as React from 'react' @@ -63,7 +64,7 @@ interface Props extends EdgeSceneProps<'send2'> {} export interface SendScene2Params { walletId: string - tokenId?: string + tokenId: EdgeTokenId dismissAlert?: boolean isoExpireDate?: string minNativeAmount?: string @@ -142,7 +143,7 @@ const SendComponent = (props: Props) => { const initExpireDate = isoExpireDate != null ? new Date(isoExpireDate) : undefined const [processingAmountChanged, setProcessingAmountChanged] = React.useState<boolean>(false) const [walletId, setWalletId] = useState<string>(initWalletId) - const [spendInfo, setSpendInfo] = useState<EdgeSpendInfo>(initSpendInfo ?? { spendTargets: [{}] }) + const [spendInfo, setSpendInfo] = useState<EdgeSpendInfo>(initSpendInfo ?? { tokenId: null, spendTargets: [{}] }) const [fieldChanged, setFieldChanged] = useState<ExchangeFlipInputFields>('fiat') const [feeNativeAmount, setFeeNativeAmount] = useState<string>('') const [minNativeAmount, setMinNativeAmount] = useState<string | undefined>(initMinNativeAmount) @@ -168,7 +169,7 @@ const SendComponent = (props: Props) => { const pinSpendingLimitsAmount = useSelector<number>(state => state.ui.settings.spendingLimits.transaction.amount ?? 0) const defaultIsoFiat = useSelector<string>(state => state.ui.settings.defaultIsoFiat) const currencyWallets = useWatch(account, 'currencyWallets') - const [tokenId, setTokenId] = useState<string | undefined>(spendInfo.tokenId ?? tokenIdProp) + const [tokenId, setTokenId] = useState<EdgeTokenId>(spendInfo.tokenId ?? tokenIdProp) const coreWallet = currencyWallets[walletId] const { pluginId, memoOptions = [] } = coreWallet.currencyInfo const currencyCode = getCurrencyCode(coreWallet, tokenId) @@ -179,10 +180,6 @@ const SendComponent = (props: Props) => { spendInfo.tokenId = tokenId - // TODO: Fix currency plugins that implement getMaxSpendable to not look at the currencyCode - // but the tokenId. Then we can remove the line below - spendInfo.currencyCode = currencyCode - if (initialMount.current) { if (hiddenFeaturesMap.scamWarning === false) { triggerScamWarningModal(account.disklet).catch(err => showError(err)) @@ -317,6 +314,7 @@ const SendComponent = (props: Props) => { ref={flipInputModalRef} bridge={bridge} startNativeAmount={spendTarget.nativeAmount} + feeTokenId={null} forceField={fieldChanged} onAmountsChanged={handleAmountsChanged(spendTarget)} onMaxSet={handleSetMax(index)} @@ -330,7 +328,7 @@ const SendComponent = (props: Props) => { if (error == null) return console.log(error) const insufficientFunds = asMaybeInsufficientFundsError(error) - if (insufficientFunds != null && insufficientFunds.currencyCode != null && spendInfo.currencyCode !== insufficientFunds.currencyCode) { + if (insufficientFunds != null && insufficientFunds.tokenId != null && spendInfo.tokenId !== insufficientFunds.tokenId) { await Airship.show(bridge => <InsufficientFeesModal bridge={bridge} coreError={insufficientFunds} navigation={navigation} wallet={coreWallet} />) } }) @@ -388,7 +386,7 @@ const SendComponent = (props: Props) => { const { pluginId: newPluginId } = currencyWallets[result.walletId].currencyInfo if (pluginId !== newPluginId || currencyCode !== result.currencyCode) { setTokenId(result.tokenId) - setSpendInfo({ spendTargets: [{}] }) + setSpendInfo({ tokenId: result.tokenId, spendTargets: [{}] }) } }) .catch(error => { @@ -883,7 +881,7 @@ const SendComponent = (props: Props) => { // Mount/Unmount life-cycle events: useMount(() => { if (doCheckAndShowGetCryptoModal) { - dispatch(checkAndShowGetCryptoModal(navigation, route.params.walletId, route.params.spendInfo?.currencyCode)).catch(err => showError(err)) + dispatch(checkAndShowGetCryptoModal(navigation, route.params.walletId, route.params.spendInfo?.tokenId)).catch(err => showError(err)) } }) useUnmount(() => { @@ -907,7 +905,7 @@ const SendComponent = (props: Props) => { spendInfo.spendTargets[0].nativeAmount = maxSpendable } if (spendInfo.spendTargets[0].nativeAmount == null) { - flipInputModalRef.current?.setFees({ feeNativeAmount: '' }) + flipInputModalRef.current?.setFees({ feeNativeAmount: '', feeTokenId: null }) } if (pinSpendingLimitsEnabled) { const rate = exchangeRates[`${currencyCode}_${defaultIsoFiat}`] ?? INFINITY_STRING @@ -947,7 +945,7 @@ const SendComponent = (props: Props) => { setEdgeTransaction(edgeTx) const { parentNetworkFee, networkFee } = edgeTx const feeNativeAmount = parentNetworkFee ?? networkFee - const feeTokenId = parentNetworkFee == null ? tokenId : undefined + const feeTokenId = parentNetworkFee == null ? tokenId : null setFeeNativeAmount(feeNativeAmount) flipInputModalRef.current?.setFees({ feeTokenId, feeNativeAmount }) flipInputModalRef.current?.setError(null) @@ -955,8 +953,9 @@ const SendComponent = (props: Props) => { } catch (e: any) { const insufficientFunds = asMaybeInsufficientFundsError(e) if (insufficientFunds != null) { - if (insufficientFunds.currencyCode != null) { - e.message = sprintf(lstrings.stake_error_insufficient_s, insufficientFunds.currencyCode) + if (insufficientFunds.tokenId != null) { + const errorCurrencyCode = getCurrencyCode(coreWallet, insufficientFunds.tokenId) + e.message = sprintf(lstrings.stake_error_insufficient_s, errorCurrencyCode) } else { e.message = lstrings.exchange_insufficient_funds_title } @@ -965,7 +964,7 @@ const SendComponent = (props: Props) => { setError(e) setEdgeTransaction(null) flipInputModalRef.current?.setError(e.message) - flipInputModalRef.current?.setFees({ feeNativeAmount: '' }) + flipInputModalRef.current?.setFees({ feeNativeAmount: '', feeTokenId: null }) } setProcessingAmountChanged(false) }, [spendInfo, maxSpendSetter, walletId, pinSpendingLimitsEnabled, pinValue]) diff --git a/src/components/scenes/Staking/StakeModifyScene.tsx b/src/components/scenes/Staking/StakeModifyScene.tsx index cf5b1a8987d..fc4049987ee 100644 --- a/src/components/scenes/Staking/StakeModifyScene.tsx +++ b/src/components/scenes/Staking/StakeModifyScene.tsx @@ -1,5 +1,5 @@ import { eq, gt, toFixed } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Image, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' @@ -11,7 +11,7 @@ import { getDisplayDenomination, getExchangeDenominationFromAccount } from '../. import { useSelector } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' import { getCurrencyIconUris } from '../../../util/CdnUris' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced, getWalletTokenId } from '../../../util/CurrencyInfoHelpers' import { getWalletName } from '../../../util/CurrencyWalletHelpers' import { getPolicyIconUris, getPositionAllocations } from '../../../util/stakeUtils' import { toBigNumberString } from '../../../util/toBigNumberString' @@ -156,7 +156,8 @@ const StakeModifySceneComponent = (props: Props) => { if (allocationToMod == null) throw new Error(`Existing stake not found for ${modCurrencyCode}`) setChangeQuoteRequest({ ...changeQuoteRequest, currencyCode: modCurrencyCode, nativeAmount: allocationToMod.nativeAmount }) } else if (modification === 'stake' && existingStaked.length === 1) { - setChangeQuoteRequest({ ...changeQuoteRequest, currencyCode: modCurrencyCode, nativeAmount: wallet.balances[modCurrencyCode] }) + const tokenId = getWalletTokenId(wallet, modCurrencyCode) + setChangeQuoteRequest({ ...changeQuoteRequest, currencyCode: modCurrencyCode, nativeAmount: wallet.balanceMap.get(tokenId) ?? '0' }) } } } @@ -188,7 +189,7 @@ const StakeModifySceneComponent = (props: Props) => { } } - const handleShowFlipInputModal = (currencyCode: string, tokenId?: string) => () => { + const handleShowFlipInputModal = (currencyCode: string, tokenId: EdgeTokenId) => () => { const header = modification === 'stake' ? lstrings.stake_modal_modify_stake_title : lstrings.stake_modal_modify_unstake_title // TODO: Max button needs to be enabled after max calculation for @@ -201,6 +202,7 @@ const StakeModifySceneComponent = (props: Props) => { bridge={bridge} wallet={wallet} tokenId={tokenId} + feeTokenId={null} startNativeAmount={eq(changeQuoteRequest.nativeAmount, '0') ? undefined : changeQuoteRequest.nativeAmount} onAmountsChanged={() => {}} onMaxSet={handleMaxButtonPress(currencyCode)} @@ -270,7 +272,7 @@ const StakeModifySceneComponent = (props: Props) => { const renderEditableQuoteAmountRow = (allocationType: 'stake' | 'unstake' | 'claim', asset: { pluginId: string; currencyCode: string }) => { const { pluginId, currencyCode } = asset - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) const quoteAllocation: QuoteAllocation | undefined = changeQuote != null ? changeQuote.allocations.find( @@ -327,7 +329,7 @@ const StakeModifySceneComponent = (props: Props) => { const quoteDenom = getExchangeDenominationFromAccount(account, pluginId, currencyCode) const title = modification === 'stake' ? lstrings.stake_estimated_staking_fee : lstrings.stake_estimated_unstaking_fee - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) return ( <CryptoFiatAmountTile @@ -354,7 +356,7 @@ const StakeModifySceneComponent = (props: Props) => { if (quoteAllocation == null) return null const quoteDenom = getExchangeDenominationFromAccount(account, pluginId, currencyCode) - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) return ( <CryptoFiatAmountTile @@ -420,7 +422,7 @@ const StakeModifySceneComponent = (props: Props) => { return ( <View style={styles.amountTilesContainer}> - <IconTile title={lstrings.wc_smartcontract_wallet} iconUri={getCurrencyIconUris(wallet.currencyInfo.pluginId).symbolImage}> + <IconTile title={lstrings.wc_smartcontract_wallet} iconUri={getCurrencyIconUris(wallet.currencyInfo.pluginId, null).symbolImage}> <EdgeText>{getWalletName(wallet)}</EdgeText> </IconTile> { @@ -442,6 +444,7 @@ const StakeModifySceneComponent = (props: Props) => { { // Render network fee tile <CryptoFiatAmountTile + tokenId={null} title={lstrings.wc_smartcontract_network_fee} nativeCryptoAmount={networkFeeQuote?.nativeAmount ?? '0'} walletId={wallet.id} diff --git a/src/components/scenes/Staking/StakeOptionsScene.tsx b/src/components/scenes/Staking/StakeOptionsScene.tsx index c8762289222..1b129a95aee 100644 --- a/src/components/scenes/Staking/StakeOptionsScene.tsx +++ b/src/components/scenes/Staking/StakeOptionsScene.tsx @@ -9,7 +9,7 @@ import { lstrings } from '../../../locales/strings' import { StakePlugin, StakePolicy, StakePositionMap } from '../../../plugins/stake-plugins/types' import { useSelector } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { getPluginFromPolicy, getPolicyAssetName, getPolicyIconUris, getPolicyTitleName } from '../../../util/stakeUtils' import { StakingOptionCard } from '../../cards/StakingOptionCard' import { SceneWrapper } from '../../common/SceneWrapper' @@ -39,7 +39,7 @@ const StakeOptionsSceneComponent = (props: Props) => { const account = useSelector(state => state.core.account) const pluginId = wallet?.currencyInfo.pluginId - const tokenId = pluginId ? getTokenId(account, pluginId, currencyCode) : undefined + const tokenId = pluginId ? getTokenIdForced(account, pluginId, currencyCode) : null // // Handlers diff --git a/src/components/scenes/Staking/StakeOverviewScene.tsx b/src/components/scenes/Staking/StakeOverviewScene.tsx index 799a3cadf0e..c141d45477f 100644 --- a/src/components/scenes/Staking/StakeOverviewScene.tsx +++ b/src/components/scenes/Staking/StakeOverviewScene.tsx @@ -10,7 +10,7 @@ import { ChangeQuoteRequest, PositionAllocation, StakePlugin, StakePolicy, Stake import { getDisplayDenominationFromState } from '../../../selectors/DenominationSelectors' import { useDispatch, useSelector } from '../../../types/reactRedux' import { EdgeSceneProps } from '../../../types/routerTypes' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../../util/CurrencyInfoHelpers' import { getAllocationLocktimeMessage, getPolicyIconUris, getPolicyTitleName, getPositionAllocations } from '../../../util/stakeUtils' import { StakingReturnsCard } from '../../cards/StakingReturnsCard' import { SceneWrapper } from '../../common/SceneWrapper' @@ -121,7 +121,7 @@ const StakeOverviewSceneComponent = (props: Props) => { const title = `${sprintf(titleBase, currencyCode)} ${getAllocationLocktimeMessage(item)}` const denomination = displayDenomMap[currencyCode] - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + const tokenId = getTokenIdForced(account, wallet.currencyInfo.pluginId, currencyCode) return <CryptoFiatAmountTile title={title} nativeCryptoAmount={nativeAmount ?? '0'} tokenId={tokenId} denomination={denomination} walletId={wallet.id} /> } diff --git a/src/components/scenes/TransactionDetailsScene.tsx b/src/components/scenes/TransactionDetailsScene.tsx index fad599775a7..7585a9269d3 100644 --- a/src/components/scenes/TransactionDetailsScene.tsx +++ b/src/components/scenes/TransactionDetailsScene.tsx @@ -47,13 +47,12 @@ interface Props extends EdgeSceneProps<'transactionDetails'> { export interface TransactionDetailsParams { edgeTransaction: EdgeTransaction walletId: string - tokenId?: string } const TransactionDetailsComponent = (props: Props) => { const { navigation, route, wallet } = props - const { edgeTransaction: transaction, tokenId, walletId } = route.params - const { metadata = {}, action, nativeAmount, date, currencyCode, txid } = transaction + const { edgeTransaction: transaction, walletId } = route.params + const { currencyCode, metadata = {}, chainAssetAction, nativeAmount, date, tokenId, txid } = transaction const { currencyInfo } = wallet const theme = useTheme() @@ -75,7 +74,7 @@ const TransactionDetailsComponent = (props: Props) => { const splitCat = metadata?.category != null || txActionSplitCat == null ? splitCategory( - metadata?.category, + metadata?.category ?? undefined, // Pick the right default: direction === 'receive' ? 'income' : 'expense' ) @@ -93,7 +92,7 @@ const TransactionDetailsComponent = (props: Props) => { }) const [acceleratedTx, setAcceleratedTx] = React.useState<null | EdgeTransaction>(null) - const { name = '' } = localMetadata + const name = localMetadata.name ?? '' // #region Crypto Fiat Rows @@ -237,13 +236,19 @@ const TransactionDetailsComponent = (props: Props) => { } transaction.metadata = mergedMetadata - wallet.saveTxMetadata(transaction.txid, transaction.currencyCode, transaction.metadata).catch(error => showError(error)) + wallet + .saveTxMetadata({ + txid: transaction.txid, + tokenId: transaction.tokenId, + metadata: transaction.metadata + }) + .catch(error => showError(error)) setLocalMetadata(mergedMetadata) } const personLabel = direction === 'receive' ? lstrings.transaction_details_sender : lstrings.transaction_details_recipient - const personName = action != null ? TX_ACTION_LABEL_MAP[action.type] : name !== '' ? name : personLabel + const personName = chainAssetAction != null ? TX_ACTION_LABEL_MAP[chainAssetAction.assetActionType] : name !== '' ? name : personLabel const personHeader = sprintf(lstrings.transaction_details_person_name, personLabel) // spendTargets recipient addresses format diff --git a/src/components/scenes/TransactionListScene.tsx b/src/components/scenes/TransactionListScene.tsx index 339cf71d092..b6bc2fc24c1 100644 --- a/src/components/scenes/TransactionListScene.tsx +++ b/src/components/scenes/TransactionListScene.tsx @@ -1,7 +1,7 @@ import { FlashList } from '@shopify/flash-list' import { abs, lt } from 'biggystring' import { asArray } from 'cleaners' -import { EdgeCurrencyWallet, EdgeTokenMap, EdgeTransaction } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId, EdgeTokenMap, EdgeTransaction } from 'edge-core-js' import { asAssetStatus, AssetStatus } from 'edge-info-server/types' import * as React from 'react' import { RefreshControl, StyleSheet } from 'react-native' @@ -36,7 +36,7 @@ const SHOW_FLIP_INPUT_TESTER = false export interface TransactionListParams { walletId: string - tokenId: string | undefined + tokenId: EdgeTokenId } interface Props extends EdgeSceneProps<'transactionList'> { @@ -230,7 +230,7 @@ function TransactionListComponent(props: Props) { } return ( <EdgeAnim enter={{ type: 'fadeInRight', distance: 30 * (index + 1) }}> - <TransactionListRow currencyCode={currencyCode} navigation={navigation} transaction={item} wallet={wallet} /> + <TransactionListRow navigation={navigation} transaction={item} wallet={wallet} /> </EdgeAnim> ) }) @@ -284,9 +284,9 @@ function TransactionListComponent(props: Props) { * If the token gets deleted, the scene will crash. * Fall back to the main currency code if this happens. */ -function checkToken(tokenId: string | undefined, allTokens: EdgeTokenMap): string | undefined { - if (tokenId == null) return undefined - if (allTokens[tokenId] == null) return undefined +function checkToken(tokenId: EdgeTokenId, allTokens: EdgeTokenMap): EdgeTokenId { + if (tokenId == null) return null + if (allTokens[tokenId] == null) return null return tokenId } diff --git a/src/components/scenes/TransactionsExportScene.tsx b/src/components/scenes/TransactionsExportScene.tsx index e0daeabd8f0..b68c2f6f51b 100644 --- a/src/components/scenes/TransactionsExportScene.tsx +++ b/src/components/scenes/TransactionsExportScene.tsx @@ -1,5 +1,5 @@ import { asBoolean, asObject, asString } from 'cleaners' -import { EdgeCurrencyWallet, EdgeTransaction } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId, EdgeTransaction } from 'edge-core-js' import * as React from 'react' import { Platform, ScrollView } from 'react-native' import RNFS from 'react-native-fs' @@ -12,7 +12,7 @@ import { lstrings } from '../../locales/strings' import { getDisplayDenomination, getExchangeDenomination } from '../../selectors/DenominationSelectors' import { connect } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { SceneWrapper } from '../common/SceneWrapper' import { DateModal } from '../modals/DateModal' @@ -38,7 +38,7 @@ interface StateProps { multiplier: string exchangeMultiplier: string parentMultiplier: string - tokenId: string | undefined + tokenId: EdgeTokenId } interface DispatchProps { @@ -104,11 +104,13 @@ class TransactionsExportSceneComponent extends React.PureComponent<Props, State> async componentDidMount(): Promise<void> { try { const { sourceWallet } = this.props.route.params - const { tokenId = sourceWallet.currencyInfo.currencyCode } = this.props + const { tokenId } = this.props const { disklet } = sourceWallet const result = await disklet.getText(EXPORT_TX_INFO_FILE) const exportTxInfoMap = asExportTxInfoMap(JSON.parse(result)) - const { isExportBitwave, isExportCsv, isExportQbo } = exportTxInfoMap[tokenId] + const tokenCurrencyCode = tokenId ?? sourceWallet.currencyInfo.currencyCode + + const { isExportBitwave, isExportCsv, isExportQbo } = exportTxInfoMap[tokenCurrencyCode] this.setState({ isExportBitwave, isExportCsv, @@ -209,14 +211,15 @@ class TransactionsExportSceneComponent extends React.PureComponent<Props, State> const { exchangeMultiplier, multiplier, parentMultiplier, route } = this.props const { sourceWallet, currencyCode } = route.params const { isExportBitwave, isExportQbo, isExportCsv, startDate, endDate } = this.state - const { tokenId = sourceWallet.currencyInfo.currencyCode } = this.props + const { tokenId } = this.props + const tokenCurrencyCode = tokenId ?? sourceWallet.currencyInfo.currencyCode let exportTxInfo: ExportTxInfo | undefined let exportTxInfoMap: ExportTxInfoMap | undefined try { const result = await sourceWallet.disklet.getText(EXPORT_TX_INFO_FILE) exportTxInfoMap = asExportTxInfoMap(JSON.parse(result)) - exportTxInfo = exportTxInfoMap[tokenId] + exportTxInfo = exportTxInfoMap[tokenCurrencyCode] } catch (e) { console.log(`Could not read ${EXPORT_TX_INFO_FILE} ${String(e)}. Failure is ok`) } @@ -250,7 +253,7 @@ class TransactionsExportSceneComponent extends React.PureComponent<Props, State> if (exportTxInfoMap == null) { exportTxInfoMap = {} } - exportTxInfoMap[tokenId] = { + exportTxInfoMap[tokenCurrencyCode] = { bitwaveAccountId: accountId, isExportBitwave, isExportQbo, @@ -285,7 +288,7 @@ class TransactionsExportSceneComponent extends React.PureComponent<Props, State> .replace(/[-\s]+/g, '-') // Collapse spaces & dashes const txs = await sourceWallet.getTransactions({ - currencyCode, + tokenId, startDate, endDate }) @@ -382,7 +385,7 @@ export const TransactionsExportScene = connect<StateProps, DispatchProps, OwnPro multiplier: getDisplayDenomination(state, params.sourceWallet.currencyInfo.pluginId, params.currencyCode).multiplier, exchangeMultiplier: getExchangeDenomination(state, params.sourceWallet.currencyInfo.pluginId, params.currencyCode).multiplier, parentMultiplier: getExchangeDenomination(state, params.sourceWallet.currencyInfo.pluginId, params.sourceWallet.currencyInfo.currencyCode).multiplier, - tokenId: getTokenId(state.core.account, params.sourceWallet.currencyInfo.pluginId, params.currencyCode) + tokenId: getTokenIdForced(state.core.account, params.sourceWallet.currencyInfo.pluginId, params.currencyCode) }), dispatch => ({ updateTxsFiatDispatch: async (wallet: EdgeCurrencyWallet, currencyCode: string, txs: EdgeTransaction[]) => diff --git a/src/components/scenes/WcConnectScene.tsx b/src/components/scenes/WcConnectScene.tsx index d8eadcf7e38..fbd8c9af93b 100644 --- a/src/components/scenes/WcConnectScene.tsx +++ b/src/components/scenes/WcConnectScene.tsx @@ -17,7 +17,7 @@ import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' import { EdgeAsset } from '../../types/types' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { truncateString } from '../../util/utils' import { Card } from '../cards/Card' import { SceneWrapper } from '../common/SceneWrapper' @@ -63,7 +63,7 @@ export const WcConnectScene = (props: Props) => { }, [proposal]) useAsyncEffect(async () => { - const r = await wallet.getReceiveAddress() + const r = await wallet.getReceiveAddress({ tokenId: null }) setWalletAddress(r.publicAddress) }, [wallet]) @@ -88,7 +88,7 @@ export const WcConnectScene = (props: Props) => { if (result?.type === 'wallet') { const { walletId, currencyCode } = result const wallet = account.currencyWallets[walletId] - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + const tokenId = getTokenIdForced(account, wallet.currencyInfo.pluginId, currencyCode) await dispatch(selectWalletToken({ navigation, walletId, tokenId })) setSelectedWallet({ walletId, currencyCode }) } @@ -108,7 +108,7 @@ export const WcConnectScene = (props: Props) => { } else { const walletNameStr = truncateString(walletName || '', MAX_ADDRESS_CHARACTERS) const walletImage = ( - <CryptoIcon pluginId={wallet.currencyInfo.pluginId} tokenId={getTokenId(account, wallet.currencyInfo.pluginId, selectedWallet.currencyCode)} /> + <CryptoIcon pluginId={wallet.currencyInfo.pluginId} tokenId={getTokenIdForced(account, wallet.currencyInfo.pluginId, selectedWallet.currencyCode)} /> ) const walletAddressStr = truncateString(walletAddress, MAX_ADDRESS_CHARACTERS, true) return <SelectableRow arrowTappable icon={walletImage} subTitle={walletAddressStr} title={walletNameStr} onPress={handleWalletListModal} /> diff --git a/src/components/scenes/WcConnectionsScene.tsx b/src/components/scenes/WcConnectionsScene.tsx index dac54a10965..cdf7001fdc5 100644 --- a/src/components/scenes/WcConnectionsScene.tsx +++ b/src/components/scenes/WcConnectionsScene.tsx @@ -207,12 +207,12 @@ const getProposalNamespaceCompatibleEdgeTokenIds = (proposal: Web3WalletTypes.Se if (requiredChainIds.has(chainIdString)) { hasWalletForRequiredNamespace = true if (!edgeTokenIdMap.has(pluginId)) { - edgeTokenIdMap.set(pluginId, { pluginId }) + edgeTokenIdMap.set(pluginId, { pluginId, tokenId: null }) } } if (optionalChainIds.has(chainIdString)) { if (!edgeTokenIdMap.has(pluginId)) { - edgeTokenIdMap.set(pluginId, { pluginId }) + edgeTokenIdMap.set(pluginId, { pluginId, tokenId: null }) } } } diff --git a/src/components/services/AccountCallbackManager.tsx b/src/components/services/AccountCallbackManager.tsx index 3ed1e492c95..3b135672e16 100644 --- a/src/components/services/AccountCallbackManager.tsx +++ b/src/components/services/AccountCallbackManager.tsx @@ -132,7 +132,7 @@ export function AccountCallbackManager(props: Props) { }), // These ones defer their work until later: - wallet.watch('balances', () => setRatesDirty()), + wallet.watch('balanceMap', () => setRatesDirty()), wallet.watch('enabledTokenIds', () => setRatesDirty()) ] diff --git a/src/components/services/LoanManagerService.ts b/src/components/services/LoanManagerService.ts index e55f37c935c..cf673a39c1b 100644 --- a/src/components/services/LoanManagerService.ts +++ b/src/components/services/LoanManagerService.ts @@ -1,5 +1,5 @@ import { add, div, mul } from 'biggystring' -import { EdgeAccount } from 'edge-core-js' +import { EdgeAccount, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { LoginUpdatePayload, NewPushEvent } from '../../controllers/action-queue/types/pushApiTypes' @@ -89,7 +89,7 @@ export const LoanManagerService = (props: Props) => { // Update Liquidation Threshold Push Event // const getCurrencyCode = React.useCallback( - (borrowEngine: BorrowEngine, tokenId?: string): string => { + (borrowEngine: BorrowEngine, tokenId: EdgeTokenId): string => { const tokens = allTokens[borrowEngine.currencyWallet.currencyInfo.pluginId] const token = tokenId != null ? tokens[tokenId] : undefined return token?.currencyCode ?? borrowEngine.currencyWallet.currencyInfo.currencyCode @@ -186,7 +186,7 @@ export const LoanManagerService = (props: Props) => { const onlyUsdBasedDebts = filteredDebts.every(debt => (debt.tokenId != null ? USD_BASED_TOKEN_IDS.includes(debt.tokenId.toLowerCase()) : false)) if (onlyOneCollateral && onlyUsdBasedDebts) { - const getExchangeAmount = (tokenId: string | undefined, nativeAmount: string): string => { + const getExchangeAmount = (tokenId: EdgeTokenId, nativeAmount: string): string => { const tokens = allTokens[borrowEngine.currencyWallet.currencyInfo.pluginId] const token = tokenId != null ? tokens[tokenId] : undefined const denom = token != null ? token.denominations[0] : borrowEngine.currencyWallet.currencyInfo.denominations[0] diff --git a/src/components/services/SortedWalletList.ts b/src/components/services/SortedWalletList.ts index b13174233dc..73c3c8a8dde 100644 --- a/src/components/services/SortedWalletList.ts +++ b/src/components/services/SortedWalletList.ts @@ -6,6 +6,7 @@ import { useWalletsSubscriber } from '../../hooks/useWalletsSubscriber' import { useWatch } from '../../hooks/useWatch' import { useDispatch, useSelector } from '../../types/reactRedux' import { GuiExchangeRates, WalletListItem } from '../../types/types' +import { getWalletTokenId } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { normalizeForSearch } from '../../util/utils' @@ -54,6 +55,7 @@ export function SortedWalletList(props: Props) { // Add the wallet itself: wallets.push({ key: wallet == null ? `${walletId}-loading` : walletId, + tokenId: null, wallet, walletId }) @@ -224,7 +226,8 @@ function getFiat(item: WalletListItem, exchangeRates: GuiExchangeRates): number currencyCode, denominations: [denomination] } = token != null ? token : wallet.currencyInfo - const nativeBalance = wallet.balances[currencyCode] ?? '0' + const tokenId = getWalletTokenId(wallet, currencyCode) + const nativeBalance = wallet.balanceMap.get(tokenId) ?? '0' // Find the rate: const rate = exchangeRates[`${currencyCode}_${wallet.fiatCurrencyCode}`] ?? '0' diff --git a/src/components/services/WalletConnectService.tsx b/src/components/services/WalletConnectService.tsx index e7bcf11f323..6a6acb5f520 100644 --- a/src/components/services/WalletConnectService.tsx +++ b/src/components/services/WalletConnectService.tsx @@ -2,13 +2,14 @@ import '@walletconnect/react-native-compat' import { Core } from '@walletconnect/core' import Web3Wallet, { Web3WalletTypes } from '@walletconnect/web3wallet' -import { asNumber, asObject, asOptional, asString, asUnknown } from 'cleaners' +import { asNumber, asObject, asString, asUnknown } from 'cleaners' import { EdgeAccount } from 'edge-core-js' import * as React from 'react' import { ENV } from '../../env' import { useAsyncEffect } from '../../hooks/useAsyncEffect' import { getAccounts, getClient, getWalletIdFromSessionNamespace, waitingClients, walletConnectClient } from '../../hooks/useWalletConnect' +import { asLegacyTokenId } from '../../types/types' import { WcSmartContractModal } from '../modals/WcSmartContractModal' import { Airship, showError } from '../services/AirshipInstance' @@ -99,7 +100,7 @@ export const WalletConnectService = (props: Props) => { } // Cleaners -const payloadAmounts = asObject({ nativeAmount: asString, networkFee: asString, tokenId: asOptional(asString) }) +const payloadAmounts = asObject({ nativeAmount: asString, networkFee: asString, tokenId: asLegacyTokenId }) const asSessionRequest = asObject({ id: asNumber, topic: asString, diff --git a/src/components/text/CryptoText.tsx b/src/components/text/CryptoText.tsx index e357c08a484..108efd58109 100644 --- a/src/components/text/CryptoText.tsx +++ b/src/components/text/CryptoText.tsx @@ -6,7 +6,7 @@ import { EdgeText } from '../themed/EdgeText' interface Props { nativeAmount: string - tokenId?: EdgeTokenId + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet withSymbol?: boolean hideBalance?: boolean diff --git a/src/components/text/FiatText.tsx b/src/components/text/FiatText.tsx index 4315cbb4f4f..921939654c8 100644 --- a/src/components/text/FiatText.tsx +++ b/src/components/text/FiatText.tsx @@ -17,7 +17,7 @@ interface Props { // Amount to show: nativeCryptoAmount: string - tokenId?: EdgeTokenId + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } diff --git a/src/components/text/TickerText.tsx b/src/components/text/TickerText.tsx index 69b262a0b4b..b3daf038912 100644 --- a/src/components/text/TickerText.tsx +++ b/src/components/text/TickerText.tsx @@ -13,7 +13,7 @@ import { EdgeText } from '../themed/EdgeText' interface Props { wallet: EdgeCurrencyWallet - tokenId?: EdgeTokenId + tokenId: EdgeTokenId } const getPercentDeltaString = (currencyCode: string, assetToFiatRate: string, assetToYestFiatRate: string, usdToWalletFiatRate: string, theme: Theme) => { diff --git a/src/components/themed/BuyCrypto.tsx b/src/components/themed/BuyCrypto.tsx index 2e5b46ab559..d8882ef70ea 100644 --- a/src/components/themed/BuyCrypto.tsx +++ b/src/components/themed/BuyCrypto.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { View } from 'react-native' import { sprintf } from 'sprintf-js' @@ -18,7 +18,7 @@ const allowedPluginIds = Object.keys(SPECIAL_CURRENCY_INFO).filter(pluginId => ! interface OwnProps { wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId navigation: NavigationBase } diff --git a/src/components/themed/CreateWalletSelectCryptoRow.tsx b/src/components/themed/CreateWalletSelectCryptoRow.tsx index 2391064fdd6..71fdf47bdef 100644 --- a/src/components/themed/CreateWalletSelectCryptoRow.tsx +++ b/src/components/themed/CreateWalletSelectCryptoRow.tsx @@ -1,3 +1,4 @@ +import { EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { TouchableOpacity, View } from 'react-native' @@ -15,7 +16,7 @@ interface Props { // Icon currency: pluginId: string - tokenId?: string + tokenId: EdgeTokenId // Callbacks: onPress?: () => Promise<void> | void diff --git a/src/components/themed/CryptoExchangeFlipInputWrapperComponent.tsx b/src/components/themed/CryptoExchangeFlipInputWrapperComponent.tsx index adaf2d3fa99..9c4713290d5 100644 --- a/src/components/themed/CryptoExchangeFlipInputWrapperComponent.tsx +++ b/src/components/themed/CryptoExchangeFlipInputWrapperComponent.tsx @@ -1,4 +1,5 @@ import { add } from 'biggystring' +import { EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator, View } from 'react-native' @@ -6,7 +7,7 @@ import { formatNumber } from '../../locales/intl' import { lstrings } from '../../locales/strings' import { connect } from '../../types/reactRedux' import { GuiCurrencyInfo } from '../../types/types' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { convertNativeToDenomination } from '../../util/utils' import { Card } from '../cards/Card' import { CryptoIcon } from '../icons/CryptoIcon' @@ -21,7 +22,6 @@ interface OwnProps { buttonText: string headerText: string primaryCurrencyInfo: GuiCurrencyInfo - tokenId?: string overridePrimaryNativeAmount: string isFocused: boolean isThinking?: boolean @@ -37,6 +37,7 @@ interface OwnProps { interface StateProps { name?: string cryptoAmount?: string + tokenId: EdgeTokenId } interface State { @@ -217,17 +218,17 @@ export const CryptoExchangeFlipInputWrapper = connect<StateProps, {}, OwnProps>( (state, ownProps) => { const { currencyWallets } = state.core.account const wallet = currencyWallets[ownProps.walletId] - if (wallet == null) return {} - - const { balances, name } = wallet + if (wallet == null) return { tokenId: null } + const { balanceMap, name } = wallet const { displayCurrencyCode, displayDenomination } = ownProps.primaryCurrencyInfo - const balance = balances?.[displayCurrencyCode] ?? '0' + + const tokenId = getTokenIdForced(state.core.account, wallet.currencyInfo.pluginId, displayCurrencyCode) + + const balance = balanceMap.get(tokenId) ?? '0' const cryptoAmountRaw: string = convertNativeToDenomination(displayDenomination.multiplier)(balance) const cryptoAmount = formatNumber(add(cryptoAmountRaw, '0')) - const tokenId = getTokenId(state.core.account, wallet.currencyInfo.pluginId, displayCurrencyCode) - return { name: name ?? '', cryptoAmount, tokenId } }, dispatch => ({}) diff --git a/src/components/themed/EdgeProviderComponent.tsx b/src/components/themed/EdgeProviderComponent.tsx index c2223f80d08..516cc6b24b7 100644 --- a/src/components/themed/EdgeProviderComponent.tsx +++ b/src/components/themed/EdgeProviderComponent.tsx @@ -13,7 +13,7 @@ import { GuiPlugin } from '../../types/GuiPluginTypes' import { useDispatch, useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' import { UriQueryMap } from '../../types/WebTypes' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { makePluginUri } from '../../util/GuiPluginTools' import { bestOfPlugins } from '../../util/ReferralHelpers' import { setPluginScene } from '../navigation/GuiPluginBackButton' @@ -113,7 +113,7 @@ export function EdgeProviderComponent(props: Props): JSX.Element { // Build our EdgeProvider instance one time: const [edgeProvider] = React.useState(() => { const selectedWallet = account.currencyWallets[selectedWalletId] - const selectedTokenId = selectedWallet == null ? undefined : getTokenId(account, selectedWallet.currencyInfo.pluginId, selectedCurrencyCode) + const selectedTokenId = selectedWallet == null ? null : getTokenIdForced(account, selectedWallet.currencyInfo.pluginId, selectedCurrencyCode) return new EdgeProviderServer({ account, dispatch, diff --git a/src/components/themed/ExchangeQuoteComponent.tsx b/src/components/themed/ExchangeQuoteComponent.tsx index 4b744d01579..ba9df23b802 100644 --- a/src/components/themed/ExchangeQuoteComponent.tsx +++ b/src/components/themed/ExchangeQuoteComponent.tsx @@ -35,13 +35,14 @@ export const ExchangeQuote = (props: Props) => { // Fees are assumed to be denominated in the native currency const feeNativeAmount = networkFee.nativeAmount - const feeCryptoText = useCryptoText({ wallet: fromWallet, nativeAmount: feeNativeAmount, withSymbol: false }) + const feeCryptoText = useCryptoText({ wallet: fromWallet, nativeAmount: feeNativeAmount, tokenId: null, withSymbol: false }) const { currencyCode: parentCurrencyCode, denomination: parentDenomination, isoFiatCurrencyCode } = useTokenDisplayData({ - wallet: fromWallet + wallet: fromWallet, + tokenId: null }) const { currencyCode: fromCurrencyCode, denomination: fromDenomination } = useTokenDisplayData({ diff --git a/src/components/themed/ExchangedFlipInput2.tsx b/src/components/themed/ExchangedFlipInput2.tsx index d7f3166848a..f06cc347d86 100644 --- a/src/components/themed/ExchangedFlipInput2.tsx +++ b/src/components/themed/ExchangedFlipInput2.tsx @@ -1,5 +1,5 @@ import { div, log10, mul, round } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import React, { useMemo, useState } from 'react' import { ActivityIndicator, Platform, ReturnKeyType, TouchableOpacity } from 'react-native' @@ -31,7 +31,7 @@ export interface ExchangedFlipInputAmounts { export interface Props { walletId: string - tokenId?: string + tokenId: EdgeTokenId startNativeAmount?: string keyboardVisible?: boolean headerText: string diff --git a/src/components/themed/ExplorerCard.tsx b/src/components/themed/ExplorerCard.tsx index 064324e2ddf..b732e6ea103 100644 --- a/src/components/themed/ExplorerCard.tsx +++ b/src/components/themed/ExplorerCard.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Linking, View } from 'react-native' import { sprintf } from 'sprintf-js' @@ -17,7 +17,7 @@ const transactionListUnsupportedPluginIds = Object.keys(SPECIAL_CURRENCY_INFO).f interface OwnProps { wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId } type Props = OwnProps @@ -34,7 +34,7 @@ export const ExplorerCard = (props: Props) => { // const handlePress = useHandler(async () => { - const receiveAddress = await wallet.getReceiveAddress() + const receiveAddress = await wallet.getReceiveAddress({ tokenId: null }) const url = sprintf(addressExplorer, receiveAddress.publicAddress) await Linking.openURL(url) }) diff --git a/src/components/themed/TransactionListRow.tsx b/src/components/themed/TransactionListRow.tsx index 5d103879243..2cb1d4058d2 100644 --- a/src/components/themed/TransactionListRow.tsx +++ b/src/components/themed/TransactionListRow.tsx @@ -39,8 +39,6 @@ import { EdgeText } from './EdgeText' interface Props { navigation: NavigationBase wallet: EdgeCurrencyWallet - tokenId?: string - currencyCode: string transaction: EdgeTransaction } @@ -48,9 +46,10 @@ export function TransactionListRow(props: Props) { const theme = useTheme() const styles = getStyles(theme) - const { navigation, currencyCode, wallet, tokenId, transaction } = props + const { navigation, wallet, transaction } = props + const { tokenId } = transaction const { canReplaceByFee = false } = wallet.currencyInfo - const { metadata = {}, action } = transaction + const { metadata = {}, chainAssetAction, currencyCode } = transaction const { category, name } = metadata const defaultAmountFiat = metadata.exchangeAmount?.[wallet.fiatCurrencyCode] ?? 0 @@ -91,7 +90,7 @@ export function TransactionListRow(props: Props) { // Fiat Amount const isoDate = new Date(transaction.date * 1000).toISOString() const historicalRate = useHistoricalRate(`${currencyCode}_${fiatCurrencyCode}`, isoDate) - const amountFiat = defaultAmountFiat > 0 ? defaultAmountFiat : historicalRate * Number(cryptoExchangeAmount) + const amountFiat = (defaultAmountFiat ?? 0) > 0 ? defaultAmountFiat ?? 0 : historicalRate * Number(cryptoExchangeAmount) const fiatAmount = displayFiatAmount(amountFiat) const fiatSymbol = getSymbolFromCurrency(nonIsoFiatCurrencyCode) @@ -99,8 +98,8 @@ export function TransactionListRow(props: Props) { // Transaction Title const transactionTitle = - action != null - ? TX_ACTION_LABEL_MAP[action.type] + chainAssetAction != null + ? TX_ACTION_LABEL_MAP[chainAssetAction.assetActionType] : name != null && name !== '' ? name : sprintf(isSentTransaction ? lstrings.transaction_sent_1s : lstrings.transaction_received_1s, displayName) @@ -108,7 +107,9 @@ export function TransactionListRow(props: Props) { // Icon & Thumbnail const thumbnailPath = useContactThumbnail(name) - const isSwapIcon = (action != null && (action.type === 'swap' || action.type === 'swapOrderFill')) || transaction.swapData != null + const isSwapIcon = + (chainAssetAction != null && (chainAssetAction.assetActionType === 'swap' || chainAssetAction.assetActionType === 'swapOrderFill')) || + transaction.swapData != null const arrowIconName = isSwapIcon ? 'swap-horizontal' : isSentTransaction ? 'arrow-up' : 'arrow-down' const arrowIconSize = thumbnailPath ? theme.rem(1) : theme.rem(1.25) const arrowIconColor = isSwapIcon ? theme.txDirFgSwapUi4 : isSentTransaction ? theme.txDirFgSendUi4 : theme.txDirFgReceiveUi4 @@ -162,8 +163,7 @@ export function TransactionListRow(props: Props) { } navigation.push('transactionDetails', { edgeTransaction: transaction, - walletId: wallet.id, - tokenId + walletId: wallet.id }) }) diff --git a/src/components/themed/TransactionListTop.tsx b/src/components/themed/TransactionListTop.tsx index f36315f1674..3e34d663b86 100644 --- a/src/components/themed/TransactionListTop.tsx +++ b/src/components/themed/TransactionListTop.tsx @@ -1,5 +1,5 @@ import { add, gt, mul, round } from 'biggystring' -import { EdgeAccount, EdgeBalances, EdgeCurrencyWallet, EdgeDenomination } from 'edge-core-js' +import { EdgeAccount, EdgeBalanceMap, EdgeCurrencyWallet, EdgeDenomination, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator, TouchableOpacity, View } from 'react-native' import { AirshipBridge } from 'react-native-airship' @@ -44,7 +44,7 @@ interface OwnProps { isLightAccount: boolean // Wallet identity: - tokenId: string | undefined + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet // Scene state: @@ -57,7 +57,7 @@ interface OwnProps { interface StateProps { account: EdgeAccount - balances: EdgeBalances + balanceMap: EdgeBalanceMap currencyCode: string displayDenomination: EdgeDenomination exchangeDenomination: EdgeDenomination @@ -191,14 +191,13 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat } renderBalanceBox = () => { - const { balances, currencyCode, displayDenomination, exchangeDenomination, exchangeRate, isAccountBalanceVisible, theme, tokenId, wallet, walletName } = - this.props + const { balanceMap, displayDenomination, exchangeDenomination, exchangeRate, isAccountBalanceVisible, theme, tokenId, wallet, walletName } = this.props const styles = getStyles(theme) const fiatCurrencyCode = wallet.fiatCurrencyCode.replace(/^iso:/, '') const fiatSymbol = getSymbolFromCurrency(wallet.fiatCurrencyCode) - const nativeBalance = balances[currencyCode] ?? '0' + const nativeBalance = balanceMap.get(tokenId) ?? '0' const cryptoAmount = convertNativeToDenomination(displayDenomination.multiplier)(nativeBalance) // convert to correct denomination const cryptoAmountFormat = formatNumber(add(cryptoAmount, '0')) @@ -350,13 +349,13 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat handleStakePress = () => { triggerHaptic('impactLight') - const { currencyCode, wallet, navigation } = this.props + const { currencyCode, wallet, navigation, tokenId } = this.props const { stakePlugins, stakePolicies, stakePositionMap } = this.state // Handle FIO staking if (currencyCode === 'FIO') { navigation.push('fioStakingOverview', { - currencyCode, + tokenId, walletId: wallet.id }) } @@ -586,7 +585,7 @@ export function TransactionListTop(props: OwnProps) { const activeUsername = useSelector(state => state.core.account.username) const walletName = useWalletName(wallet) - const balances = useWatch(wallet, 'balances') + const balanceMap = useWatch(wallet, 'balanceMap') const handleBalanceVisibility = useHandler(() => { dispatch(toggleAccountBalanceVisibility()) @@ -596,7 +595,7 @@ export function TransactionListTop(props: OwnProps) { <TransactionListTopComponent {...props} account={account} - balances={balances} + balanceMap={balanceMap} currencyCode={currencyCode} displayDenomination={displayDenomination} exchangeDenomination={exchangeDenomination} diff --git a/src/components/themed/WalletList.tsx b/src/components/themed/WalletList.tsx index b6a52c955f8..f933b181fac 100644 --- a/src/components/themed/WalletList.tsx +++ b/src/components/themed/WalletList.tsx @@ -1,5 +1,5 @@ import { FlashList } from '@shopify/flash-list' -import { EdgeAccount } from 'edge-core-js' +import { EdgeAccount, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { SectionList, ViewStyle } from 'react-native' @@ -10,7 +10,7 @@ import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' import { EdgeAsset, FlatListItem, WalletListItem } from '../../types/types' -import { getCreateWalletTypes, getTokenId } from '../../util/CurrencyInfoHelpers' +import { getCreateWalletTypes, getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { assetOverrides } from '../../util/serverState' import { normalizeForSearch } from '../../util/utils' import { showError } from '../services/AirshipInstance' @@ -39,7 +39,7 @@ interface Props { createWalletId?: string // Callbacks: - onPress?: (walletId: string, currencyCode: string, tokenId?: string) => void + onPress?: (walletId: string, tokenId: EdgeTokenId) => void } export interface WalletCreateItem { @@ -47,7 +47,7 @@ export interface WalletCreateItem { currencyCode: string displayName: string pluginId: string - tokenId?: string // Used for creating tokens + tokenId: EdgeTokenId // Used for creating tokens walletType?: string // Used for creating wallets createWalletIds?: string[] } @@ -93,12 +93,10 @@ export function WalletList(props: Props) { const handlePress = React.useMemo( () => onPress ?? - ((walletId: string, currencyCode: string) => { - const wallet = account.currencyWallets[walletId] - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + ((walletId: string, tokenId: EdgeTokenId) => { dispatch(selectWalletToken({ navigation, walletId, tokenId })).catch(err => showError(err)) }), - [account, dispatch, navigation, onPress] + [dispatch, navigation, onPress] ) // Filter the common wallet list: @@ -266,11 +264,13 @@ export const getCreateWalletList = (account: EdgeAccount, opts: CreateWalletList const createWalletCurrencies = getCreateWalletTypes(account, filterActivation) for (const createWalletCurrency of createWalletCurrencies) { const { currencyCode, currencyName, pluginId, walletType } = createWalletCurrency + const tokenId = getTokenIdForced(account, pluginId, currencyCode) walletList.push({ key: `create-${walletType}-${pluginId}`, currencyCode, displayName: currencyName, pluginId, + tokenId, walletType }) } diff --git a/src/components/themed/WalletListCreateRow.tsx b/src/components/themed/WalletListCreateRow.tsx index 6626505ec32..380c6542fa3 100644 --- a/src/components/themed/WalletListCreateRow.tsx +++ b/src/components/themed/WalletListCreateRow.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { View } from 'react-native' import { TouchableOpacity } from 'react-native-gesture-handler' @@ -13,7 +13,7 @@ import { useWatch } from '../../hooks/useWatch' import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' import { ThunkAction } from '../../types/reduxTypes' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { logEvent, TrackingEventName } from '../../util/tracking' import { CryptoIcon } from '../icons/CryptoIcon' import { ListModal } from '../modals/ListModal' @@ -30,7 +30,7 @@ export interface WalletListCreateRowProps { pluginId: string walletType?: string - onPress?: (walletId: string, currencyCode: string) => void + onPress?: (walletId: string, tokenId: EdgeTokenId) => void } export const WalletListCreateRowComponent = (props: WalletListCreateRowProps) => { @@ -54,12 +54,12 @@ export const WalletListCreateRowComponent = (props: WalletListCreateRowProps) => const theme = useTheme() const styles = getStyles(theme) - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) const networkName = pluginId != null && tokenId != null ? ` (${account.currencyConfig[pluginId].currencyInfo.displayName})` : '' const handlePress = useHandler(() => { - const handleRes = (walletId: string) => (onPress != null ? onPress(walletId, currencyCode) : null) + const handleRes = (walletId: string) => (onPress != null ? onPress(walletId, tokenId) : null) if (walletType != null) { dispatch(createAndSelectWallet({ walletType })) .then(handleRes) @@ -81,6 +81,7 @@ export const WalletListCreateRowComponent = (props: WalletListCreateRowProps) => Airship.show(bridge => { const renderRow = (wallet: EdgeCurrencyWallet) => ( <WalletListCurrencyRow + tokenId={null} wallet={wallet} onPress={walletId => { dispatch( diff --git a/src/components/themed/WalletListCurrencyRow.tsx b/src/components/themed/WalletListCurrencyRow.tsx index 5b9da9b27a0..28dfff67959 100644 --- a/src/components/themed/WalletListCurrencyRow.tsx +++ b/src/components/themed/WalletListCurrencyRow.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeToken } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeToken, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { useState } from 'react' import { TouchableOpacity } from 'react-native' @@ -17,12 +17,12 @@ import { EdgeText } from './EdgeText' interface Props { customAsset?: CustomAsset token?: EdgeToken - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet // Callbacks: onLongPress?: () => void - onPress?: (walletId: string, currencyCode: string, tokenId?: string, customAsset?: CustomAsset) => void + onPress?: (walletId: string, tokenId: EdgeTokenId, customAsset?: CustomAsset) => void } const WalletListCurrencyRowComponent = (props: Props) => { @@ -41,22 +41,17 @@ const WalletListCurrencyRowComponent = (props: Props) => { const pausedWallets = useSelector(state => state.ui.settings.userPausedWalletsSet) const isPaused = (pausedWallets != null && pausedWallets.has(wallet.id)) || isKeysOnlyPlugin(wallet.currencyInfo.pluginId) - // Currency code and wallet name for display: - const currencyCode = customAsset?.currencyCode ?? token?.currencyCode ?? wallet.currencyInfo.currencyCode - // // State // - const [primaryColor, setPrimaryColor] = useState<string>('#00000000') // // Handlers // - const handlePress = useHandler(() => { triggerHaptic('impactLight') - if (onPress != null) onPress(wallet.id, currencyCode, tokenId, customAsset) + if (onPress != null) onPress(wallet.id, tokenId, customAsset) }) const handleLongPress = useHandler(() => { diff --git a/src/components/themed/WalletListSortableRow.tsx b/src/components/themed/WalletListSortableRow.tsx index b0ee8b519e4..0d8c683198e 100644 --- a/src/components/themed/WalletListSortableRow.tsx +++ b/src/components/themed/WalletListSortableRow.tsx @@ -9,6 +9,7 @@ import { formatNumber, formatNumberInput } from '../../locales/intl' import { getDisplayDenominationFromState, getExchangeDenomination } from '../../selectors/DenominationSelectors' import { calculateFiatBalance } from '../../selectors/WalletSelectors' import { useDispatch, useSelector } from '../../types/reactRedux' +import { getWalletTokenId } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { DECIMAL_PRECISION, decimalOrZero, truncateDecimals } from '../../util/utils' import { CryptoIcon } from '../icons/CryptoIcon' @@ -49,8 +50,9 @@ function WalletListSortableRowComponent(props: Props) { const multiplier = displayDenomination.multiplier const name = getWalletName(wallet) const symbol = displayDenomination.symbol + const tokenId = getWalletTokenId(wallet, currencyCode) - const balance = wallet.balances[currencyCode] ?? '0' + const balance = wallet.balanceMap.get(tokenId) ?? '0' const preliminaryCryptoAmount = truncateDecimals(div(balance, multiplier, DECIMAL_PRECISION)) const finalCryptoAmount = formatNumberInput(decimalOrZero(preliminaryCryptoAmount, 6)) // make it show zero if infinitesimal number const finalCryptoAmountString = showBalance ? `${symbol || ''} ${finalCryptoAmount}` : '' @@ -66,7 +68,7 @@ function WalletListSortableRowComponent(props: Props) { <Ionicon name="ios-menu" size={theme.rem(1.25)} color={theme.icon} /> </View> <View style={styles.iconContainer}> - <CryptoIcon pluginId={wallet.currencyInfo.pluginId} walletId={wallet.id} /> + <CryptoIcon pluginId={wallet.currencyInfo.pluginId} walletId={wallet.id} tokenId={null} /> </View> <View style={styles.detailsContainer}> <View style={styles.detailsRow}> diff --git a/src/components/themed/WalletListSwipeable.tsx b/src/components/themed/WalletListSwipeable.tsx index e037c71d559..8128cad2b80 100644 --- a/src/components/themed/WalletListSwipeable.tsx +++ b/src/components/themed/WalletListSwipeable.tsx @@ -6,7 +6,7 @@ import { useHandler } from '../../hooks/useHandler' import { useDispatch, useSelector } from '../../types/reactRedux' import { NavigationProp } from '../../types/routerTypes' import { FlatListItem } from '../../types/types' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { EdgeAnim } from '../common/EdgeAnim' import { InsetStyles } from '../common/SceneWrapper' import { searchWalletList } from '../services/SortedWalletList' @@ -60,7 +60,7 @@ function WalletListSwipeableComponent(props: Props) { const handleCreateWallet = useHandler(async (walletId, currencyCode) => { const wallet = account.currencyWallets[walletId] - const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + const tokenId = getTokenIdForced(account, wallet.currencyInfo.pluginId, currencyCode) dispatch(selectWalletToken({ navigation, walletId, tokenId })) .then(() => navigation.navigate('transactionList', { walletId, tokenId })) .finally(onReset) diff --git a/src/components/themed/WalletListSwipeableCurrencyRow.tsx b/src/components/themed/WalletListSwipeableCurrencyRow.tsx index 9b25e55ca09..b9fd770060d 100644 --- a/src/components/themed/WalletListSwipeableCurrencyRow.tsx +++ b/src/components/themed/WalletListSwipeableCurrencyRow.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeToken } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeToken, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Text, TouchableOpacity } from 'react-native' import { AirshipBridge } from 'react-native-airship' @@ -21,7 +21,7 @@ interface Props { navigation: NavigationProp<'walletList'> token?: EdgeToken - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } diff --git a/src/components/themed/WalletListSwipeableLoadingRow.tsx b/src/components/themed/WalletListSwipeableLoadingRow.tsx index 49084ebaa06..d14b8304621 100644 --- a/src/components/themed/WalletListSwipeableLoadingRow.tsx +++ b/src/components/themed/WalletListSwipeableLoadingRow.tsx @@ -38,7 +38,7 @@ function WalletListSwipeableLoadingRowComponent(props: Props) { setTimeout(() => { if (rowRef.current != null) rowRef.current.close() }, 150) - Airship.show(bridge => <WalletListMenuModal bridge={bridge} navigation={navigation} walletId={walletId} />).catch(err => showError(err)) + Airship.show(bridge => <WalletListMenuModal bridge={bridge} navigation={navigation} walletId={walletId} tokenId={null} />).catch(err => showError(err)) }) // rendering ----------------------------------------------------------- diff --git a/src/components/tiles/AddressTile2.tsx b/src/components/tiles/AddressTile2.tsx index 71bd0b07042..c668620968a 100644 --- a/src/components/tiles/AddressTile2.tsx +++ b/src/components/tiles/AddressTile2.tsx @@ -16,7 +16,7 @@ import { lstrings } from '../../locales/strings' import { PaymentProtoError } from '../../types/PaymentProtoError' import { useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenId, getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { parseDeepLink } from '../../util/DeepLinkParser' import { checkPubAddress } from '../../util/FioAddressUtils' import { AddressModal } from '../modals/AddressModal' @@ -98,7 +98,7 @@ export const AddressTile2 = React.forwardRef((props: Props, ref: React.Forwarded fioAddress = address.toLowerCase() address = publicAddress } catch (e: any) { - if (!e.code || e.code !== fioPlugin.currencyInfo.defaultSettings.errorCodes.INVALID_FIO_ADDRESS) { + if (!e.code || e.code !== fioPlugin.currencyInfo.defaultSettings?.errorCodes.INVALID_FIO_ADDRESS) { setLoading(false) return showError(e) } @@ -217,7 +217,7 @@ export const AddressTile2 = React.forwardRef((props: Props, ref: React.Forwarded bridge={bridge} headerTitle={lstrings.your_wallets} navigation={navigation} - allowedAssets={[{ pluginId, tokenId: getTokenId(account, pluginId, currencyCode) }]} + allowedAssets={[{ pluginId, tokenId: getTokenIdForced(account, pluginId, currencyCode) }]} excludeWalletIds={[coreWallet.id]} /> )) @@ -227,7 +227,7 @@ export const AddressTile2 = React.forwardRef((props: Props, ref: React.Forwarded const wallet = currencyWallets[walletId] // Prefer segwit address if the selected wallet has one - const { segwitAddress, publicAddress } = await wallet.getReceiveAddress() + const { segwitAddress, publicAddress } = await wallet.getReceiveAddress({ tokenId: null }) const address = segwitAddress != null ? segwitAddress : publicAddress await changeAddress(address) }) diff --git a/src/components/tiles/CryptoFiatAmountTile.tsx b/src/components/tiles/CryptoFiatAmountTile.tsx index cbc5cd11aef..4762352c198 100644 --- a/src/components/tiles/CryptoFiatAmountTile.tsx +++ b/src/components/tiles/CryptoFiatAmountTile.tsx @@ -1,6 +1,6 @@ /* eslint-disable react-native/no-raw-text */ import { abs, div } from 'biggystring' -import { EdgeDenomination } from 'edge-core-js' +import { EdgeDenomination, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { MAX_CRYPTO_AMOUNT_CHARACTERS } from '../../constants/WalletAndCurrencyConstants' @@ -17,7 +17,7 @@ interface Props { nativeCryptoAmount: string title: string walletId: string - tokenId?: string + tokenId: EdgeTokenId type?: TileType onPress?: () => Promise<void> | void } diff --git a/src/components/tiles/FiatAmountTile.tsx b/src/components/tiles/FiatAmountTile.tsx index 6e10ddb2c7a..f98d3721739 100644 --- a/src/components/tiles/FiatAmountTile.tsx +++ b/src/components/tiles/FiatAmountTile.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { formatFiatString } from '../../hooks/useFiatText' @@ -16,7 +16,7 @@ interface Props { // Crypto amount props nativeCryptoAmount?: string - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } diff --git a/src/components/tiles/TotalDebtCollateralTile.tsx b/src/components/tiles/TotalDebtCollateralTile.tsx index 516ac3e007a..3c6c6ecf29f 100644 --- a/src/components/tiles/TotalDebtCollateralTile.tsx +++ b/src/components/tiles/TotalDebtCollateralTile.tsx @@ -11,5 +11,5 @@ export const TotalDebtCollateralTile = (props: { title: string; wallet: EdgeCurr // Floor totalFiatAmount value at '0' const totalFiatAmount = max('0', useTotalFiatAmount(wallet, debtsOrCollaterals)) - return <FiatAmountTile title={title} fiatAmount={totalFiatAmount} wallet={wallet} /> + return <FiatAmountTile title={title} fiatAmount={totalFiatAmount} wallet={wallet} tokenId={null} /> } diff --git a/src/components/ui4/AssetChangeTextUi4.tsx b/src/components/ui4/AssetChangeTextUi4.tsx index e46e1e347e1..681e20a7363 100644 --- a/src/components/ui4/AssetChangeTextUi4.tsx +++ b/src/components/ui4/AssetChangeTextUi4.tsx @@ -1,5 +1,5 @@ import { abs, div, gt, lt, mul, sub } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { TextStyle } from 'react-native' @@ -12,7 +12,7 @@ import { EdgeText } from '../themed/EdgeText' interface Props { wallet: EdgeCurrencyWallet - tokenId?: string + tokenId: EdgeTokenId style?: TextStyle } diff --git a/src/components/ui4/CryptoIconUi4.tsx b/src/components/ui4/CryptoIconUi4.tsx index 136a79fb7b7..03c208688b1 100644 --- a/src/components/ui4/CryptoIconUi4.tsx +++ b/src/components/ui4/CryptoIconUi4.tsx @@ -1,3 +1,4 @@ +import { EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Platform, StyleSheet, View } from 'react-native' import FastImage from 'react-native-fast-image' @@ -14,7 +15,7 @@ import { cacheStyles, Theme, useTheme } from '../services/ThemeContext' interface Props { // Main props - If non is specified, would just render an empty view pluginId?: string // Needed when walletId is not supplied and we still want to get an icon - tokenId?: string // Needed when it's a token (not the plugin's native currency) + tokenId: EdgeTokenId // Needed when it's a token (not the plugin's native currency) walletId?: string // To allow showing the progress ratio sync circle // Image props @@ -40,6 +41,7 @@ const CryptoIconComponent = (props: Props) => { const account = useSelector(state => state.core.account) const currencyWallets = useWatch(account, 'currencyWallets') const wallet = walletId != null ? currencyWallets[walletId] : null + const compromised = useSelector(state => { if (walletId == null) return 0 const { modalShown = 0 } = state.ui?.settings?.securityCheckedWallets?.[walletId] ?? {} @@ -78,7 +80,7 @@ const CryptoIconComponent = (props: Props) => { } // Get Parent Icon URI - const icon = getCurrencyIconUris(pluginId) + const icon = getCurrencyIconUris(pluginId, null) const source = { uri: mono ? icon.symbolImageDarkMono : icon.symbolImage } // Return Parent logo from the edge server diff --git a/src/components/ui4/CurrencyViewUi4.tsx b/src/components/ui4/CurrencyViewUi4.tsx index 0f6d134676e..1f7d2274ff1 100644 --- a/src/components/ui4/CurrencyViewUi4.tsx +++ b/src/components/ui4/CurrencyViewUi4.tsx @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeToken } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeToken, EdgeTokenId } from 'edge-core-js' import * as React from 'react' import { Text, View } from 'react-native' @@ -18,7 +18,7 @@ import { SplitRowsView } from './SplitRowsView' interface Props { nativeAmount?: string token?: EdgeToken - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet // Callbacks diff --git a/src/constants/plugins/GuiPlugins.ts b/src/constants/plugins/GuiPlugins.ts index 8d0b65b5b67..aba493872a0 100644 --- a/src/constants/plugins/GuiPlugins.ts +++ b/src/constants/plugins/GuiPlugins.ts @@ -21,32 +21,32 @@ export const guiPlugins: { [pluginId: string]: GuiPlugin } = { mandatoryPermissions: true, fixCurrencyCodes: { BAT: { pluginId: 'ethereum', tokenId: '0d8775f648430679a709e98d2b0cb6250d2887ef' }, - BCH: { pluginId: 'bitcoincash' }, - BNB: { pluginId: 'binance' }, - BTC: { pluginId: 'bitcoin' }, - CELO: { pluginId: 'celo' }, + BCH: { pluginId: 'bitcoincash', tokenId: null }, + BNB: { pluginId: 'binance', tokenId: null }, + BTC: { pluginId: 'bitcoin', tokenId: null }, + CELO: { pluginId: 'celo', tokenId: null }, CHZ: { pluginId: 'ethereum', tokenId: '3506424f91fd33084466f402d5d97f05f8e3b4af' }, COMP: { pluginId: 'ethereum', tokenId: 'c00e94cb662c3520282e6f5717214004a7f26888' }, DAI: { pluginId: 'ethereum', tokenId: '6b175474e89094c44da98b954eedeac495271d0f' }, - DASH: { pluginId: 'dash' }, - DGB: { pluginId: 'digibyte' }, - DOGE: { pluginId: 'dogecoin' }, - DOT: { pluginId: 'polkadot' }, - EOS: { pluginId: 'eos' }, - ETC: { pluginId: 'ethereumclassic' }, - ETH: { pluginId: 'ethereum' }, - FLOW: { pluginId: 'flow' }, - HBAR: { pluginId: 'hedera' }, - LTC: { pluginId: 'litecoin' }, - MATIC_POLYGON: { pluginId: 'polygon' }, - QTUM: { pluginId: 'qtum' }, - RVN: { pluginId: 'ravencoin' }, + DASH: { pluginId: 'dash', tokenId: null }, + DGB: { pluginId: 'digibyte', tokenId: null }, + DOGE: { pluginId: 'dogecoin', tokenId: null }, + DOT: { pluginId: 'polkadot', tokenId: null }, + EOS: { pluginId: 'eos', tokenId: null }, + ETC: { pluginId: 'ethereumclassic', tokenId: null }, + ETH: { pluginId: 'ethereum', tokenId: null }, + FLOW: { pluginId: 'flow', tokenId: null }, + HBAR: { pluginId: 'hedera', tokenId: null }, + LTC: { pluginId: 'litecoin', tokenId: null }, + MATIC_POLYGON: { pluginId: 'polygon', tokenId: null }, + QTUM: { pluginId: 'qtum', tokenId: null }, + RVN: { pluginId: 'ravencoin', tokenId: null }, TUSD: { pluginId: 'ethereum', tokenId: '0000000000085d4780b73119b644ae5ecd22b376' }, USDC: { pluginId: 'ethereum', tokenId: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, USDT: { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' }, - XLM: { pluginId: 'stellar' }, - XRP: { pluginId: 'ripple' }, - XTZ: { pluginId: 'tezos' }, + XLM: { pluginId: 'stellar', tokenId: null }, + XRP: { pluginId: 'ripple', tokenId: null }, + XTZ: { pluginId: 'tezos', tokenId: null }, ZRX: { pluginId: 'ethereum', tokenId: 'e41d2489571d322189246dafa5ebde1f4699f498' } } }, diff --git a/src/constants/txActionConstants.ts b/src/constants/txActionConstants.ts index 29135715f5b..0dbace26ea0 100644 --- a/src/constants/txActionConstants.ts +++ b/src/constants/txActionConstants.ts @@ -1,14 +1,21 @@ -import { EdgeTxActionStakeType, EdgeTxActionSwapType } from 'edge-core-js' +import { EdgeAssetActionType } from 'edge-core-js' import { lstrings } from '../locales/strings' -export const TX_ACTION_LABEL_MAP: Record<EdgeTxActionSwapType | EdgeTxActionStakeType, string> = { +export const TX_ACTION_LABEL_MAP: Record<EdgeAssetActionType, string> = { + buy: '', + sell: '', + sellNetworkFee: '', swap: lstrings.transaction_details_swap, swapOrderPost: lstrings.transaction_details_swap_order_post, swapOrderFill: lstrings.transaction_details_swap_order_fill, swapOrderCancel: lstrings.transaction_details_swap_order_cancel, stake: lstrings.transaction_details_stake, + stakeNetworkFee: '', stakeOrder: lstrings.transaction_details_stake_order, + tokenApproval: '', + transfer: '', unstake: lstrings.transaction_details_unstake, + unstakeNetworkFee: '', unstakeOrder: lstrings.transaction_details_unstake_order } diff --git a/src/controllers/action-queue/cleaners.ts b/src/controllers/action-queue/cleaners.ts index 001ce8a459a..16531e59523 100644 --- a/src/controllers/action-queue/cleaners.ts +++ b/src/controllers/action-queue/cleaners.ts @@ -3,6 +3,7 @@ // import { asArray, asBoolean, asEither, asMaybe, asNull, asNumber, asObject, asOptional, asString, asValue, Cleaner } from 'cleaners' +import { asEdgeTokenId } from '../../types/types' import { asBase64 } from '../../util/cleaners/asBase64' import { ActionEffect, @@ -87,7 +88,7 @@ const asWyreBuyActionOp = asObject<WyreBuyActionOp>({ type: asValue('wyre-buy'), nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asWyreSellActionOp = asObject<WyreSellActionOp>({ @@ -95,7 +96,7 @@ const asWyreSellActionOp = asObject<WyreSellActionOp>({ wyreAccountId: asString, nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asLoanBorrowActionOp = asObject<LoanBorrowActionOp>({ @@ -103,7 +104,7 @@ const asLoanBorrowActionOp = asObject<LoanBorrowActionOp>({ borrowPluginId: asString, nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asLoanDepositActionOp = asObject<LoanDepositActionOp>({ @@ -111,7 +112,7 @@ const asLoanDepositActionOp = asObject<LoanDepositActionOp>({ borrowPluginId: asString, nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asLoanRepayActionOp = asObject<LoanRepayActionOp>({ @@ -119,7 +120,8 @@ const asLoanRepayActionOp = asObject<LoanRepayActionOp>({ borrowPluginId: asString, nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId, + fromTokenId: asEdgeTokenId }) const asLoanWithdrawActionOp = asObject<LoanWithdrawActionOp>({ @@ -127,17 +129,17 @@ const asLoanWithdrawActionOp = asObject<LoanWithdrawActionOp>({ borrowPluginId: asString, nativeAmount: asString, walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asSwapActionOp = asObject<SwapActionOp>({ type: asValue('swap'), amountFor: asValue('from', 'to'), - fromTokenId: asOptional(asString), + fromTokenId: asEdgeTokenId, fromWalletId: asString, nativeAmount: asString, expectedPayoutNativeAmount: asOptional(asString), - toTokenId: asOptional(asString), + toTokenId: asEdgeTokenId, toWalletId: asString, displayKey: (raw: unknown) => asOptional(asSwapActionOpDisplayKey)(raw) }) @@ -175,7 +177,7 @@ const asAddressBalanceEffect = asObject<AddressBalanceEffect>({ aboveAmount: asOptional(asString), belowAmount: asOptional(asString), walletId: asString, - tokenId: asOptional(asString) + tokenId: asEdgeTokenId }) const asPushEventEffect = asObject<PushEventEffect>({ type: asValue('push-event'), diff --git a/src/controllers/action-queue/display.ts b/src/controllers/action-queue/display.ts index 25abc6e1544..6a6d4ab1b02 100644 --- a/src/controllers/action-queue/display.ts +++ b/src/controllers/action-queue/display.ts @@ -271,7 +271,7 @@ async function getActionOpDisplayKeyMessage(account: EdgeAccount, actionOp: ParA const fromCurrencyCode = getCurrencyCode(fromWallet, fromTokenId) const toCurrencyCode = getCurrencyCode(toWallet, toTokenId) - const feeCurrencyCode = getCurrencyCode(toWallet) + const feeCurrencyCode = getCurrencyCode(toWallet, null) titleData = { stringKey: `action_display_title_swap` } messageData = { diff --git a/src/controllers/action-queue/runtime/checkActionEffect.ts b/src/controllers/action-queue/runtime/checkActionEffect.ts index 9a232d3bebc..3b33be991d9 100644 --- a/src/controllers/action-queue/runtime/checkActionEffect.ts +++ b/src/controllers/action-queue/runtime/checkActionEffect.ts @@ -1,6 +1,5 @@ import { gte, lte } from 'biggystring' -import { getCurrencyCode } from '../../../util/CurrencyInfoHelpers' import { filterNull } from '../../../util/safeFilters' import { checkPushEvent } from '../push' import { ActionEffect, EffectCheckResult, ExecutionContext, SeqEffect } from '../types' @@ -101,8 +100,7 @@ export async function checkActionEffect(context: ExecutionContext, effect: Actio // TODO: Use effect.address when we can check address balances const { aboveAmount, belowAmount, tokenId, walletId } = effect const wallet = await account.waitForCurrencyWallet(walletId) - const currencyCode = getCurrencyCode(wallet, tokenId) - const walletBalance = wallet.balances[currencyCode] ?? '0' + const walletBalance = wallet.balanceMap.get(tokenId) ?? '0' return { delay: 15000, @@ -128,6 +126,7 @@ export async function checkActionEffect(context: ExecutionContext, effect: Actio // Get transaction const txs = await wallet.getTransactions({ // TODO: Add a parameter to limit to one transaction in result + tokenId: null, searchString: txId }) diff --git a/src/controllers/action-queue/runtime/evaluateAction.ts b/src/controllers/action-queue/runtime/evaluateAction.ts index 40f741a6ce4..8193d6588fa 100644 --- a/src/controllers/action-queue/runtime/evaluateAction.ts +++ b/src/controllers/action-queue/runtime/evaluateAction.ts @@ -2,7 +2,6 @@ import { add, mul } from 'biggystring' import { ApprovableAction, BorrowEngine, BorrowPlugin } from '../../../plugins/borrow-plugins/types' import { queryBorrowPlugins } from '../../../plugins/helpers/borrowPluginHelpers' -import { getCurrencyCode } from '../../../util/CurrencyInfoHelpers' import { getOrCreateLoanAccount } from '../../loan-manager/redux/actions' import { waitForBorrowEngineSync } from '../../loan-manager/util/waitForLoanAccountSync' import { ActionEffect, ActionProgram, ActionProgramState, BroadcastTx, ExecutableAction, ExecutionContext, ExecutionOutput, PendingTxMap } from '../types' @@ -242,15 +241,12 @@ export async function evaluateAction(context: ExecutionContext, program: ActionP const toWallet = await account.waitForCurrencyWallet(toWalletId) if (toWallet == null) throw new Error(`Wallet '${toWalletId}' not found for toWalletId`) - const fromCurrencyCode = getCurrencyCode(fromWallet, fromTokenId) - const toCurrencyCode = getCurrencyCode(toWallet, toTokenId) - const execute = async (): Promise<ExecutionOutput> => { const swapQuote = await account.fetchSwapQuote({ fromWallet, toWallet, - fromCurrencyCode, - toCurrencyCode, + fromTokenId, + toTokenId, nativeAmount, quoteFor: amountFor }) @@ -294,7 +290,7 @@ export async function evaluateAction(context: ExecutionContext, program: ActionP // quotes since the swap payout amount is not guaranteed. const swapPayoutNativeAmount = payoutNativeAmount != null ? payoutNativeAmount : amountFor === 'from' ? mul(swapData.payoutNativeAmount, '0.9') : swapData.payoutNativeAmount - const walletBalance = toWallet.balances[toCurrencyCode] ?? '0' + const walletBalance = toWallet.balanceMap.get(toTokenId) ?? '0' const aboveAmount = add(walletBalance, swapPayoutNativeAmount) const broadcastTxs: BroadcastTx[] = [ diff --git a/src/controllers/action-queue/types.ts b/src/controllers/action-queue/types.ts index 899f908046e..ae500739e04 100644 --- a/src/controllers/action-queue/types.ts +++ b/src/controllers/action-queue/types.ts @@ -1,4 +1,4 @@ -import { EdgeAccount, EdgeNetworkFee, EdgeTransaction } from 'edge-core-js' +import { EdgeAccount, EdgeNetworkFee, EdgeTokenId, EdgeTransaction } from 'edge-core-js' import { Dispatch, GetState } from '../../types/reduxTypes' import { CleanError } from './cleaners' @@ -30,7 +30,7 @@ export interface WyreBuyActionOp { type: 'wyre-buy' nativeAmount: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface WyreSellActionOp { @@ -38,7 +38,7 @@ export interface WyreSellActionOp { wyreAccountId: string nativeAmount: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface LoanBorrowActionOp { @@ -46,7 +46,7 @@ export interface LoanBorrowActionOp { borrowPluginId: string nativeAmount: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface LoanDepositActionOp { @@ -54,7 +54,7 @@ export interface LoanDepositActionOp { borrowPluginId: string nativeAmount: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface LoanRepayActionOp { @@ -62,8 +62,8 @@ export interface LoanRepayActionOp { borrowPluginId: string nativeAmount: string walletId: string - tokenId?: string - fromTokenId?: string + tokenId: EdgeTokenId + fromTokenId: EdgeTokenId } export interface LoanWithdrawActionOp { @@ -71,16 +71,16 @@ export interface LoanWithdrawActionOp { borrowPluginId: string nativeAmount: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface SwapActionOp { type: 'swap' amountFor: 'from' | 'to' - fromTokenId?: string + fromTokenId: EdgeTokenId fromWalletId: string nativeAmount: string - toTokenId?: string + toTokenId: EdgeTokenId toWalletId: string displayKey?: SwapActionOpDisplayKey @@ -130,7 +130,7 @@ export interface AddressBalanceEffect { aboveAmount?: string belowAmount?: string walletId: string - tokenId?: string + tokenId: EdgeTokenId } export interface PushEventEffect { type: 'push-event' diff --git a/src/controllers/action-queue/types/pushCleaners.ts b/src/controllers/action-queue/types/pushCleaners.ts index 3b7e6797aad..bc2a2c94bbf 100644 --- a/src/controllers/action-queue/types/pushCleaners.ts +++ b/src/controllers/action-queue/types/pushCleaners.ts @@ -1,5 +1,6 @@ import { asArray, asDate, asEither, asNumber, asObject, asOptional, asString, asTuple, asValue, Cleaner } from 'cleaners' +import { asLegacyTokenId } from '../../../types/types' import { asBase64 } from '../../../util/cleaners/asBase64' import { NewPushEvent } from './pushApiTypes' import { @@ -19,7 +20,7 @@ import { export const asAddressBalanceTrigger: Cleaner<AddressBalanceTrigger> = asObject({ type: asValue('address-balance'), pluginId: asString, - tokenId: asOptional(asString), + tokenId: asLegacyTokenId, address: asString, aboveAmount: asOptional(asString), // Satoshis or Wei or such belowAmount: asOptional(asString) // Satoshis or Wei or such diff --git a/src/controllers/action-queue/types/pushTypes.ts b/src/controllers/action-queue/types/pushTypes.ts index ef42be95245..57e850b2bc9 100644 --- a/src/controllers/action-queue/types/pushTypes.ts +++ b/src/controllers/action-queue/types/pushTypes.ts @@ -2,10 +2,12 @@ // Triggers that may cause an event to fire. // +import { EdgeTokenId } from 'edge-core-js' + export interface AddressBalanceTrigger { readonly type: 'address-balance' readonly pluginId: string - readonly tokenId?: string + readonly tokenId: EdgeTokenId readonly address: string readonly aboveAmount?: string // Satoshis or Wei or such readonly belowAmount?: string // Satoshis or Wei or such diff --git a/src/controllers/edgeProvider/EdgeProviderServer.tsx b/src/controllers/edgeProvider/EdgeProviderServer.tsx index b0adb19237d..a492b59c72c 100644 --- a/src/controllers/edgeProvider/EdgeProviderServer.tsx +++ b/src/controllers/edgeProvider/EdgeProviderServer.tsx @@ -1,6 +1,16 @@ import { abs } from 'biggystring' -import { asArray, asEither, asObject, asOptional, asString, Cleaner } from 'cleaners' -import { EdgeAccount, EdgeCurrencyWallet, EdgeParsedUri, EdgeReceiveAddress, EdgeSpendInfo, EdgeSpendTarget, EdgeTransaction, JsonObject } from 'edge-core-js' +import { asArray, asOptional } from 'cleaners' +import { + EdgeAccount, + EdgeCurrencyWallet, + EdgeParsedUri, + EdgeReceiveAddress, + EdgeSpendInfo, + EdgeSpendTarget, + EdgeTokenId, + EdgeTransaction, + JsonObject +} from 'edge-core-js' import * as React from 'react' import { Linking, Platform } from 'react-native' import { CustomTabs } from 'react-native-custom-tabs' @@ -19,10 +29,11 @@ import { Dispatch } from '../../types/reduxTypes' import { NavigationBase } from '../../types/routerTypes' import { EdgeAsset, MapObject } from '../../types/types' import { getCurrencyIconUris } from '../../util/CdnUris' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { makeCurrencyCodeTable } from '../../util/tokenIdTools' import { CurrencyConfigMap } from '../../util/utils' +import { asExtendedCurrencyCode } from './types/edgeProviderCleaners' import { EdgeGetReceiveAddressOptions, EdgeGetWalletHistoryResult, @@ -34,13 +45,7 @@ import { WalletDetails } from './types/edgeProviderTypes' -const asEdgeTokenIdExtended = asObject({ - pluginId: asString, - tokenId: asOptional(asString), - currencyCode: asOptional(asString) -}) - -const asCurrencyCodesArray: Cleaner<ExtendedCurrencyCode[] | undefined> = asOptional(asArray(asEither(asString, asEdgeTokenIdExtended))) +const asCurrencyCodesArray = asOptional(asArray(asExtendedCurrencyCode)) export class EdgeProviderServer implements EdgeProviderMethods { // Private properties: @@ -49,7 +54,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { _navigation: NavigationBase _plugin: GuiPlugin _reloadWebView: () => void - _selectedTokenId: string | undefined + _selectedTokenId: EdgeTokenId _selectedWallet: EdgeCurrencyWallet | undefined // Public properties: @@ -62,7 +67,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { navigation: NavigationBase plugin: GuiPlugin reloadWebView: () => void - selectedTokenId?: string + selectedTokenId: string | null selectedWallet?: EdgeCurrencyWallet }) { const { account, deepLink, dispatch, navigation, plugin, reloadWebView, selectedTokenId, selectedWallet } = opts @@ -113,7 +118,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { const chainCode = this._selectedWallet.currencyInfo.currencyCode const tokenCode = currencyCode const { pluginId } = this._selectedWallet.currencyInfo - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getTokenIdForced(account, pluginId, currencyCode) this._selectedTokenId = tokenId const unfixCode = unfixCurrencyCode(this._plugin.fixCurrencyCodes, pluginId, tokenId) @@ -147,7 +152,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { const wallet = this._selectedWallet if (wallet == null) throw new Error('No selected wallet') - const receiveAddress = await wallet.getReceiveAddress() + const receiveAddress = await wallet.getReceiveAddress({ tokenId: null }) if (options.metadata != null) { receiveAddress.metadata = options.metadata } @@ -162,7 +167,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { const { currencyConfig, currencyInfo, fiatCurrencyCode } = wallet const { currencyCode } = tokenId == null ? currencyInfo : currencyConfig.allTokens[tokenId] const walletName = getWalletName(wallet) - const receiveAddress = await wallet.getReceiveAddress() + const receiveAddress = await wallet.getReceiveAddress({ tokenId: null }) const icons = getCurrencyIconUris(wallet.currencyInfo.pluginId, tokenId) const returnObject: WalletDetails = { @@ -242,8 +247,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { const wallet = this._selectedWallet if (wallet == null) throw new Error('No selected wallet') - const { currencyConfig, currencyInfo, fiatCurrencyCode } = wallet - const { currencyCode } = tokenId == null ? currencyInfo : currencyConfig.allTokens[tokenId] + const { fiatCurrencyCode } = wallet // Prompt user with yes/no modal for permission const confirmTxShare = await Airship.show<'ok' | 'cancel' | undefined>(bridge => ( @@ -262,9 +266,9 @@ export class EdgeProviderServer implements EdgeProviderMethods { } // Grab transactions from current wallet - const balance = wallet.balances[currencyCode] ?? '0' + const balance = wallet.balanceMap.get(tokenId) ?? '0' - const txs = await wallet.getTransactions({ currencyCode }) + const txs = await wallet.getTransactions({ tokenId }) const result: EdgeGetWalletHistoryResult = { fiatCurrencyCode, balance, @@ -374,12 +378,14 @@ export class EdgeProviderServer implements EdgeProviderMethods { }): Promise<EdgeTransaction | undefined> { const wallet = this._selectedWallet if (wallet == null) throw new Error('No selected wallet') + const { tokenId } = spendInfo return await new Promise((resolve, reject) => { const lockTilesMap = lockInputs ? { address: true, amount: true, wallet: true } : undefined this._navigation.navigate('send2', { walletId: wallet.id, + tokenId, spendInfo, lockTilesMap, onBack: () => resolve(undefined), @@ -420,7 +426,7 @@ export class EdgeProviderServer implements EdgeProviderMethods { const wallet = this._selectedWallet if (wallet == null) throw new Error('No selected wallet') - const { publicAddress } = await wallet.getReceiveAddress() + const { publicAddress } = await wallet.getReceiveAddress({ tokenId: null }) const signedMessage = await wallet.signMessage(message, { otherParams: { publicAddress } }) console.log(`signMessage public address:***${publicAddress}***`) console.log(`signMessage signedMessage:***${signedMessage}***`) @@ -507,12 +513,14 @@ export function upgradeExtendedCurrencyCodes( out.push(...codeLookup(tokenCode).filter(match => match.pluginId === parent.pluginId)) } } else { - const { pluginId, tokenId, currencyCode } = code + const { pluginId } = code - if (currencyCode == null) { + if ('tokenId' in code) { + const { tokenId } = code // The object is already in the modern format: out.push({ pluginId, tokenId }) } else { + const { currencyCode } = code // The object contains a scoped currency code: out.push(...codeLookup(currencyCode).filter(match => match.pluginId === pluginId)) } @@ -522,7 +530,7 @@ export function upgradeExtendedCurrencyCodes( return out } -function unfixCurrencyCode(fixCurrencyCodes: { [badString: string]: EdgeAsset } = {}, pluginId: string, tokenId?: string): string | undefined { +function unfixCurrencyCode(fixCurrencyCodes: { [badString: string]: EdgeAsset } = {}, pluginId: string, tokenId: EdgeTokenId): string | undefined { return Object.keys(fixCurrencyCodes).find(uid => fixCurrencyCodes[uid].pluginId === pluginId && fixCurrencyCodes[uid].tokenId === tokenId) } diff --git a/src/controllers/edgeProvider/types/edgeProviderCleaners.ts b/src/controllers/edgeProvider/types/edgeProviderCleaners.ts index 40a93ab96a0..897b7718489 100644 --- a/src/controllers/edgeProvider/types/edgeProviderCleaners.ts +++ b/src/controllers/edgeProvider/types/edgeProviderCleaners.ts @@ -1,17 +1,17 @@ import { asArray, asBoolean, asEither, asNumber, asObject, asOptional, asString, asTuple, asUnknown, asValue, Cleaner } from 'cleaners' import { EdgeMemo, EdgeMetadata, EdgeNetworkFee, EdgeReceiveAddress, EdgeTransaction } from 'edge-core-js' +import { asEdgeAsset, asEdgeCurrencyCode, asEdgeTokenId } from '../../../types/types' import { EdgeGetWalletHistoryResult, EdgeProviderDeepLink, EdgeProviderMethods, EdgeProviderSpendTarget, EdgeRequestSpendOptions, - ExtendedCurrencyCode, WalletDetails } from './edgeProviderTypes' -const asEdgeMetadata = asObject<EdgeMetadata>({ +const asEdgeMetadata = asObject<EdgeMetadata & { amountFiat?: number }>({ amountFiat: asOptional(asNumber), bizId: asOptional(asNumber), category: asOptional(asString), @@ -46,8 +46,8 @@ const asEdgeMemo = asObject<EdgeMemo>({ const asEdgeTransaction = asObject<EdgeTransaction>({ walletId: asString, + tokenId: asEdgeTokenId, currencyCode: asString, - tokenId: asOptional(asString, null), // Amounts: nativeAmount: asString, @@ -67,14 +67,7 @@ const asEdgeTransaction = asObject<EdgeTransaction>({ txid: asString }) -const asExtendedCurrencyCode: Cleaner<ExtendedCurrencyCode> = asEither( - asString, - asObject({ - pluginId: asString, - tokenId: asOptional(asString), - currencyCode: asOptional(asString) - }) -) +export const asExtendedCurrencyCode = asEither(asString, asEdgeAsset, asEdgeCurrencyCode) const asWalletDetails = asObject<WalletDetails>({ name: asString, diff --git a/src/controllers/edgeProvider/types/edgeProviderTypes.ts b/src/controllers/edgeProvider/types/edgeProviderTypes.ts index 87b49e531a7..9efb06ac095 100644 --- a/src/controllers/edgeProvider/types/edgeProviderTypes.ts +++ b/src/controllers/edgeProvider/types/edgeProviderTypes.ts @@ -1,12 +1,8 @@ import { EdgeMetadata, EdgeNetworkFee, EdgeReceiveAddress, EdgeTransaction } from 'edge-core-js' -export type ExtendedCurrencyCode = - | string - | { - pluginId: string - tokenId?: string - currencyCode?: string - } +import { asExtendedCurrencyCode } from './edgeProviderCleaners' + +export type ExtendedCurrencyCode = ReturnType<typeof asExtendedCurrencyCode> export interface WalletDetails { name: string diff --git a/src/hooks/useCryptoText.ts b/src/hooks/useCryptoText.ts index dc1947b190a..8506003dc88 100644 --- a/src/hooks/useCryptoText.ts +++ b/src/hooks/useCryptoText.ts @@ -9,7 +9,7 @@ interface Props { nativeAmount: string wallet: EdgeCurrencyWallet hideBalance?: boolean - tokenId?: EdgeTokenId + tokenId: EdgeTokenId withSymbol?: boolean } diff --git a/src/hooks/useSelectedWallet.ts b/src/hooks/useSelectedWallet.ts index 53ea2661249..2ee3d9a5820 100644 --- a/src/hooks/useSelectedWallet.ts +++ b/src/hooks/useSelectedWallet.ts @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import { useWatch } from '../hooks/useWatch' import { useSelector } from '../types/reactRedux' @@ -6,7 +6,7 @@ import { getTokenId } from '../util/CurrencyInfoHelpers' export interface SelectedWallet { currencyCode: string - tokenId?: string + tokenId: EdgeTokenId wallet: EdgeCurrencyWallet } @@ -25,6 +25,7 @@ export function useSelectedWallet(): SelectedWallet | undefined { // because the selected token must exist before being selected, // so the selector above will force us to render in any case: const tokenId = getTokenId(account, wallet.currencyInfo.pluginId, currencyCode) + if (tokenId === undefined) return return { currencyCode, diff --git a/src/hooks/useTokenDisplayData.ts b/src/hooks/useTokenDisplayData.ts index 65bb8c6f1db..72e49845d7f 100644 --- a/src/hooks/useTokenDisplayData.ts +++ b/src/hooks/useTokenDisplayData.ts @@ -12,7 +12,7 @@ import { useWatch } from './useWatch' * 3. Localization: commas, decimals, spaces * */ -export const useTokenDisplayData = (props: { tokenId?: EdgeTokenId; wallet: EdgeCurrencyWallet }) => { +export const useTokenDisplayData = (props: { tokenId: EdgeTokenId; wallet: EdgeCurrencyWallet }) => { const { tokenId, wallet } = props const { currencyConfig, currencyInfo } = wallet const { allTokens } = currencyConfig diff --git a/src/hooks/useTransactionList.ts b/src/hooks/useTransactionList.ts index d190d816edc..b57babc814c 100644 --- a/src/hooks/useTransactionList.ts +++ b/src/hooks/useTransactionList.ts @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeTransaction } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId, EdgeTransaction } from 'edge-core-js' import * as React from 'react' import { showError } from '../components/services/AirshipInstance' @@ -19,7 +19,7 @@ interface Output { * so call the `requestMore` method to request more transactions, * until `atEnd` becomes true. */ -export function useTransactionList(wallet: EdgeCurrencyWallet, tokenId: string | undefined, searchString?: string): Output { +export function useTransactionList(wallet: EdgeCurrencyWallet, tokenId: EdgeTokenId, searchString?: string): Output { const { currencyCode } = tokenId == null ? wallet.currencyInfo : wallet.currencyConfig.allTokens[tokenId] const requestMore = React.useRef(() => {}) diff --git a/src/hooks/useWalletBalance.ts b/src/hooks/useWalletBalance.ts index bc31c433b8c..36da9023ba2 100644 --- a/src/hooks/useWalletBalance.ts +++ b/src/hooks/useWalletBalance.ts @@ -4,16 +4,14 @@ import * as React from 'react' /** * Subscribes to a specific balance within a wallet. */ -export function useWalletBalance(wallet: EdgeCurrencyWallet, tokenId?: EdgeTokenId): string { +export function useWalletBalance(wallet: EdgeCurrencyWallet, tokenId: EdgeTokenId): string { // The core still reports balances by currency code: - const token = tokenId == null ? null : wallet.currencyConfig.allTokens[tokenId] - const { currencyCode } = token == null ? wallet.currencyInfo : token - const [out, setOut] = React.useState<string>(wallet.balances[currencyCode] ?? '0') + const [out, setOut] = React.useState<string>(wallet.balanceMap.get(tokenId) ?? '0') React.useEffect(() => { - setOut(wallet.balances[currencyCode] ?? '0') - return wallet.watch('balances', balances => setOut(balances[currencyCode] ?? '0')) - }, [wallet, currencyCode]) + setOut(wallet.balanceMap.get(tokenId) ?? '0') + return wallet.watch('balanceMap', balances => setOut(wallet.balanceMap.get(tokenId) ?? '0')) + }, [wallet, tokenId]) return out } diff --git a/src/hooks/useWalletConnect.tsx b/src/hooks/useWalletConnect.tsx index a94bfab3781..cc6d29eaf05 100644 --- a/src/hooks/useWalletConnect.tsx +++ b/src/hooks/useWalletConnect.tsx @@ -119,7 +119,7 @@ export function useWalletConnect(): WalletConnect { const chainId = SPECIAL_CURRENCY_INFO[wallet.currencyInfo.pluginId].walletConnectV2ChainId if (chainId == null) return - const address = await wallet.getReceiveAddress() + const address = await wallet.getReceiveAddress({ tokenId: null }) const supportedNamespaces = getSupportedNamespaces(chainId, address.publicAddress) // Check that we support all required methods @@ -238,7 +238,7 @@ export const getAccounts = async (currencyWallets: { [walletId: string]: EdgeCur const chainId = SPECIAL_CURRENCY_INFO[wallet.currencyInfo.pluginId].walletConnectV2ChainId if (chainId == null) continue - const address = await currencyWallets[walletId].getReceiveAddress() + const address = await currencyWallets[walletId].getReceiveAddress({ tokenId: null }) const account = `${chainId.namespace}:${chainId.reference}:${address.publicAddress}` map.set(account, walletId) } diff --git a/src/plugins/borrow-plugins/common/ApprovableCall.ts b/src/plugins/borrow-plugins/common/ApprovableCall.ts index 2d058bcc475..d5a7f2b881f 100644 --- a/src/plugins/borrow-plugins/common/ApprovableCall.ts +++ b/src/plugins/borrow-plugins/common/ApprovableCall.ts @@ -3,6 +3,7 @@ import { EdgeCurrencyWallet, EdgeMetadata, EdgeSpendInfo, EdgeToken, EdgeTransac import { ethers } from 'ethers' import { PendingTxMap } from '../../../controllers/action-queue/types' +import { getWalletTokenId } from '../../../util/CurrencyInfoHelpers' import { ApprovableAction } from '../types' import { asBigNumber } from './cleaners/asBigNumber' import { SIDE_EFFECT_CURRENCY_CODE } from './constants' @@ -33,8 +34,10 @@ export const makeApprovableCall = async (params: CallInfo): Promise<ApprovableAc const makeApprovableCallSpend = async (dryrun: boolean, pendingTxMap: Readonly<PendingTxMap>): Promise<EdgeTransaction> => { const pendingTxs = pendingTxMap[walletId] + const currencyCode = spendToken?.currencyCode ?? wallet.currencyInfo.currencyCode + const tokenId = getWalletTokenId(wallet, currencyCode) const edgeSpendInfo: EdgeSpendInfo = { - currencyCode: spendToken?.currencyCode ?? wallet.currencyInfo.currencyCode, + tokenId, skipChecks: dryrun, spendTargets: [ { diff --git a/src/plugins/borrow-plugins/plugins/aave/AaveBorrowEngineFactory.ts b/src/plugins/borrow-plugins/plugins/aave/AaveBorrowEngineFactory.ts index b0bbdd66084..72bf2978c9f 100644 --- a/src/plugins/borrow-plugins/plugins/aave/AaveBorrowEngineFactory.ts +++ b/src/plugins/borrow-plugins/plugins/aave/AaveBorrowEngineFactory.ts @@ -1,6 +1,6 @@ import { add, div, gt, gte, lt, min, mul } from 'biggystring' import { asMaybe, Cleaner } from 'cleaners' -import { EdgeCurrencyWallet, EdgeToken } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeToken, EdgeTokenId } from 'edge-core-js' import { BigNumber, BigNumberish, ethers, Overrides } from 'ethers' import { ContractMethod, ParaSwap, SwapSide } from 'paraswap' @@ -38,7 +38,7 @@ export interface BorrowEngineBlueprint { export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => { return async (wallet: EdgeCurrencyWallet): Promise<BorrowEngine> => { const { aaveNetwork, asTokenContractAddress } = blueprint - const walletAddress = (await wallet.getReceiveAddress()).publicAddress + const walletAddress = (await wallet.getReceiveAddress({ tokenId: null })).publicAddress const REFERRAL_CODE = 0 // No referral code is used for AAVE contract calls const INTEREST_RATE_MODE = 2 // Only variable is supported for now @@ -47,7 +47,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // Private Methods // - const addressToTokenId = (address: string): string | undefined => { + const addressToTokenId = (address: string): EdgeTokenId => { const addressNormalized = address.toLowerCase() const tokenIds = Object.keys(wallet.currencyConfig.allTokens) for (const tokenId of tokenIds) { @@ -60,8 +60,9 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => return tokenId } } + throw new Error(`Cannot find token for contract address: ${address}`) } - const adjustCollateral = (tokenId: string | undefined, amount: string) => { + const adjustCollateral = (tokenId: EdgeTokenId, amount: string) => { if (instance.collaterals.length === 0) throw new Error(`Invalid execution time; too early invocation`) const index = instance.collaterals.findIndex(collateral => collateral.tokenId === tokenId) if (index === -1) throw new Error(`Could not find tokenId ${tokenId}`) @@ -71,7 +72,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // Update entries field to trigger change event instance.collaterals = [...instance.collaterals] } - const adjustDebt = (tokenId: string | undefined, amount: string) => { + const adjustDebt = (tokenId: EdgeTokenId, amount: string) => { if (instance.debts.length === 0) throw new Error(`Invalid execution time; too early invocation`) const index = instance.debts.findIndex(debt => debt.tokenId === tokenId) if (index === -1) throw new Error(`Could not find tokenId ${tokenId}`) @@ -81,7 +82,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // Update entries field to trigger change event instance.debts = [...instance.debts] } - const getToken = (tokenId?: string): EdgeToken => { + const getToken = (tokenId: EdgeTokenId): EdgeToken => { if (tokenId == null) throw new Error('Getting wrapped native token not supported yet. ' + 'Explicitly pass in tokenId for the wrapped token.') const token = wallet.currencyConfig.allTokens[tokenId] if (token == null) throw new Error(`Unable to find token on wallet for ${tokenId} tokenId`) @@ -217,11 +218,11 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => const token = getToken(tokenId) const tokenAddress = getTokenAddress(token) - const spenderAddress = (await wallet.getReceiveAddress()).publicAddress + const spenderAddress = (await wallet.getReceiveAddress({ tokenId: null })).publicAddress const asset = tokenAddress const amount = BigNumber.from(nativeAmount) - const onBehalfOf = fromWallet === wallet ? spenderAddress : (await fromWallet.getReceiveAddress()).publicAddress + const onBehalfOf = fromWallet === wallet ? spenderAddress : (await fromWallet.getReceiveAddress({ tokenId: null })).publicAddress const tokenContract = await aaveNetwork.makeTokenContract(tokenAddress) const gasPrice = await aaveNetwork.provider.getGasPrice() @@ -281,7 +282,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // Anything above the current collateral amount will be automatically // converted to the MAX_AMOUNT in order to withdraw all collateral. const contractTokenAmount = BigNumber.from(gt(nativeAmount, collateral.nativeAmount) ? MAX_AMOUNT : request.nativeAmount) - const to = (await toWallet.getReceiveAddress()).publicAddress + const to = (await toWallet.getReceiveAddress({ tokenId: null })).publicAddress const gasPrice = await aaveNetwork.provider.getGasPrice() @@ -318,7 +319,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => const asset = tokenAddress const amount = BigNumber.from(nativeAmount) - const onBehalfOf = (await fromWallet.getReceiveAddress()).publicAddress + const onBehalfOf = (await fromWallet.getReceiveAddress({ tokenId: null })).publicAddress const gasPrice = await aaveNetwork.provider.getGasPrice() @@ -359,7 +360,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // nativeAmount can't be zero if (nativeAmount === '0') throw new Error('BorrowEngine: repay request contains no nativeAmount.') - const fromAddress = (await fromWallet.getReceiveAddress()).publicAddress + const fromAddress = (await fromWallet.getReceiveAddress({ tokenId: null })).publicAddress const debtToken = getToken(tokenId) const debtTokenAddress = getTokenAddress(getToken(tokenId)) @@ -392,7 +393,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => // Build ParaSwap swap info // Cap the amount to swap at the debt amount const amountToSwap = amountToCover.mul(100 + PARASWAP_SLIPPAGE_PERCENT).div(100) - const chainId = fromWallet.currencyInfo.defaultSettings.otherSettings.chainParams.chainId + const chainId = fromWallet.currencyInfo.defaultSettings?.otherSettings.chainParams.chainId const paraswap = new ParaSwap(chainId, 'https://apiv5.paraswap.io') const priceRoute = await paraswap.getRate(collateralTokenAddress, debtTokenAddress, amountToSwap.toString(), fromAddress, SwapSide.BUY, { partner: 'aave', @@ -518,7 +519,7 @@ export const makeAaveBorrowEngineFactory = (blueprint: BorrowEngineBlueprint) => }, // Utilities - async getAprQuote(tokenId?: string): Promise<number> { + async getAprQuote(tokenId: EdgeTokenId): Promise<number> { const token = getToken(tokenId) const tokenAddress = getTokenAddress(token) diff --git a/src/plugins/borrow-plugins/types.ts b/src/plugins/borrow-plugins/types.ts index 0b13941e057..efee3d259c0 100644 --- a/src/plugins/borrow-plugins/types.ts +++ b/src/plugins/borrow-plugins/types.ts @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet, EdgeNetworkFee, EdgeTransaction } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeNetworkFee, EdgeTokenId, EdgeTransaction } from 'edge-core-js' import { Subscriber } from 'yaob' import { PendingTxMap } from '../../controllers/action-queue/types' @@ -9,7 +9,7 @@ import { PendingTxMap } from '../../controllers/action-queue/types' // Borrow more: export interface BorrowRequest { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string // Optional source for the funds which will borrow on behalf of the borrow engine's currencyWallet @@ -18,17 +18,17 @@ export interface BorrowRequest { // Make payment: export interface RepayRequest { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string // Optional source for the funds which will repay on behalf of the borrow engine's currencyWallet fromWallet?: EdgeCurrencyWallet - fromTokenId?: string + fromTokenId: EdgeTokenId } // Deposit collateral: export interface DepositRequest { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string // Optional source for the funds which will deposit on behalf of the borrow engine's currencyWallet @@ -37,7 +37,7 @@ export interface DepositRequest { // Withdraw collateral: export interface WithdrawRequest { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string // Optional destination for the funds @@ -73,12 +73,12 @@ export type BorrowActionId = 'loan-create' | 'loan-deposit' | 'loan-borrow' | 'l // ----------------------------------------------------------------------------- export interface BorrowCollateral { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string } export interface BorrowDebt { - tokenId?: string + tokenId: EdgeTokenId nativeAmount: string apr: number } @@ -112,7 +112,7 @@ export interface BorrowEngine { // Utilities: // Returns the APR for borrow a particular token - getAprQuote: (tokenId?: string) => Promise<number> + getAprQuote: (tokenId: EdgeTokenId) => Promise<number> // Calculates projected LTV after making a debt or collateral modification calculateProjectedLtv: (request: CalculateLtvRequest) => Promise<string> diff --git a/src/plugins/gui/RewardsCardPlugin.tsx b/src/plugins/gui/RewardsCardPlugin.tsx index 78176da588b..6f32bccc6c0 100644 --- a/src/plugins/gui/RewardsCardPlugin.tsx +++ b/src/plugins/gui/RewardsCardPlugin.tsx @@ -46,7 +46,9 @@ export const makeRewardsCardPlugin: FiatPluginFactory = async params => { // Get supported crypto assets: const supportedAssetMap = await provider.getSupportedAssets({ direction: 'sell', paymentTypes: [], regionCode: { countryCode: 'US' } }) - const allowedAssets: EdgeAsset[] = Object.keys(supportedAssetMap.crypto).map(pluginId => ({ pluginId })) + + // Only supporting mainnet currencies for now + const allowedAssets: EdgeAsset[] = Object.keys(supportedAssetMap.crypto).map(pluginId => ({ pluginId, tokenId: null })) // // Helpers: diff --git a/src/plugins/gui/amountQuotePlugin.ts b/src/plugins/gui/amountQuotePlugin.ts index 89b56bdd708..e341fd846bd 100644 --- a/src/plugins/gui/amountQuotePlugin.ts +++ b/src/plugins/gui/amountQuotePlugin.ts @@ -7,7 +7,7 @@ import { lstrings } from '../../locales/strings' import { config } from '../../theme/appConfig' import { EdgeAsset } from '../../types/types' import { getPartnerIconUri } from '../../util/CdnUris' -import { getTokenId } from '../../util/CurrencyInfoHelpers' +import { getTokenIdForced } from '../../util/CurrencyInfoHelpers' import { fetchInfo } from '../../util/network' import { logEvent } from '../../util/tracking' import { fuzzyTimeout } from '../../util/utils' @@ -125,7 +125,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi for (const currencyCode in currencyCodeMap) { if (currencyCodeMap[currencyCode]) { try { - const currencyTokenId = getTokenId(account, currencyPluginId, currencyCode) + const currencyTokenId = getTokenIdForced(account, currencyPluginId, currencyCode) allowedAssets.push({ pluginId: currencyPluginId, tokenId: currencyTokenId }) } catch (e: any) { // This is ok. We might not support a specific pluginId diff --git a/src/plugins/gui/fiatPluginTypes.ts b/src/plugins/gui/fiatPluginTypes.ts index 0421ffa9271..7b4b986dbdf 100644 --- a/src/plugins/gui/fiatPluginTypes.ts +++ b/src/plugins/gui/fiatPluginTypes.ts @@ -1,6 +1,6 @@ import { asValue } from 'cleaners' import { EdgeAccount } from 'edge-core-js' -import { EdgeTransaction } from 'edge-core-js/types' +import { EdgeTokenId, EdgeTransaction } from 'edge-core-js/types' import { DisablePluginMap } from '../../actions/ExchangeInfoActions' import { LaunchPaymentProtoParams } from '../../actions/PaymentProtoActions' @@ -97,7 +97,7 @@ export interface FiatPluginOpenExternalWebViewParams { export interface FiatPluginWalletPickerResult { walletId: string - tokenId: string | undefined + tokenId: EdgeTokenId /** @deprecated Use tokenId instead */ currencyCode: string } diff --git a/src/plugins/gui/fiatProviderTypes.ts b/src/plugins/gui/fiatProviderTypes.ts index 72e6262980c..1c8a1f84b78 100644 --- a/src/plugins/gui/fiatProviderTypes.ts +++ b/src/plugins/gui/fiatProviderTypes.ts @@ -1,4 +1,4 @@ -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import { FiatPaymentType, FiatPluginRegionCode, FiatPluginUi } from './fiatPluginTypes' @@ -58,7 +58,7 @@ export interface FiatProviderAssetMap { export interface FiatProviderGetQuoteParams { wallet?: EdgeCurrencyWallet pluginId: string - tokenId?: string + tokenId: EdgeTokenId displayCurrencyCode: string exchangeAmount: string fiatCurrencyCode: string diff --git a/src/plugins/gui/providers/banxaProvider.ts b/src/plugins/gui/providers/banxaProvider.ts index 81836291293..ea44a6d294f 100644 --- a/src/plugins/gui/providers/banxaProvider.ts +++ b/src/plugins/gui/providers/banxaProvider.ts @@ -468,7 +468,7 @@ export const banxaProvider: FiatProviderFactory = { expirationDate: new Date(Date.now() + 50000), approveQuote: async (approveParams: FiatProviderApproveQuoteParams): Promise<void> => { const { showUi, coreWallet } = approveParams - const receiveAddress = await coreWallet.getReceiveAddress() + const receiveAddress = await coreWallet.getReceiveAddress({ tokenId: null }) const bodyParams: any = { payment_method_id: paymentObj?.id ?? '', @@ -550,6 +550,7 @@ export const banxaProvider: FiatProviderFactory = { walletId: coreWallet.id, tokenId, spendInfo: { + tokenId, spendTargets: [ { nativeAmount, diff --git a/src/plugins/gui/providers/bityProvider.ts b/src/plugins/gui/providers/bityProvider.ts index c8604334f0c..fa3c5409588 100644 --- a/src/plugins/gui/providers/bityProvider.ts +++ b/src/plugins/gui/providers/bityProvider.ts @@ -6,6 +6,7 @@ import { sprintf } from 'sprintf-js' import { lstrings } from '../../../locales/strings' import { HomeAddress, SepaInfo } from '../../../types/FormTypes' import { StringMap } from '../../../types/types' +import { getWalletTokenId } from '../../../util/CurrencyInfoHelpers' import { FiatPaymentType, FiatPluginUi } from '../fiatPluginTypes' import { FiatProvider, @@ -250,7 +251,7 @@ const approveBityQuote = async ( if (orderData.message_to_sign != null) { const { body } = orderData.message_to_sign - const { publicAddress } = await wallet.getReceiveAddress() + const { publicAddress } = await wallet.getReceiveAddress({ tokenId: null }) const signedMessage = await wallet.signMessage(body, { otherParams: { publicAddress } }) const signUrl = baseUrl + orderData.message_to_sign.signature_submission_url const request = { @@ -408,7 +409,7 @@ export const bityProvider: FiatProviderFactory = { // Bity only checks SEPA info format validity. // Home address and KYC is only required for sell. - const cryptoAddress = (await coreWallet.getReceiveAddress()).publicAddress + const cryptoAddress = (await coreWallet.getReceiveAddress({ tokenId: null })).publicAddress await showUi.sepaForm({ headerTitle: lstrings.sepa_form_title, @@ -489,8 +490,10 @@ const completeSellOrder = async (approveQuoteRes: BityApproveQuoteResponse, core throw new Error('Bity: Could not find input denomination: ' + inputCurrencyCode) } + const tokenId = getWalletTokenId(coreWallet, inputCurrencyCode) + const spendInfo: EdgeSpendInfo = { - currencyCode: inputCurrencyCode, + tokenId, spendTargets: [ { nativeAmount, @@ -502,7 +505,7 @@ const completeSellOrder = async (approveQuoteRes: BityApproveQuoteResponse, core notes: `${pluginDisplayName} ${lstrings.transaction_details_exchange_order_id}: ${id}` } } - await showUi.send({ walletId: coreWallet.id, spendInfo }) + await showUi.send({ walletId: coreWallet.id, spendInfo, tokenId }) } /** diff --git a/src/plugins/gui/providers/moonpayProvider.ts b/src/plugins/gui/providers/moonpayProvider.ts index 1536c6b7f63..029adb8b586 100644 --- a/src/plugins/gui/providers/moonpayProvider.ts +++ b/src/plugins/gui/providers/moonpayProvider.ts @@ -249,7 +249,7 @@ export const moonpayProvider: FiatProviderFactory = { expirationDate: new Date(Date.now() + 8000), approveQuote: async (approveParams: FiatProviderApproveQuoteParams): Promise<void> => { const { coreWallet, showUi } = approveParams - const receiveAddress = await coreWallet.getReceiveAddress() + const receiveAddress = await coreWallet.getReceiveAddress({ tokenId: null }) const url = new URL('https://buy.moonpay.com?', true) const queryObj: MoonpayWidgetQueryParams = { apiKey, diff --git a/src/plugins/gui/providers/paybisProvider.ts b/src/plugins/gui/providers/paybisProvider.ts index cc8f3d90aff..cc3410624ff 100644 --- a/src/plugins/gui/providers/paybisProvider.ts +++ b/src/plugins/gui/providers/paybisProvider.ts @@ -216,20 +216,20 @@ const CRYPTO_DECIMALS = -8 const PAYBIS_TO_EDGE_CURRENCY_MAP: Record<string, ExtendedTokenId> = { // ADA: { pluginId: 'cardano' }, - BNB: { pluginId: 'binancechain' }, - BCH: { pluginId: 'bitcoincash' }, - BTC: { pluginId: 'bitcoin' }, - 'BTC-TESTNET': { pluginId: 'bitcointestnet', currencyCode: 'TESTBTC' }, - DOGE: { pluginId: 'dogecoin' }, - ETH: { pluginId: 'ethereum' }, - LTC: { pluginId: 'litecoin' }, - DOT: { pluginId: 'polkadot' }, - 'MATIC-POLYGON': { pluginId: 'polygon', currencyCode: 'MATIC' }, - SOL: { pluginId: 'solana' }, - TRX: { pluginId: 'tron' }, - XLM: { pluginId: 'stellar' }, - XRP: { pluginId: 'ripple' }, - XTZ: { pluginId: 'tezos' }, + BNB: { pluginId: 'binancechain', tokenId: null }, + BCH: { pluginId: 'bitcoincash', tokenId: null }, + BTC: { pluginId: 'bitcoin', tokenId: null }, + 'BTC-TESTNET': { pluginId: 'bitcointestnet', currencyCode: 'TESTBTC', tokenId: null }, + DOGE: { pluginId: 'dogecoin', tokenId: null }, + ETH: { pluginId: 'ethereum', tokenId: null }, + LTC: { pluginId: 'litecoin', tokenId: null }, + DOT: { pluginId: 'polkadot', tokenId: null }, + 'MATIC-POLYGON': { pluginId: 'polygon', currencyCode: 'MATIC', tokenId: null }, + SOL: { pluginId: 'solana', tokenId: null }, + TRX: { pluginId: 'tron', tokenId: null }, + XLM: { pluginId: 'stellar', tokenId: null }, + XRP: { pluginId: 'ripple', tokenId: null }, + XTZ: { pluginId: 'tezos', tokenId: null }, USDT: { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' }, USDC: { pluginId: 'ethereum', tokenId: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, SHIB: { pluginId: 'ethereum', tokenId: '95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce' }, @@ -456,7 +456,7 @@ export const paybisProvider: FiatProviderFactory = { paymentTypes, approveQuote: async (approveParams: FiatProviderApproveQuoteParams): Promise<void> => { const { coreWallet, showUi } = approveParams - const receiveAddress = await coreWallet.getReceiveAddress() + const receiveAddress = await coreWallet.getReceiveAddress({ tokenId: null }) let bodyParams if (direction === 'buy') { @@ -531,6 +531,7 @@ export const paybisProvider: FiatProviderFactory = { // Launch the SendScene to make payment const spendInfo: EdgeSpendInfo = { + tokenId, spendTargets: [ { nativeAmount, diff --git a/src/plugins/gui/providers/simplexProvider.ts b/src/plugins/gui/providers/simplexProvider.ts index eb13e932c2f..99e54292e06 100644 --- a/src/plugins/gui/providers/simplexProvider.ts +++ b/src/plugins/gui/providers/simplexProvider.ts @@ -315,7 +315,7 @@ export const simplexProvider: FiatProviderFactory = { expirationDate: new Date(Date.now() + 8000), approveQuote: async (approveParams: FiatProviderApproveQuoteParams): Promise<void> => { const { showUi, coreWallet } = approveParams - const receiveAddress = await coreWallet.getReceiveAddress() + const receiveAddress = await coreWallet.getReceiveAddress({ tokenId: null }) const data = { ts: Math.floor(Date.now() / 1000), diff --git a/src/plugins/stake-plugins/currency/tronStakePlugin.ts b/src/plugins/stake-plugins/currency/tronStakePlugin.ts index 09169e30ff7..fd0d9194e54 100644 --- a/src/plugins/stake-plugins/currency/tronStakePlugin.ts +++ b/src/plugins/stake-plugins/currency/tronStakePlugin.ts @@ -3,6 +3,7 @@ import { asDate, asMaybe, asObject, asString } from 'cleaners' import { EdgeSpendInfo, EdgeTransaction } from 'edge-core-js' import { lstrings } from '../../../locales/strings' +import { getWalletTokenId } from '../../../util/CurrencyInfoHelpers' import { ChangeQuote, ChangeQuoteRequest, @@ -160,7 +161,7 @@ export const makeTronStakePlugin = async (): Promise<StakePlugin> => { const resource = policy.rewardAssets[0].internalCurrencyCode ?? policy.rewardAssets[0].currencyCode const spendTargets = [ { - publicAddress: (await wallet.getReceiveAddress()).publicAddress + publicAddress: (await wallet.getReceiveAddress({ tokenId: null })).publicAddress } ] @@ -173,6 +174,7 @@ export const makeTronStakePlugin = async (): Promise<StakePlugin> => { } const spendInfo: EdgeSpendInfo = { + tokenId: null, spendTargets, otherParams: { type: 'addV2', @@ -184,6 +186,7 @@ export const makeTronStakePlugin = async (): Promise<StakePlugin> => { } case 'unstake': { const spendInfo: EdgeSpendInfo = { + tokenId: null, spendTargets, otherParams: { type: 'removeV2', @@ -195,6 +198,7 @@ export const makeTronStakePlugin = async (): Promise<StakePlugin> => { } case 'claim': { const spendInfo: EdgeSpendInfo = { + tokenId: null, spendTargets, otherParams: { type: 'withdrawExpireUnfreeze' @@ -242,7 +246,8 @@ export const makeTronStakePlugin = async (): Promise<StakePlugin> => { const policy = getPolicyFromId(stakePolicyId) const rewardAsset = policy.rewardAssets[0].internalCurrencyCode ?? policy.rewardAssets[0].currencyCode - const balanceTrx = wallet.balances[currencyCode] ?? '0' + const tokenId = getWalletTokenId(wallet, currencyCode) + const balanceTrx = wallet.balanceMap.get(tokenId) ?? '0' const canStake = gt(balanceTrx, '0') let canClaim = false const allocations: PositionAllocation[] = [] @@ -318,9 +323,10 @@ const fetchChangeQuoteV1 = async (request: ChangeQuoteRequest): Promise<ChangeQu const isStake = action === 'stake' const spendInfo: EdgeSpendInfo = { + tokenId: null, spendTargets: [ { - publicAddress: (await wallet.getReceiveAddress()).publicAddress + publicAddress: (await wallet.getReceiveAddress({ tokenId: null })).publicAddress } ], otherParams: { @@ -364,7 +370,8 @@ const fetchStakePositionV1 = async (request: StakePositionRequest): Promise<Stak const rewardAsset = policy.rewardAssets[0].currencyCode const stakedAmount = wallet.stakingStatus.stakedAmounts.find(amount => amount.otherParams?.type === rewardAsset) const nativeAmount = stakedAmount?.nativeAmount ?? '0' - const balanceTrx = wallet.balances[currencyCode] ?? '0' + const tokenId = getWalletTokenId(wallet, currencyCode) + const balanceTrx = wallet.balanceMap.get(tokenId) ?? '0' const locktime = stakedAmount?.unlockDate != null ? new Date(stakedAmount.unlockDate) : undefined return { diff --git a/src/plugins/stake-plugins/generic/util/EdgeWalletSigner.ts b/src/plugins/stake-plugins/generic/util/EdgeWalletSigner.ts index 8bd98e476b2..356dd321632 100644 --- a/src/plugins/stake-plugins/generic/util/EdgeWalletSigner.ts +++ b/src/plugins/stake-plugins/generic/util/EdgeWalletSigner.ts @@ -24,7 +24,7 @@ export class EdgeWalletSigner extends ethers.Signer { } async getAddress(): Promise<string> { - const { publicAddress } = await this.wallet.getReceiveAddress() + const { publicAddress } = await this.wallet.getReceiveAddress({ tokenId: null }) return publicAddress } @@ -77,6 +77,7 @@ export class EdgeWalletSigner extends ethers.Signer { const memoHexValue = data == null ? undefined : typeof data === 'string' ? data.replace('0x', '') : base16.stringify(data) const memos: EdgeMemo[] = memoHexValue == null ? [] : [{ type: 'hex', value: memoHexValue }] const edgeTransaction = await this.wallet.makeSpend({ + tokenId: null, spendTargets, memos, metadata: await transaction.customData, diff --git a/src/plugins/stake-plugins/thorchainSavers/tcSaversPlugin.ts b/src/plugins/stake-plugins/thorchainSavers/tcSaversPlugin.ts index 6d65e46f83c..399de692559 100644 --- a/src/plugins/stake-plugins/thorchainSavers/tcSaversPlugin.ts +++ b/src/plugins/stake-plugins/thorchainSavers/tcSaversPlugin.ts @@ -6,7 +6,7 @@ import { sprintf } from 'sprintf-js' import { asMaybeContractLocation } from '../../../components/scenes/EditTokenScene' import { lstrings } from '../../../locales/strings' import { StringMap } from '../../../types/types' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { getTokenId, getWalletTokenId } from '../../../util/CurrencyInfoHelpers' import { getHistoricalRate } from '../../../util/exchangeRates' import { cleanMultiFetch, fetchInfo, fetchWaterfall } from '../../../util/network' import { assert } from '../../gui/pluginUtils' @@ -429,11 +429,11 @@ const stakeRequest = async (opts: EdgeGuiPluginOptions, request: ChangeQuoteRequ const { wallet, nativeAmount, currencyCode, stakePolicyId, account } = request const { pluginId } = wallet.currencyInfo - const tokenId = getTokenId(account, pluginId, currencyCode) + const tokenId = getWalletTokenId(wallet, currencyCode) const isToken = tokenId != null const isEvm = EVM_PLUGINIDS[pluginId] - const walletBalance = wallet.balances[currencyCode] + const walletBalance = wallet.balanceMap.get(tokenId) ?? '0' const exchangeAmount = await wallet.nativeToDenomination(nativeAmount, currencyCode) const thorAmount = toFixed(mul(exchangeAmount, THOR_LIMIT_UNITS), 0, 0) const parentCurrencyCode = wallet.currencyInfo.currencyCode @@ -443,7 +443,7 @@ const stakeRequest = async (opts: EdgeGuiPluginOptions, request: ChangeQuoteRequ } if (lt(walletBalance, nativeAmount)) { - throw new InsufficientFundsError({ currencyCode }) + throw new InsufficientFundsError({ tokenId }) } await updateInboundAddresses(opts) @@ -526,7 +526,7 @@ const stakeRequest = async (opts: EdgeGuiPluginOptions, request: ChangeQuoteRequ assert(memoValue != null, 'Missing memoValue') if (lt(walletBalance, nativeAmount)) { - throw new InsufficientFundsError({ currencyCode: parentCurrencyCode }) + throw new InsufficientFundsError({ tokenId: null }) } } else { assert(router == null, 'router must be null') @@ -594,6 +594,7 @@ const stakeRequest = async (opts: EdgeGuiPluginOptions, request: ChangeQuoteRequ // 1. Fund the primary address with the requestedAmount + fees for tx #2 // 2. Send the requested amount to the pool address fundingSpendInfo = { + tokenId: null, spendTargets: [ { publicAddress: primaryAddress, @@ -613,7 +614,7 @@ const stakeRequest = async (opts: EdgeGuiPluginOptions, request: ChangeQuoteRequ const remainingBalance = sub(sub(walletBalance, mul(networkFee, '2')), nativeAmount) if (lt(remainingBalance, '0')) { - throw new InsufficientFundsError({ currencyCode }) + throw new InsufficientFundsError({ tokenId: null }) } } @@ -759,7 +760,9 @@ const unstakeRequestInner = async (opts: EdgeGuiPluginOptions, request: ChangeQu const { ninerealmsClientId } = asInitOptions(opts.initOptions) const { action, wallet, nativeAmount: requestNativeAmount, currencyCode, account } = request const { pluginId } = wallet.currencyInfo - const isToken = wallet.currencyInfo.currencyCode !== currencyCode + + const tokenId = getTokenId(account, pluginId, currencyCode) ?? null + const isToken = tokenId != null const isEvm = EVM_PLUGINIDS[pluginId] const policyCurrencyInfo = policyCurrencyInfos[pluginId] @@ -847,6 +850,7 @@ const unstakeRequestInner = async (opts: EdgeGuiPluginOptions, request: ChangeQu } const spendInfo: EdgeSpendInfo = { + tokenId, spendTargets: [{ publicAddress: poolAddress, nativeAmount: sendNativeAmount }], otherParams: { outputSort: 'targets', utxoSourceAddress, forceChangeAddress }, metadata: { @@ -893,6 +897,7 @@ const unstakeRequestInner = async (opts: EdgeGuiPluginOptions, request: ChangeQu // 2. Send the requested amount to the pool address const estimateTx = await wallet.makeSpend({ + tokenId: null, spendTargets: [{ publicAddress: primaryAddress, nativeAmount: sendNativeAmount }], memos: [ { @@ -905,7 +910,7 @@ const unstakeRequestInner = async (opts: EdgeGuiPluginOptions, request: ChangeQu const remainingBalance = sub(sub(parentBalance, mul(networkFee, '2')), sendNativeAmount) if (lt(remainingBalance, '0')) { - throw new InsufficientFundsError({ currencyCode }) + throw new InsufficientFundsError({ tokenId: null }) } } @@ -935,6 +940,7 @@ const unstakeRequestInner = async (opts: EdgeGuiPluginOptions, request: ChangeQu if (needsFundingPrimary) { // Transfer funds into the primary address const tx = await wallet.makeSpend({ + tokenId: null, spendTargets: [ { publicAddress: primaryAddress, @@ -1100,20 +1106,21 @@ const getPrimaryAddress = async ( parentBalance: string }> => { const displayPublicKey = await account.getDisplayPublicKey(wallet.id) + const tokenId = getWalletTokenId(wallet, currencyCode) const { publicAddress, nativeBalance } = await wallet.getReceiveAddress({ forceIndex: 0, - currencyCode + tokenId }) // If this is a single address chain (ie ETH, AVAX) // then the address balance is always the wallet balance const hasSingleAddress = displayPublicKey.toLowerCase() === publicAddress.toLowerCase() - const parentCurrencyCode = wallet.currencyInfo.currencyCode + const assetBalance = wallet.balanceMap.get(tokenId) ?? '0' return { primaryAddress: publicAddress, - addressBalance: hasSingleAddress ? wallet.balances[currencyCode] : nativeBalance ?? '0', - parentBalance: wallet.balances[parentCurrencyCode] + addressBalance: hasSingleAddress ? assetBalance : nativeBalance ?? '0', + parentBalance: wallet.balanceMap.get(null) ?? '0' } } diff --git a/src/reducers/scenes/SettingsReducer.ts b/src/reducers/scenes/SettingsReducer.ts index 9e8177a241b..6bb84f6aca5 100644 --- a/src/reducers/scenes/SettingsReducer.ts +++ b/src/reducers/scenes/SettingsReducer.ts @@ -54,7 +54,7 @@ export const settingsLegacy = (state: SettingsState = initialState, action: Acti // @ts-expect-error newState.denominationSettings[pluginId][currencyCode] = currencyInfo.denominations[0] } - for (const token of currencyInfo.metaTokens) { + for (const token of currencyInfo.metaTokens ?? []) { const tokenCode = token.currencyCode // @ts-expect-error newState.denominationSettings[pluginId][tokenCode] = token.denominations[0] diff --git a/src/selectors/WalletSelectors.ts b/src/selectors/WalletSelectors.ts index 148b95843c2..9693e8624c0 100644 --- a/src/selectors/WalletSelectors.ts +++ b/src/selectors/WalletSelectors.ts @@ -2,6 +2,7 @@ import { mul } from 'biggystring' import { EdgeCurrencyInfo, EdgeCurrencyWallet, EdgeDenomination } from 'edge-core-js' import { RootState, ThunkAction } from '../types/reduxTypes' +import { getWalletTokenId } from '../util/CurrencyInfoHelpers' import { getWalletFiat } from '../util/CurrencyWalletHelpers' import { convertNativeToExchange, zeroString } from '../util/utils' @@ -57,7 +58,8 @@ export const convertCurrencyFromExchangeRates = ( export const calculateFiatBalance = (wallet: EdgeCurrencyWallet, exchangeDenomination: EdgeDenomination, exchangeRates: { [pair: string]: string }): string => { const currencyCode = exchangeDenomination.name - const nativeBalance = wallet.balances[currencyCode] ?? '0' + const tokenId = getWalletTokenId(wallet, currencyCode) + const nativeBalance = wallet.balanceMap.get(tokenId) ?? '0' if (zeroString(nativeBalance)) return '0' const nativeToExchangeRatio: string = exchangeDenomination.multiplier const cryptoAmount = convertNativeToExchange(nativeToExchangeRatio)(nativeBalance) diff --git a/src/types/routerTypes.tsx b/src/types/routerTypes.tsx index 85e7e9e9893..ba4d45f0f23 100644 --- a/src/types/routerTypes.tsx +++ b/src/types/routerTypes.tsx @@ -1,6 +1,6 @@ import * as NavigationCore from '@react-navigation/core' import { StackActionHelpers } from '@react-navigation/native' -import { EdgeCurrencyInfo, EdgeCurrencyWallet, EdgeSpendInfo, JsonObject, OtpError } from 'edge-core-js' +import { EdgeCurrencyInfo, EdgeCurrencyWallet, EdgeSpendInfo, EdgeTokenId, JsonObject, OtpError } from 'edge-core-js' import { InitialRouteName } from 'edge-login-ui-rn' import { CoinRankingDetailsParams } from '../components/scenes/CoinRankingDetailsScene' @@ -112,7 +112,7 @@ export interface RouteParamList { displayName?: string multiplier?: string networkLocation?: JsonObject - tokenId?: string // Acts like "add token" if this is missing + tokenId?: EdgeTokenId // Acts like "add token" if this is missing walletId: string } exchange: {} @@ -185,11 +185,11 @@ export interface RouteParamList { } fioStakingChange: { change: 'add' | 'remove' - currencyCode: string + tokenId: EdgeTokenId walletId: string } fioStakingOverview: { - currencyCode: string + tokenId: EdgeTokenId walletId: string } home: {} @@ -204,12 +204,12 @@ export interface RouteParamList { loanCreateConfirmation: { borrowEngine: BorrowEngine borrowPlugin: BorrowPlugin - destTokenId: string + destTokenId: EdgeTokenId destWallet: EdgeCurrencyWallet nativeDestAmount: string nativeSrcAmount: string paymentMethod?: PaymentMethod - srcTokenId?: string + srcTokenId: EdgeTokenId srcWallet: EdgeCurrencyWallet } loanClose: { diff --git a/src/types/types.ts b/src/types/types.ts index a04645d9843..718ccb5a417 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,10 +1,25 @@ -import { asBoolean, asMaybe, asNumber, asObject, asString } from 'cleaners' -import { EdgeCurrencyWallet, EdgeDenomination, EdgeMetadata, EdgeToken } from 'edge-core-js/types' +import { asBoolean, asEither, asMaybe, asNull, asNumber, asObject, asOptional, asString } from 'cleaners' +import { EdgeCurrencyWallet, EdgeDenomination, EdgeMetadata, EdgeToken, EdgeTokenId } from 'edge-core-js/types' import { LocaleStringKey } from '../locales/en_US' import { RootState } from './reduxTypes' import { Theme } from './Theme' +/** @deprecated Only to be used for payloads that still allow undefined for + * tokenId such as notification server + */ +export const asLegacyTokenId = asOptional(asString, null) + +export const asEdgeTokenId = asEither(asString, asNull) +export const asEdgeAsset = asObject({ + pluginId: asString, + tokenId: asEdgeTokenId +}) +export const asEdgeCurrencyCode = asObject({ + pluginId: asString, + currencyCode: asString +}) + export interface BooleanMap { [key: string]: boolean } @@ -20,7 +35,7 @@ export interface MapObject<T> { export interface GuiCurrencyInfo { walletId: string - tokenId: string | undefined + tokenId: EdgeTokenId displayCurrencyCode: string exchangeCurrencyCode: string displayDenomination: EdgeDenomination @@ -110,7 +125,7 @@ export interface CurrencyConverter { export const emptyCurrencyInfo: GuiCurrencyInfo = { walletId: '', - tokenId: undefined, + tokenId: null, displayCurrencyCode: '', exchangeCurrencyCode: '', displayDenomination: { @@ -307,19 +322,19 @@ export interface AppConfig { */ export interface WalletListItem { key: string + tokenId: EdgeTokenId + walletId: string - // These will be set for token rows: + // `token` will be set for token rows: token?: EdgeToken - tokenId?: string // The wallet will be present once it loads: wallet?: EdgeCurrencyWallet - walletId: string } export interface EdgeAsset { pluginId: string - tokenId?: string + tokenId: EdgeTokenId } export interface TempActionDisplayInfo { diff --git a/src/util/ActionProgramUtils.ts b/src/util/ActionProgramUtils.ts index 9b37dc64a8b..cc7b90ff643 100644 --- a/src/util/ActionProgramUtils.ts +++ b/src/util/ActionProgramUtils.ts @@ -1,5 +1,5 @@ import { add, div, gt, gte, log10, max, mul, sub, toFixed } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js/types' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js/types' import { sprintf } from 'sprintf-js' import { MAX_AMOUNT } from '../constants/valueConstants' @@ -26,7 +26,7 @@ export interface LoanAsset { wallet: EdgeCurrencyWallet nativeAmount: string paymentMethodId?: string - tokenId?: string + tokenId: EdgeTokenId } interface AaveCreateActionParams { @@ -65,6 +65,7 @@ export const makeAaveCreateActionProgram = async (params: AaveCreateActionParams await enableToken(depositTokenCc, borrowEngineWallet) const toTokenId = source.tokenId ?? Object.keys(allTokens).find(tokenId => allTokens[tokenId].currencyCode === 'WBTC') + if (toTokenId == null) throw new Error(`makeAaveCreateActionProgram: Cannot find toTokenId`) // If deposit source wallet is not the borrowEngineWallet, swap first into the borrow engine wallet + deposit token before depositing. @@ -178,9 +179,9 @@ export const makeAaveDepositAction = async ({ }: { borrowEngineWallet: EdgeCurrencyWallet borrowPluginId: string - depositTokenId?: string + depositTokenId: EdgeTokenId nativeAmount: string - srcTokenId?: string + srcTokenId: EdgeTokenId srcWallet: EdgeCurrencyWallet }): Promise<ActionOp> => { const sequenceActions: ActionOp[] = [] @@ -197,6 +198,7 @@ export const makeAaveDepositAction = async ({ await enableToken(depositTokenCc, borrowEngineWallet) const allTokens = borrowEngineWallet.currencyConfig.allTokens const tokenId = depositTokenId ?? Object.keys(allTokens).find(tokenId => allTokens[tokenId].currencyCode === 'WBTC') + if (tokenId == null) throw new Error(`makeAaveDepositAction: Cannot find tokenId`) // If deposit source wallet is not the borrowEngineWallet, swap first into the borrow engine wallet + deposit token before depositing. if (srcWallet.id !== borrowEngineWallet.id) { @@ -289,7 +291,7 @@ export const makeAaveCloseAction = async ({ isoFiatCurrencyCode, convertNativeToExchange(debtDenom.multiplier)(debt.nativeAmount) ) - const debtBalanceNativeAmount = wallet.balances[debtCurrencyCode] + const debtBalanceNativeAmount = wallet.balanceMap.get(debtTokenId) ?? '0' const debtBalanceFiat = convertCurrencyFromExchangeRates( exchangeRates, debtCurrencyCode, @@ -352,14 +354,19 @@ export const makeAaveCloseAction = async ({ } // Repay with balance actions - if (!zeroString(repayWithBalanceNativeAmount)) - evmActions.push({ - type: 'loan-repay', - nativeAmount: repayWithBalanceNativeAmount, - borrowPluginId, - tokenId: debtTokenId, - walletId: wallet.id - }) + if (!zeroString(repayWithBalanceNativeAmount)) { + throw new Error('Need to revisit what fromTokenId should be assigned to due to core2.0') + // evmActions.push({ + // type: 'loan-repay', + // nativeAmount: repayWithBalanceNativeAmount, + // borrowPluginId, + // tokenId: debtTokenId, + + // // XXX what should fromTokenId be? + // fromTokenId: null, + // walletId: wallet.id + // }) + } // Repay with collateral actions if (!zeroString(repayWithCollateralNativeAmount)) diff --git a/src/util/CdnUris.ts b/src/util/CdnUris.ts index b7c2cded57e..da9122491e5 100644 --- a/src/util/CdnUris.ts +++ b/src/util/CdnUris.ts @@ -1,3 +1,5 @@ +import { EdgeTokenId } from 'edge-core-js' + import { EDGE_CONTENT_SERVER_URI } from '../constants/CdnConstants' import { BorrowPluginInfo } from '../plugins/borrow-plugins/types' import { edgeDark } from '../theme/variables/edgeDark' @@ -46,8 +48,9 @@ export interface CurrencyIcons { symbolImageDarkMono: string } -export function getCurrencyIconUris(pluginId: string, contractAddress: string = pluginId): CurrencyIcons { - const currencyPath = `${pluginId}/${removeHexPrefix(contractAddress)}`.toLowerCase() +export function getCurrencyIconUris(pluginId: string, tokenId: EdgeTokenId): CurrencyIcons { + const iconFile = tokenId ?? pluginId + const currencyPath = `${pluginId}/${removeHexPrefix(iconFile)}`.toLowerCase() return { symbolImage: `${edgeLight.iconServerBaseUri}/currencyIconsV3/${currencyPath}.png`, symbolImageDarkMono: `${edgeDark.iconServerBaseUri}/currencyIconsV3/${currencyPath}_dark.png` diff --git a/src/util/CurrencyInfoHelpers.ts b/src/util/CurrencyInfoHelpers.ts index 8f74375c493..e7bc46d8af9 100644 --- a/src/util/CurrencyInfoHelpers.ts +++ b/src/util/CurrencyInfoHelpers.ts @@ -108,22 +108,41 @@ export function getCreateWalletType(account: EdgeAccount, currencyCode: string): return currencyInfo ? makeCreateWalletType(currencyInfo) : null } -export const getTokenId = (account: EdgeAccount, pluginId: string, currencyCode: string): string | undefined => { +export const getTokenId = (account: EdgeAccount, pluginId: string, currencyCode: string): EdgeTokenId | undefined => { const currencyConfig = account.currencyConfig[pluginId] if (currencyConfig == null) return + if (currencyConfig.currencyInfo.currencyCode === currencyCode) return null const { allTokens } = currencyConfig - return Object.keys(allTokens).find(edgeToken => allTokens[edgeToken].currencyCode === currencyCode) + const tokenId = Object.keys(allTokens).find(edgeToken => allTokens[edgeToken].currencyCode === currencyCode) + return tokenId +} + +export const getTokenIdForced = (account: EdgeAccount, pluginId: string, currencyCode: string): EdgeTokenId => { + const tokenId = getTokenId(account, pluginId, currencyCode) + if (tokenId === undefined) throw new Error('getTokenIdForced: tokenId not found') + return tokenId +} + +export const getWalletTokenId = (wallet: EdgeCurrencyWallet, currencyCode: string): EdgeTokenId => { + const { currencyConfig, currencyInfo } = wallet + if (currencyInfo.currencyCode === currencyCode) return null + const { allTokens } = currencyConfig ?? {} + const tokenId = Object.keys(allTokens).find(edgeToken => allTokens[edgeToken].currencyCode === currencyCode) + if (tokenId == null) { + throw new Error(`Cannot find tokenId for currencyCode ${currencyCode}`) + } + return tokenId } /** * Get the currencyCode associated with a tokenId */ -export const getCurrencyCode = (wallet: EdgeCurrencyWallet, tokenId?: EdgeTokenId): string => { +export const getCurrencyCode = (wallet: EdgeCurrencyWallet, tokenId: EdgeTokenId): string => { const { currencyCode } = tokenId != null ? wallet.currencyConfig.allTokens[tokenId] : wallet.currencyInfo return currencyCode } -export const getToken = (wallet: EdgeCurrencyWallet, tokenId?: string): EdgeToken | undefined => { +export const getToken = (wallet: EdgeCurrencyWallet, tokenId: EdgeTokenId): EdgeToken | undefined => { if (tokenId == null) { // Either special handling should be done by the caller, or the workflow should not allow this to execute. } else { diff --git a/src/util/CurrencyWalletHelpers.ts b/src/util/CurrencyWalletHelpers.ts index 97308327710..f97903d6e84 100644 --- a/src/util/CurrencyWalletHelpers.ts +++ b/src/util/CurrencyWalletHelpers.ts @@ -1,10 +1,11 @@ import { sub } from 'biggystring' -import { EdgeCurrencyWallet } from 'edge-core-js' +import { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import { sprintf } from 'sprintf-js' import { showFullScreenSpinner } from '../components/modals/AirshipFullScreenSpinner' import { SPECIAL_CURRENCY_INFO } from '../constants/WalletAndCurrencyConstants' import { lstrings } from '../locales/strings' +import { getWalletTokenId } from './CurrencyInfoHelpers' import { getFioStakingBalances } from './stakeUtils' /** @@ -25,9 +26,14 @@ export function getWalletFiat(wallet: EdgeCurrencyWallet): { fiatCurrencyCode: s export const getAvailableBalance = (wallet: EdgeCurrencyWallet, tokenCode?: string): string => { const { currencyCode, pluginId } = wallet.currencyInfo - const cCode = tokenCode ?? currencyCode - let balance = wallet.balances[cCode] ?? '0' - if (SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported) { + let tokenId: EdgeTokenId + if (tokenCode == null || tokenCode === currencyCode) { + tokenId = null + } else { + tokenId = getWalletTokenId(wallet, tokenCode) + } + let balance = wallet.balanceMap.get(tokenId) ?? '0' + if (SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported && tokenId == null) { // Special case for FIO mainnet (no token) const { locked } = getFioStakingBalances(wallet.stakingStatus) balance = sub(balance, locked) diff --git a/src/util/FioAddressUtils.ts b/src/util/FioAddressUtils.ts index bdffc85252b..4be789c3d35 100644 --- a/src/util/FioAddressUtils.ts +++ b/src/util/FioAddressUtils.ts @@ -135,7 +135,7 @@ export const setFioExpiredCheckToDisklet = async (lastChecks: { [fioName: string export const fioMakeSpend = async (fioWallet: EdgeCurrencyWallet, actionName: string, params: unknown): Promise<EdgeTransaction> => { const fakeSpendTarget = { publicAddress: '', nativeAmount: '0' } const spendInfo: EdgeSpendInfo = { - currencyCode: 'FIO', + tokenId: null, spendTargets: [fakeSpendTarget], otherParams: { action: { @@ -198,7 +198,7 @@ const isWalletConnected = async ( return true } } - const receiveAddress = await wallet.getReceiveAddress() + const receiveAddress = await wallet.getReceiveAddress({ tokenId: null }) if (connectedAddress === receiveAddress.publicAddress) return true } catch (e: any) { // @@ -469,14 +469,17 @@ export const checkPubAddress = async (fioPlugin: EdgeCurrencyConfig, fioAddress: const { public_address: publicAddress } = await fioPlugin.otherMethods.getConnectedPublicAddress(fioAddress.toLowerCase(), chainCode, tokenCode) return publicAddress } catch (e: any) { - if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings.errorCodes.INVALID_FIO_ADDRESS) { - throw new FioError(lstrings.fio_error_invalid_address, fioPlugin.currencyInfo.defaultSettings.errorCodes.INVALID_FIO_ADDRESS) + if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings?.errorCodes.INVALID_FIO_ADDRESS) { + throw new FioError(lstrings.fio_error_invalid_address, fioPlugin.currencyInfo.defaultSettings?.errorCodes.INVALID_FIO_ADDRESS) } - if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_ADDRESS_IS_NOT_EXIST) { - throw new FioError(lstrings.send_fio_request_error_addr_not_exist, fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_ADDRESS_IS_NOT_EXIST) + if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_ADDRESS_IS_NOT_EXIST) { + throw new FioError(lstrings.send_fio_request_error_addr_not_exist, fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_ADDRESS_IS_NOT_EXIST) } - if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_ADDRESS_IS_NOT_LINKED) { - throw new FioError(sprintf(lstrings.err_address_not_linked_title, tokenCode), fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_ADDRESS_IS_NOT_LINKED) + if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_ADDRESS_IS_NOT_LINKED) { + throw new FioError( + sprintf(lstrings.err_address_not_linked_title, tokenCode), + fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_ADDRESS_IS_NOT_LINKED + ) } throw new Error(lstrings.fio_connect_wallets_err) } @@ -615,7 +618,7 @@ export const checkIsDomainPublic = async (fioPlugin: EdgeCurrencyConfig, domain: try { isDomainPublic = fioPlugin.otherMethods ? await fioPlugin.otherMethods.isDomainPublic(domain) : false } catch (e: any) { - if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_DOMAIN_IS_NOT_EXIST) { + if (e.labelCode && e.labelCode === fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_DOMAIN_IS_NOT_EXIST) { throw new Error(lstrings.fio_get_reg_info_domain_err_msg) } @@ -666,7 +669,7 @@ export const getRegInfo = async ( return { activationCost, feeValue, - supportedAssets: [{ pluginId: 'fio' }], + supportedAssets: [{ pluginId: 'fio', tokenId: null }], supportedCurrencies: { [FIO_STR]: true }, paymentInfo: { [FIO_STR]: { @@ -679,7 +682,7 @@ export const getRegInfo = async ( } // todo: temporary commented to use fallback referral code by default. // const referralCode = isFallback ? fioPlugin.currencyInfo.defaultSettings.fallbackRef : fioPlugin.currencyInfo.defaultSettings.defaultRef - const reqResult = await buyAddressRequest(fioPlugin, fioAddress, fioPlugin.currencyInfo.defaultSettings.fallbackRef, selectedWallet, activationCost) + const reqResult = await buyAddressRequest(fioPlugin, fioAddress, fioPlugin.currencyInfo.defaultSettings?.fallbackRef, selectedWallet, activationCost) return { ...reqResult, feeValue @@ -717,7 +720,7 @@ export const getDomainRegInfo = async ( throw new Error(lstrings.fio_get_fee_err_msg) } - const reqResult = await buyAddressRequest(fioPlugin, fioDomain, fioPlugin.currencyInfo.defaultSettings.defaultRef, selectedWallet, activationCost) + const reqResult = await buyAddressRequest(fioPlugin, fioDomain, fioPlugin.currencyInfo.defaultSettings?.defaultRef, selectedWallet, activationCost) return { ...reqResult, feeValue @@ -781,12 +784,12 @@ const buyAddressRequest = async ( } } catch (e: any) { const errorMessages = { - [fioPlugin.currencyInfo.defaultSettings.errorCodes.INVALID_FIO_ADDRESS]: lstrings.fio_error_invalid_address, - [fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_DOMAIN_IS_NOT_EXIST]: lstrings.fio_get_reg_info_domain_err_msg, - [fioPlugin.currencyInfo.defaultSettings.errorCodes.FIO_DOMAIN_IS_NOT_PUBLIC]: lstrings.fio_address_register_domain_is_not_public, - [fioPlugin.currencyInfo.defaultSettings.errorCodes.SERVER_ERROR]: lstrings.fio_get_reg_info_err_msg, - [fioPlugin.currencyInfo.defaultSettings.errorCodes.ALREADY_SENT_REGISTRATION_REQ_FOR_DOMAIN]: lstrings.fio_get_reg_info_already_sent_err_msg, - [fioPlugin.currencyInfo.defaultSettings.errorCodes.ALREADY_REGISTERED]: lstrings.fio_address_register_screen_not_available + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.INVALID_FIO_ADDRESS]: lstrings.fio_error_invalid_address, + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_DOMAIN_IS_NOT_EXIST]: lstrings.fio_get_reg_info_domain_err_msg, + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.FIO_DOMAIN_IS_NOT_PUBLIC]: lstrings.fio_address_register_domain_is_not_public, + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.SERVER_ERROR]: lstrings.fio_get_reg_info_err_msg, + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.ALREADY_SENT_REGISTRATION_REQ_FOR_DOMAIN]: lstrings.fio_get_reg_info_already_sent_err_msg, + [fioPlugin.currencyInfo.defaultSettings?.errorCodes.ALREADY_REGISTERED]: lstrings.fio_address_register_screen_not_available } if (e.labelCode && errorMessages[e.labelCode]) { throw new Error(errorMessages[e.labelCode]) @@ -998,13 +1001,13 @@ export const convertEdgeToFIOCodes = (pluginId: string, edgeChainCode: string, e } export const fioToEdgeMap: MapObject<EdgeAsset> = { - bitcoin: { pluginId: 'bitcoin' }, - bitcoincash: { pluginId: 'bitcoincash' }, + bitcoin: { pluginId: 'bitcoin', tokenId: null }, + bitcoincash: { pluginId: 'bitcoincash', tokenId: null }, dai: { pluginId: 'ethereum', tokenId: '6b175474e89094c44da98b954eedeac495271d0f' }, - dogecoin: { pluginId: 'dogecoin' }, - ethereum: { pluginId: 'ethereum' }, - litecoin: { pluginId: 'litecoin' }, - polygon: { pluginId: 'polygon' }, + dogecoin: { pluginId: 'dogecoin', tokenId: null }, + ethereum: { pluginId: 'ethereum', tokenId: null }, + litecoin: { pluginId: 'litecoin', tokenId: null }, + polygon: { pluginId: 'polygon', tokenId: null }, tether: { pluginId: 'ethereum', tokenId: 'dac17f958d2ee523a2206206994597c13d831ec7' }, usdc: { pluginId: 'ethereum', tokenId: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' } } diff --git a/src/util/fake/fakeBtcInfo.ts b/src/util/fake/fakeBtcInfo.ts index 5c4536cb5dd..4959123cc20 100644 --- a/src/util/fake/fakeBtcInfo.ts +++ b/src/util/fake/fakeBtcInfo.ts @@ -39,9 +39,5 @@ export const btcCurrencyInfo: EdgeCurrencyInfo = { // Explorers: blockExplorer: 'https://blockchair.com/bitcoin/block/%s', addressExplorer: 'https://blockchair.com/bitcoin/address/%s', - transactionExplorer: 'https://blockchair.com/bitcoin/transaction/%s', - - // Images: - symbolImage: ``, - symbolImageDarkMono: `` + transactionExplorer: 'https://blockchair.com/bitcoin/transaction/%s' } diff --git a/src/util/fake/fakeCurrencyPlugin.ts b/src/util/fake/fakeCurrencyPlugin.ts index 2030976e3fe..b490d672128 100644 --- a/src/util/fake/fakeCurrencyPlugin.ts +++ b/src/util/fake/fakeCurrencyPlugin.ts @@ -1,7 +1,6 @@ import { add, lt, mul } from 'biggystring' import { asArray, asNumber, asObject, asOptional, asString } from 'cleaners' import { - EdgeCurrencyCodeOptions, EdgeCurrencyEngine, EdgeCurrencyEngineCallbacks, EdgeCurrencyEngineOptions, @@ -15,6 +14,8 @@ import { EdgeSpendInfo, EdgeStakingStatus, EdgeToken, + EdgeTokenId, + EdgeTokenIdOptions, EdgeTransaction, EdgeWalletInfo, InsufficientFundsError, @@ -83,7 +84,7 @@ class FakeCurrencyEngine { progress: 0, txs: {} } - this.enabledTokens = currencyInfo.metaTokens.map(token => token.currencyCode) + this.enabledTokens = currencyInfo.metaTokens?.map(token => token.currencyCode) ?? [] this.enabledTokensMap = this.enabledTokens.reduce((prev, token) => { return { ...prev, [token]: true } }, {}) @@ -95,6 +96,17 @@ class FakeCurrencyEngine { }) } + private _getCurrencyCode(tokenId: EdgeTokenId): string { + for (const token of this.currencyInfo.metaTokens ?? []) { + const { currencyCode, contractAddress } = token + const tId = contractAddress?.toLowerCase().replace('0x', '') + if (tId === tokenId) { + return currencyCode + } + } + throw new Error('No matching tokenId') + } + private _updateState(settings: Partial<State>): void { const state = this.state const { @@ -120,7 +132,7 @@ class FakeCurrencyEngine { return { ...prev, [currencyCode]: nativeBalance } } - const metaToken = this.currencyInfo.metaTokens.find(token => token.currencyCode === currencyCode) + const metaToken = this.currencyInfo.metaTokens?.find(token => token.currencyCode === currencyCode) if (metaToken == null) throw new Error(`Invalid token ${currencyCode}`) const nativeBalance = mul(balance, metaToken.denominations[0].multiplier) return { ...prev, [currencyCode]: nativeBalance } @@ -204,8 +216,10 @@ class FakeCurrencyEngine { return this.state.blockHeight } - getBalance(opts: EdgeCurrencyCodeOptions): string { - const { currencyCode = this.currencyInfo.currencyCode } = opts + getBalance(opts: EdgeTokenIdOptions): string { + const { tokenId } = opts + const currencyCode = this._getCurrencyCode(tokenId) + const balance = this.state.balances[currencyCode] if (balance == null) { throw new Error('Unknown currency') @@ -214,7 +228,7 @@ class FakeCurrencyEngine { } } - getNumTransactions(opts: EdgeCurrencyCodeOptions): number { + getNumTransactions(opts: EdgeTokenIdOptions): number { return Object.keys(this.state.txs).length } @@ -245,20 +259,20 @@ class FakeCurrencyEngine { } // Addresses: - async getFreshAddress(opts: EdgeCurrencyCodeOptions): Promise<EdgeFreshAddress> { - return { publicAddress: this.currencyInfo.defaultSettings.publicAddress } + async getFreshAddress(opts: EdgeTokenIdOptions): Promise<EdgeFreshAddress> { + return { publicAddress: this.currencyInfo.defaultSettings?.publicAddress } } async addGapLimitAddresses(addresses: string[]): Promise<void> {} async isAddressUsed(address: string): Promise<boolean> { - return address === this.currencyInfo.defaultSettings.publicAddress + return address === this.currencyInfo.defaultSettings?.publicAddress } // Spending: async makeSpend(spendInfo: EdgeSpendInfo): Promise<EdgeTransaction> { - const { currencyCode = this.currencyInfo.currencyCode, spendTargets, tokenId = null } = spendInfo - const tokenSpend = currencyCode !== this.currencyInfo.currencyCode + const { tokenId, spendTargets } = spendInfo + const tokenSpend = tokenId != null // Check the spend targets: let total = '0' @@ -269,9 +283,10 @@ class FakeCurrencyEngine { } // Check the balances: - if (lt(this.getBalance({ currencyCode }), total)) { - return await Promise.reject(new InsufficientFundsError()) + if (lt(this.getBalance({ tokenId }), total)) { + return await Promise.reject(new InsufficientFundsError({ tokenId })) } + const currencyCode = this._getCurrencyCode(tokenId) // TODO: Return a high-fidelity transaction return { @@ -339,7 +354,7 @@ class FakeCurrencyTools { // URI parsing: async parseUri(uri: string): Promise<EdgeParsedUri> { - return await Promise.resolve({}) + return await Promise.resolve({ tokenId: null }) } async encodeUri(): Promise<string> { diff --git a/src/util/stakeUtils.ts b/src/util/stakeUtils.ts index 8aa62ddf251..171d22bbf81 100644 --- a/src/util/stakeUtils.ts +++ b/src/util/stakeUtils.ts @@ -65,7 +65,7 @@ export const getAllocationLocktimeMessage = (allocation: PositionAllocation) => * Returns the icon uris of stake and reward assets. */ export const getPolicyIconUris = ( - { metaTokens, pluginId }: EdgeCurrencyInfo, + { metaTokens = [], pluginId }: EdgeCurrencyInfo, stakePolicy: StakePolicy ): { stakeAssetUris: string[]; rewardAssetUris: string[] } => { const stakeAssetNames = getAssetCurrencyCodes(stakePolicy, 'stakeAssets') @@ -78,8 +78,8 @@ export const getPolicyIconUris = ( (rewardAssetName, i) => stakePolicy.rewardAssets[i].cdnName ?? metaTokens.find(metaToken => metaToken.currencyCode === rewardAssetName)?.contractAddress ) - const stakeAssetUris = stakeContractAddresses.map(stakeContractAddress => getCurrencyIconUris(pluginId, stakeContractAddress).symbolImage) - const rewardAssetUris = rewardContractAddresses.map(rewardContractAddress => getCurrencyIconUris(pluginId, rewardContractAddress).symbolImage) + const stakeAssetUris = stakeContractAddresses.map(stakeContractAddress => getCurrencyIconUris(pluginId, stakeContractAddress ?? null).symbolImage) + const rewardAssetUris = rewardContractAddresses.map(rewardContractAddress => getCurrencyIconUris(pluginId, rewardContractAddress ?? null).symbolImage) return { stakeAssetUris, rewardAssetUris } } diff --git a/src/util/tokenIdTools.ts b/src/util/tokenIdTools.ts index aefa3ff0616..730d2a5c99e 100644 --- a/src/util/tokenIdTools.ts +++ b/src/util/tokenIdTools.ts @@ -46,7 +46,7 @@ export function makeCurrencyCodeTable(currencyConfigMap: CurrencyConfigMap): (cu const currencyConfig = currencyConfigMap[pluginId] const { allTokens, currencyInfo } = currencyConfig - addMatch(currencyInfo.currencyCode, { pluginId }) + addMatch(currencyInfo.currencyCode, { pluginId, tokenId: null }) for (const tokenId of Object.keys(allTokens)) { const token = allTokens[tokenId] diff --git a/src/util/utils.ts b/src/util/utils.ts index a9deacc6b3c..b565f1a9289 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -28,7 +28,7 @@ import { convertCurrencyFromExchangeRates } from '../selectors/WalletSelectors' import { RootState } from '../types/reduxTypes' import { GuiExchangeRates, GuiFiatType } from '../types/types' import { getWalletFiat } from '../util/CurrencyWalletHelpers' -import { getTokenId } from './CurrencyInfoHelpers' +import { getCurrencyCode, getTokenId } from './CurrencyInfoHelpers' import { base58 } from './encoding' export const DECIMAL_PRECISION = 18 @@ -277,8 +277,9 @@ export const getTotalFiatAmountFromExchangeRates = (state: RootState, isoFiatCur for (const walletId of Object.keys(state.core.account.currencyWallets)) { const wallet = state.core.account.currencyWallets[walletId] log.push(`LogTot: pluginId:${wallet.currencyInfo.pluginId} wallet=${wallet.id.slice(0, 5)} isoFiat=${isoFiatCurrencyCode}`) - for (const currencyCode of Object.keys(wallet.balances)) { - const nativeBalance = wallet.balances[currencyCode] ?? '0' + for (const tokenId of wallet.balanceMap.keys()) { + const nativeBalance = wallet.balanceMap.get(tokenId) ?? '0' + const currencyCode = getCurrencyCode(wallet, tokenId) const rate = exchangeRates[`${currencyCode}_${isoFiatCurrencyCode}`] ?? '0' log.push(`\nLogTot: code=${currencyCode} rate=${rate} nb=${nativeBalance}`) diff --git a/yarn.lock b/yarn.lock index ca761ea45e2..660b05cf81c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8612,10 +8612,10 @@ ed25519@0.0.4: bindings "^1.2.1" nan "^2.0.9" -edge-core-js@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-1.14.0.tgz#16569cf6a24b50e26e0105eca554bd041ce15a01" - integrity sha512-P2yGTyu4eqoFUHFnrmkemxJrFFezql+cL2ZZ01JDyRJFgeAwy9zlEmg1Aj4IgOq8hzoNUFTd68fKTVPZZRDKEA== +edge-core-js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.0.0.tgz#264e87b1ae673b10c0529e7fb7b804364d79c765" + integrity sha512-lNVIElFgTL0gPAsEN6dS6xicrmFAvRMokkuGa3tnNUrDY4QbfuJotGlIAMdlYE5fLqbP5DzFAsSgU/nybfbtYQ== dependencies: aes-js "^3.1.0" base-x "^4.0.0" @@ -8719,10 +8719,10 @@ edge-currency-plugins@^2.5.1: wifgrs "^2.0.6" ws "^7.4.6" -edge-exchange-plugins@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/edge-exchange-plugins/-/edge-exchange-plugins-1.3.0.tgz#6fde352b0e837a4c0e762a911812594e11508b3a" - integrity sha512-DZbQ0A9vjD7PFaS7d+frStn3V1fKjmfRo/WDM3GksVoB1Lj4ZZArBibN9SPBGDZxTAU8U5axwlMSLEbUMzlSLA== +edge-exchange-plugins@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/edge-exchange-plugins/-/edge-exchange-plugins-2.0.0.tgz#e584342e9c9e54a5c3ce90a42e45606a2ac65beb" + integrity sha512-tPnevYDh0XsjeduuZ3Drjo9hK3hPxPj+efUG4kQJpLtzLdnChkR2g52OleIzFCxjR5pdAM17R1zqPVSbNYv6jw== dependencies: biggystring "^4.1.3" cleaners "^0.3.13"