diff --git a/e2e/utils/global.setup.ts b/e2e/utils/global.setup.ts index 131e7dc6..cfb379f2 100644 --- a/e2e/utils/global.setup.ts +++ b/e2e/utils/global.setup.ts @@ -10,8 +10,8 @@ import { // for user register and login setup('setup new wallet or login if already registered', async ({ page, extensionId }) => { // let playwright know this is going to be slow - // Wait up to 2 minutes to setup an account - setup.setTimeout(500_000); + // Wait up to 10 minutes to setup an account + setup.setTimeout(600_000); // Create a new page and navigate to extension // Navigate and wait for network to be idle diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index e9b1d65a..2eb35a50 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -11,7 +11,7 @@ import { getApp } from 'firebase/app'; import { getAuth } from 'firebase/auth/web-extension'; import type { TokenInfo } from 'flow-native-token-registry'; import { encode } from 'rlp'; -import web3, { TransactionError } from 'web3'; +import web3, { TransactionError, Web3 } from 'web3'; import { findAddressWithNetwork, @@ -27,10 +27,10 @@ import { import eventBus from '@/eventBus'; import { type FeatureFlagKey, type FeatureFlags } from '@/shared/types/feature-types'; import { type TrackingEvents } from '@/shared/types/tracking-types'; -import { type ActiveChildType, type LoggedInAccount } from '@/shared/types/wallet-types'; +import { type TransactionState } from '@/shared/types/transaction-types'; +import { type LoggedInAccount } from '@/shared/types/wallet-types'; import { ensureEvmAddressPrefix, isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; -import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; -import { retryOperation } from '@/shared/utils/retryOperation'; +import { getSignAlgo } from '@/shared/utils/algo'; import { keyringService, preferenceService, @@ -60,7 +60,7 @@ import emoji from 'background/utils/emoji.json'; import fetchConfig from 'background/utils/remoteConfig'; import { notification, storage } from 'background/webapi'; import { openIndexPage } from 'background/webapi/tab'; -import { INTERNAL_REQUEST_ORIGIN, EVENTS, KEYRING_TYPE } from 'consts'; +import { INTERNAL_REQUEST_ORIGIN, EVENTS, KEYRING_TYPE, EVM_ENDPOINT } from 'consts'; import type { BlockchainResponse, @@ -76,6 +76,7 @@ import type { PreferenceAccount } from '../service/preference'; import { type EvaluateStorageResult, StorageEvaluator } from '../service/storage-evaluator'; import type { UserInfoStore } from '../service/user'; import defaultConfig from '../utils/defaultConfig.json'; +import erc20ABI from '../utils/erc20.abi.json'; import { getLoggedInAccount } from '../utils/getLoggedInAccount'; import BaseController from './base'; @@ -308,16 +309,29 @@ export class WalletController extends BaseController { // lockadd here resetPwd = async () => { - const switchingTo = 'mainnet'; + // WARNING: This resets absolutely everything + // This is used when the user forgets their password + // It should only be called from the landing page when the user is logged out + // And the user should be redirected to the landing page + // After calling this function + + // TODO: I believe the user should be logged out here + // e.g. call signOutCurrentUser + + // This clears local storage but a lot is still kept in memory await storage.clear(); + // Note that this does not clear the 'booted' state + // We should fix this, but it would involve making changes to keyringService await keyringService.resetKeyRing(); await keyringService.setLocked(); + await passwordService.clear(); + sessionService.broadcastEvent('accountsChanged', []); sessionService.broadcastEvent('lock'); - openIndexPage('reset'); - await this.switchNetwork(switchingTo); + // Redirect to welcome so that users can import their account again + openIndexPage('/welcome'); }; // lockadd here @@ -775,27 +789,6 @@ export class WalletController extends BaseController { const keyring = await keyringService.getKeyringForAccount(from, type); const res = await keyringService.signTransaction(keyring, data, options); - /* - cadence_transaction_signed: { - cadence: string; // SHA256 Hashed Cadence that was signed. - tx_id: string; // String of the transaction ID. - authorizers: string[]; // Comma separated list of authorizer account address in the transaction - proposer: string; // Address of the transactions proposer. - payer: string; // Payer of the transaction. - success: boolean; // Boolean of if the transaction was sent successful or not. true/false - }; - evm_transaction_signed: { - success: boolean; // Boolean of if the transaction was sent successful or not. true/false - flow_address: string; // Address of the account that signed the transaction - evm_address: string; // EVM Address of the account that signed the transaction - tx_id: string; // transaction id - }; - mixpanelTrack.track('transaction_signed', { - address: from, - type, - ...res, - }); - */ return res; }; @@ -880,16 +873,7 @@ export class WalletController extends BaseController { updateAlianName = (address: string, name: string) => preferenceService.updateAlianName(address, name); getAllAlianName = () => preferenceService.getAllAlianName(); - // getInitAlianNameStatus = () => preferenceService.getInitAlianNameStatus(); - // updateInitAlianNameStatus = () => - // preferenceService.changeInitAlianNameStatus(); - // getLastTimeGasSelection = (chainId) => { - // return preferenceService.getLastTimeGasSelection(chainId); - // }; - // updateLastTimeGasSelection = (chainId: string, gas: ChainGas) => { - // return preferenceService.updateLastTimeGasSelection(chainId, gas); - // }; getIsFirstOpen = () => { return preferenceService.getIsFirstOpen(); }; @@ -899,14 +883,6 @@ export class WalletController extends BaseController { listChainAssets = async (address: string) => { return await openapiService.getCoinList(address); }; - // getAddedToken = (address: string) => { - // return preferenceService.getAddedToken(address); - // }; - // updateAddedToken = (address: string, tokenList: []) => { - // return preferenceService.updateAddedToken(address, tokenList); - // }; - - // lilico new service // userinfo getUserInfo = async (forceRefresh: boolean) => { @@ -1002,17 +978,7 @@ export class WalletController extends BaseController { checkAccessibleNft = async (childAccount) => { try { - // const res = await openapiService.checkChildAccount(address); - // const nfts = await openapiService.queryAccessible( - // '0x84221fe0294044d7', - // '0x16c41a2b76dee69b' - // ); const nfts = await openapiService.checkChildAccountNFT(childAccount); - // openapiService.checkChildAccountNFT(address).then((res) => { - // console.log(res) - // }).catch((err) => { - // console.log(err) - // }) return nfts; } catch (error) { @@ -1025,13 +991,7 @@ export class WalletController extends BaseController { const network = await this.getNetwork(); const address = await userWalletService.getMainWallet(network); - // const res = await openapiService.checkChildAccount(address); const result = await openapiService.queryAccessibleFt(address, childAccount); - // openapiService.checkChildAccountNFT(address).then((res) => { - // console.log(res) - // }).catch((err) => { - // console.log(err) - // }) return result; }; @@ -1233,7 +1193,7 @@ export class WalletController extends BaseController { const tokenId = `A.${token.address.slice(2)}.${token.contractName}`; return { coin: token.name, - unit: token.symbol, + unit: token.symbol.toLowerCase(), icon: token['logoURI'] || '', balance: parseFloat(parseFloat(allBalanceMap[tokenId]).toFixed(8)), price: allPrice[index] === null ? 0 : new BN(allPrice[index].price.last).toNumber(), @@ -1306,7 +1266,7 @@ export class WalletController extends BaseController { const tokenId = `A.${token.address.slice(2)}.${token.contractName}`; return { coin: token.name, - unit: token.symbol, + unit: token.symbol.toLowerCase(), icon: token['logoURI'] || '', balance: parseFloat(parseFloat(allBalanceMap[tokenId]).toFixed(8)), price: allPrice[index] === null ? 0 : new BN(allPrice[index].price.last).toNumber(), @@ -1343,29 +1303,6 @@ export class WalletController extends BaseController { } }; - fetchCoinList = async (_expiry = 5000, { signal } = { signal: new AbortController().signal }) => { - const network = await this.getNetwork(); - try { - await this.fetchBalance({ signal }); - - // const allTokens = await openapiService.getAllTokenInfo(); - // const enabledSymbols = tokenList.map((token) => token.symbol); - // const disableSymbols = allTokens.map((token) => token.symbol).filter((symbol) => !enabledSymbols.includes(symbol)); - // console.log('disableSymbols are these ', disableSymbols, enabledSymbols, coins) - // disableSymbols.forEach((coin) => coinListService.removeCoin(coin, network)); - - const coinListResult = coinListService.listCoins(network); - return coinListResult; - } catch (err) { - if (err.message === 'Operation aborted') { - console.log('refreshCoinList operation aborted.'); - } else { - console.error('fetch coinlist encountered an error:', err); - } - throw err; - } - }; - refreshEvmList = async (_expiry = 60000) => { const now = new Date(); const exp = _expiry + now.getTime(); @@ -1374,7 +1311,7 @@ export class WalletController extends BaseController { const network = await this.getNetwork(); const evmCustomToken = (await storage.get(`${network}evmCustomToken`)) || []; - const tokenList = await openapiService.getTokenListFromGithub(network); + const tokenList = await openapiService.getTokenList(network); const address = await this.getRawEvmAddressWithPrefix(); if (!isValidEthereumAddress(address)) { @@ -1443,7 +1380,7 @@ export class WalletController extends BaseController { const coins: CoinItem[] = mergedList.map((token, index) => { return { coin: token.name, - unit: token.symbol, + unit: token.symbol.toLowerCase(), icon: token['logoURI'] || placeholder, balance: token.balance, price: allPrice[index] === null ? 0 : new BN(allPrice[index].price.last).toNumber(), @@ -1495,32 +1432,6 @@ export class WalletController extends BaseController { } }; - fetchPreviewNetNft = async () => { - try { - // Check if the nftList is already in storage and not expired - const cachedNFTList = await storage.getExpiry('previewNetNftList'); - if (cachedNFTList) { - return cachedNFTList; - } else { - // Fetch the nftList from the API - - const network = await this.getNetwork(); - const response = await fetch( - `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/flow/nfts.json` - ); - const res = await response.json(); - console.log('nftList ', res); - const nftList = res.tokens; - // Cache the nftList with a one-hour expiry (3600000 milliseconds) - await storage.setExpiry('previewNetNftList', nftList, 3600000); - return nftList; - } - } catch (error) { - console.error('Error fetching NFT list:', error); - throw error; - } - }; - requestCadenceNft = async () => { const network = await this.getNetwork(); const address = await this.getCurrentAddress(); @@ -1843,6 +1754,132 @@ export class WalletController extends BaseController { }); }; + // Master send token function that takes a transaction state from the front end and returns the transaction ID + transferTokens = async (transactionState: TransactionState): Promise => { + const transferTokensOnCadence = async () => { + return this.transferCadenceTokens( + transactionState.selectedToken.symbol, + transactionState.toAddress, + transactionState.amount + ); + }; + + const transferTokensFromChildToCadence = async () => { + return this.sendFTfromChild( + transactionState.fromAddress, + transactionState.toAddress, + 'flowTokenProvider', + transactionState.amount, + transactionState.selectedToken.symbol + ); + }; + + const transferFlowFromEvmToCadence = async () => { + console.log('transferFlowFromEvmToCadence'); + return this.withdrawFlowEvm(transactionState.amount, transactionState.toAddress); + }; + + const transferFTFromEvmToCadence = async () => { + return this.transferFTFromEvm( + transactionState.selectedToken['flowIdentifier'], + transactionState.amount, + transactionState.toAddress, + transactionState.selectedToken + ); + }; + + // Returns the transaction ID + const transferTokensOnEvm = async () => { + // the amount is always stored as a string in the transaction state + const amountStr: string = transactionState.amount; + // TODO: check if the amount is a valid number + // Create an integer string based on the required token decimals + const amountBN = new BN(amountStr.replace('.', '')); + + const decimalsCount = amountStr.split('.')[1]?.length || 0; + const decimalDifference = transactionState.selectedToken.decimals - decimalsCount; + if (decimalDifference < 0) { + throw new Error('Too many decimal places have been provided'); + } + const scaleFactor = new BN(10).pow(decimalDifference); + const integerAmount = amountBN.multipliedBy(scaleFactor); + const integerAmountStr = integerAmount.integerValue(BN.ROUND_DOWN).toFixed(); + + let address, gas, value, data; + + if (transactionState.selectedToken.symbol.toLowerCase() === 'flow') { + address = transactionState.toAddress; + gas = '1'; + // const amountBN = new BN(transactionState.amount).multipliedBy(new BN(10).pow(18)); + // the amount is always stored as a string in the transaction state + value = integerAmount.toString(16); + data = '0x'; + } else { + // Get the current network + const network = await this.getNetwork(); + // Get the Web3 provider + const provider = new Web3.providers.HttpProvider(EVM_ENDPOINT[network]); + // Get the web3 instance + const web3Instance = new Web3(provider); + // Get the erc20 contract + const erc20Contract = new web3Instance.eth.Contract( + erc20ABI, + transactionState.selectedToken.address + ); + // Encode the data + const encodedData = erc20Contract.methods + .transfer(ensureEvmAddressPrefix(transactionState.toAddress), integerAmountStr) + .encodeABI(); + gas = '1312d00'; + address = ensureEvmAddressPrefix(transactionState.selectedToken.address); + value = '0x0'; // Zero value as hex + data = encodedData.startsWith('0x') ? encodedData : `0x${encodedData}`; + } + + // Send the transaction + return this.sendEvmTransaction(address, gas, value, data); + }; + + const transferFlowFromCadenceToEvm = async () => { + return this.transferFlowEvm(transactionState.toAddress, transactionState.amount); + }; + + const transferFTFromCadenceToEvm = async () => { + const address = transactionState.selectedToken!.address.startsWith('0x') + ? transactionState.selectedToken!.address.slice(2) + : transactionState.selectedToken!.address; + + return this.transferFTToEvmV2( + `A.${address}.${transactionState.selectedToken!.contractName}.Vault`, + transactionState.amount, + transactionState.toAddress + ); + }; + + // Switch on the current transaction state + switch (transactionState.currentTxState) { + case 'FTFromEvmToCadence': + return await transferFTFromEvmToCadence(); + case 'FlowFromEvmToCadence': + return await transferFlowFromEvmToCadence(); + case 'FTFromChildToCadence': + case 'FlowFromChildToCadence': + return await transferTokensFromChildToCadence(); + case 'FTFromCadenceToCadence': + case 'FlowFromCadenceToCadence': + return await transferTokensOnCadence(); + case 'FlowFromEvmToEvm': + case 'FTFromEvmToEvm': + return await transferTokensOnEvm(); + case 'FlowFromCadenceToEvm': + return await transferFlowFromCadenceToEvm(); + case 'FTFromCadenceToEvm': + return await transferFTFromCadenceToEvm(); + default: + throw new Error(`Unsupported transaction state: ${transactionState.currentTxState}`); + } + }; + transferFlowEvm = async ( recipientEVMAddressHex: string, amount = '1.0', @@ -2358,37 +2395,7 @@ export class WalletController extends BaseController { }; // TODO: Replace with generic token - transferTokens = async (symbol: string, address: string, amount: string): Promise => { - const token = await openapiService.getTokenInfo(symbol); - if (!token) { - throw new Error(`Invaild token name - ${symbol}`); - } - await this.getNetwork(); - const script = await getScripts('ft', 'transferTokensV3'); - - const txID = await userWalletService.sendTransaction( - script - .replaceAll('', token.contractName) - .replaceAll('', token.path.balance) - .replaceAll('', token.path.receiver) - .replaceAll('', token.path.vault) - .replaceAll('', token.address), - [fcl.arg(amount, t.UFix64), fcl.arg(address, t.Address)] - ); - - mixpanelTrack.track('ft_transfer', { - from_address: (await this.getCurrentAddress()) || '', - to_address: address, - amount: parseFloat(amount), - ft_identifier: token.contractName, - type: 'flow', - }); - - return txID; - }; - - // TODO: Replace with generic token - transferInboxTokens = async ( + transferCadenceTokens = async ( symbol: string, address: string, amount: string @@ -3212,7 +3219,6 @@ export class WalletController extends BaseController { console.log('No active tab found'); return; } - console.log('tabs', tabs); if (tabs[0].id) { chrome.tabs.sendMessage(tabs[0].id, { type: 'FCW:NETWORK', diff --git a/src/background/service/openapi.ts b/src/background/service/openapi.ts index 8cafdaf3..4f82643a 100644 --- a/src/background/service/openapi.ts +++ b/src/background/service/openapi.ts @@ -319,7 +319,7 @@ const dataConfig: Record = { params: ['transaction', 'message'], }, get_transfers: { - path: '/v1/account/transfers', + path: '/api/v1/account/transfers', method: 'get', params: ['address', 'after', 'limit'], }, @@ -368,6 +368,31 @@ const dataConfig: Record = { method: 'get', params: [], }, + get_ft_list: { + path: '/api/v3/fts', + method: 'get', + params: ['network', 'chain_type'], + }, + get_nft_list: { + path: '/api/v3/nfts', + method: 'get', + params: ['network', 'chain_type'], + }, +}; + +const defaultFlowToken = { + name: 'Flow', + address: '0x4445e7ad11568276', + contractName: 'FlowToken', + path: { + balance: '/public/flowTokenBalance', + receiver: '/public/flowTokenReceiver', + vault: '/storage/flowTokenVault', + }, + logoURI: + 'https://cdn.jsdelivr.net/gh/FlowFans/flow-token-list@main/token-registry/A.1654653399040a61.FlowToken/logo.svg', + decimals: 8, + symbol: 'flow', }; const recordFetch = async (response, responseData, ...args: Parameters) => { @@ -987,37 +1012,27 @@ class OpenApiService { return data; }; - getNFTList = async (address: string, offset: number, limit: number) => { - const config = this.store.config.nft_list; + getNFTList = async (network: string) => { + const childType = await userWalletService.getActiveWallet(); + let chainType = 'flow'; + if (childType === 'evm') { + chainType = 'evm'; + } + + const nftList = await storage.getExpiry(`NFTList${network}${chainType}`); + if (nftList && nftList.length > 0) { + return nftList; + } + const config = this.store.config.get_nft_list; const data = await this.sendRequest(config.method, config.path, { - address, - offset, - limit, + network, + chain_type: chainType, }); - return data; - }; - // getNFTListV2 = async (address: string, offset: number, limit: number) => { - // const alchemyAPI = (await storage.get('alchemyAPI')) || false; - // const config = alchemyAPI - // ? this.store.config.nft_list_v2 - // : this.store.config.nft_list_lilico_v2; - // const data = await this.sendRequest(config.method, config.path, { - // address, - // offset, - // limit, - // }); - // return data; - // }; + storage.setExpiry(`NFTList${network}${chainType}`, data, 600000); - // getNFTCollectionV2 = async (address: string) => { - // // const alchemyAPI = await storage.get('alchemyAPI') || false - // const config = this.store.config.nft_collections_lilico_v2; - // const data = await this.sendRequest(config.method, config.path, { - // address, - // }); - // return data; - // }; + return data; + }; getNFTMetadata = async ( address: string, @@ -1230,11 +1245,17 @@ class OpenApiService { getTransfers = async (address: string, after = '', limit: number) => { const config = this.store.config.get_transfers; - const data = await this.sendRequest(config.method, config.path, { - address, - after, - limit, - }); + const data = await this.sendRequest( + config.method, + config.path, + { + address, + after, + limit, + }, + {}, + WEB_NEXT_URL + ); return data; }; @@ -1321,7 +1342,7 @@ class OpenApiService { if (!network) { network = await userWalletService.getNetwork(); } - const tokens = await this.getTokenListFromGithub(network); + const tokens = await this.getTokenList(network); // const coins = await remoteFetch.flowCoins(); return tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); }; @@ -1331,7 +1352,7 @@ class OpenApiService { network = await userWalletService.getNetwork(); } - const tokens = await this.getEvmListFromGithub(network); + const tokens = await this.getEvmList(network); const tokenInfo = tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); @@ -1339,7 +1360,7 @@ class OpenApiService { return tokenInfo; } - const freshTokens = await this.refreshEvmGitToken(network); + const freshTokens = await this.refreshEvmToken(network); return freshTokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); }; @@ -1372,20 +1393,19 @@ class OpenApiService { return !!flags[featureFlag]; }; - // @ts-ignore - getAllTokenInfo = async (fiterNetwork = true): Promise => { + getAllTokenInfo = async (filterNetwork = true): Promise => { const network = await userWalletService.getNetwork(); - const list = await this.getTokenListFromGithub(network); - return fiterNetwork ? list.filter((item) => item.address) : list; + const list = await this.getTokenList(network); + return filterNetwork ? list.filter((item) => item.address) : list; }; - getAllNft = async (fiterNetwork = true): Promise => { + getAllNft = async (filterNetwork = true): Promise => { const list = await remoteFetch.nftCollection(); // const network = await userWalletService.getNetwork(); return list; }; - getAllNftV2 = async (fiterNetwork = true): Promise => { + getAllNftV2 = async (filterNetwork = true): Promise => { const list = await remoteFetch.nftv2Collection(); // const network = await userWalletService.getNetwork(); return list; @@ -1450,61 +1470,26 @@ class OpenApiService { return balance; }; - fetchGitTokenList = async (network, chainType, childType) => { - const isProduction = process.env.NODE_ENV === 'production'; - let url; - - if (isProduction) { - url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/default.json`; - } else if ( - !isProduction && - childType !== 'evm' && - (network === 'testnet' || network === 'mainnet') - ) { - url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/dev.json`; - } else { - url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/default.json`; - } + fetchFTList = async (network: string, chainType: string) => { + const config = this.store.config.get_ft_list; + const data = await this.sendRequest( + config.method, + config.path, + { + network, + chain_type: chainType, + }, + {}, + WEB_NEXT_URL + ); - const response = await fetch(url); - const { tokens = [] } = await response.json(); - const hasFlowToken = tokens.some((token) => token.symbol.toLowerCase() === 'flow'); - if (!hasFlowToken) { - tokens.push({ - name: 'Flow', - address: '0x4445e7ad11568276', - contractName: 'FlowToken', - path: { - balance: '/public/flowTokenBalance', - receiver: '/public/flowTokenReceiver', - vault: '/storage/flowTokenVault', - }, - logoURI: - 'https://cdn.jsdelivr.net/gh/FlowFans/flow-token-list@main/token-registry/A.1654653399040a61.FlowToken/logo.svg', - decimals: 8, - symbol: 'flow', - }); - } - return tokens; + return data.tokens; }; addFlowTokenIfMissing = (tokens) => { const hasFlowToken = tokens.some((token) => token.symbol.toLowerCase() === 'flow'); if (!hasFlowToken) { - tokens.push({ - name: 'Flow', - address: '0x4445e7ad11568276', - contractName: 'FlowToken', - path: { - balance: '/public/flowTokenBalance', - receiver: '/public/flowTokenReceiver', - vault: '/storage/flowTokenVault', - }, - logoURI: - 'https://cdn.jsdelivr.net/gh/FlowFans/flow-token-list@main/token-registry/A.1654653399040a61.FlowToken/logo.svg', - decimals: 8, - symbol: 'flow', - }); + tokens.push(defaultFlowToken); } }; @@ -1535,82 +1520,62 @@ class OpenApiService { }); }; - getTokenListFromGithub = async (network) => { + getTokenList = async (network) => { const childType = await userWalletService.getActiveWallet(); const chainType = childType === 'evm' ? 'evm' : 'flow'; - const gitToken = await storage.getExpiry(`GitTokenList${network}${chainType}`); - if (gitToken) return gitToken; + const ftList = await storage.getExpiry(`TokenList${network}${chainType}`); + if (ftList) return ftList; - const tokens = await this.fetchGitTokenList(network, chainType, childType); + const tokens = await this.fetchFTList(network, chainType); if (chainType === 'evm') { const evmCustomToken = (await storage.get(`${network}evmCustomToken`)) || []; this.mergeCustomTokens(tokens, evmCustomToken); } - storage.setExpiry(`GitTokenList${network}${chainType}`, tokens, 600000); + storage.setExpiry(`TokenList${network}${chainType}`, tokens, 600000); return tokens; }; - getEvmListFromGithub = async (network) => { + getEvmList = async (network) => { const chainType = 'evm'; - const gitToken = await storage.getExpiry(`GitTokenList${network}${chainType}`); - if (gitToken) return gitToken; + const ftList = await storage.getExpiry(`TokenList${network}${chainType}`); + if (ftList) return ftList; - const tokens = await this.fetchGitTokenList(network, chainType, chainType); + const tokens = await this.fetchFTList(network, chainType); if (chainType === 'evm') { const evmCustomToken = (await storage.get(`${network}evmCustomToken`)) || []; this.mergeCustomTokens(tokens, evmCustomToken); } - storage.setExpiry(`GitTokenList${network}${chainType}`, tokens, 600000); + storage.setExpiry(`TokenList${network}${chainType}`, tokens, 600000); return tokens; }; - refreshEvmGitToken = async (network) => { + refreshEvmToken = async (network) => { const chainType = 'evm'; - let gitToken = await storage.getExpiry(`GitTokenList${network}${chainType}`); - if (!gitToken) gitToken = await this.fetchGitTokenList(network, chainType, 'evm'); + let ftList = await storage.getExpiry(`TokenList${network}${chainType}`); + if (!ftList) ftList = await this.fetchFTList(network, chainType); const evmCustomToken = (await storage.get(`${network}evmCustomToken`)) || []; - this.mergeCustomTokens(gitToken, evmCustomToken); + this.mergeCustomTokens(ftList, evmCustomToken); - storage.setExpiry(`GitTokenList${network}${chainType}`, gitToken, 600000); + storage.setExpiry(`TokenList${network}${chainType}`, ftList, 600000); - return gitToken; + return ftList; }; - refreshCustomEvmGitToken = async (network) => { + refreshCustomEvmToken = async (network) => { const chainType = 'evm'; - const gitToken = await this.fetchGitTokenList(network, chainType, 'evm'); + const ftList = await this.fetchFTList(network, chainType); const evmCustomToken = (await storage.get(`${network}evmCustomToken`)) || []; - this.mergeCustomTokens(gitToken, evmCustomToken); - - storage.setExpiry(`GitTokenList${network}${chainType}`, gitToken, 600000); - }; + this.mergeCustomTokens(ftList, evmCustomToken); - getNFTListFromGithub = async (network: string) => { - const childType = await userWalletService.getActiveWallet(); - let chainType = 'flow'; - if (childType === 'evm') { - chainType = 'evm'; - } - const gitToken = await storage.getExpiry(`GitNFTList${network}${chainType}`); - if (gitToken && gitToken.length > 0) { - return gitToken; - } else { - const response = await fetch( - `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/flow/nfts.json` - ); - const res = await response.json(); - const { data = {} } = res; - storage.setExpiry(`GitNFTList${network}${chainType}`, data, 600000); - return data; - } + storage.setExpiry(`TokenList${network}${chainType}`, ftList, 600000); }; getEnabledTokenList = async (network = '') => { @@ -1620,7 +1585,7 @@ class OpenApiService { } const address = await userWalletService.getCurrentAddress(); - const tokenList = await this.getTokenListFromGithub(network); + const tokenList = await this.getTokenList(network); let values; const isChild = await userWalletService.getActiveWallet(); try { diff --git a/src/background/utils/remoteConfig.ts b/src/background/utils/remoteConfig.ts index 4fabd486..db5e19b6 100644 --- a/src/background/utils/remoteConfig.ts +++ b/src/background/utils/remoteConfig.ts @@ -61,7 +61,7 @@ class fetchRemoteConfig { } if (expire < now.getTime()) { try { - const result = await openapi.getNFTListFromGithub(network); + const result = await openapi.getNFTList(network); // fetch(`${baseURL}/fetchNFTList`); // const result = await coins.json(); this.nftState[network].result = result; diff --git a/src/constant/index.ts b/src/constant/index.ts index 7f0082c8..d9b618f0 100644 --- a/src/constant/index.ts +++ b/src/constant/index.ts @@ -141,7 +141,7 @@ export const INTERNAL_REQUEST_SESSION = { export const INITIAL_OPENAPI_URL = process.env.NODE_ENV === 'production' ? 'https://api.lilico.app' : 'https://dev.lilico.app'; export const WEB_NEXT_URL = - process.env.NODE_ENV === 'production' ? 'https://lilico.app' : 'https://test.lilico.app'; + process.env.NODE_ENV === 'production' ? 'https://lilico.app' : process.env.API_BASE_URL; // export const INITIAL_OPENAPI_URL = 'http://localhost:4747'; // export const INITIAL_OPENAPI_URL = process.env.INITIAL_OPENAPI_URL!; diff --git a/src/shared/test-data/api-test-results.ts b/src/shared/test-data/api-test-results.ts index f6961e4d..eee49875 100644 --- a/src/shared/test-data/api-test-results.ts +++ b/src/shared/test-data/api-test-results.ts @@ -5389,12 +5389,11 @@ export const createTestResults = (): ApiTestResults => ({ ], fetch: [ { - functionName: 'fetchGitTokenList', + functionName: 'fetchFTList', functionGroup: 'fetch', functionParams: { network: 'mainnet', chainType: 'flow', - childType: '', }, functionResponse: [ { @@ -6414,8 +6413,17 @@ export const createTestResults = (): ApiTestResults => ({ ], fetchDetails: [ { - url: 'https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/mainnet/flow/dev.json', + url: `${WEB_NEXT_URL}/api/v3/fts?network=mainnet&chain_type=flow`, params: {}, + requestInit: { + method: 'get', + headers: { + Network: 'testnet', + Accept: 'application/json', + Authorization: 'Bearer mock-token', + 'Content-Type': 'application/json', + }, + }, responseData: { name: 'Flow Token List', network: 'mainnet', @@ -7507,7 +7515,7 @@ export const createTestResults = (): ApiTestResults => ({ timestamp: 1736979553163, }, { - functionName: 'getNFTListFromGithub', + functionName: 'getNFTList', functionGroup: 'fetch', functionParams: { network: 'testnet', @@ -7622,8 +7630,17 @@ export const createTestResults = (): ApiTestResults => ({ ], fetchDetails: [ { - url: 'https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/testnet/flow/nfts.json', + url: `${INITIAL_OPENAPI_URL}/api/v3/nfts?network=testnet&chain_type=flow`, params: {}, + requestInit: { + method: 'get', + headers: { + Network: 'testnet', + Accept: 'application/json', + Authorization: 'Bearer mock-token', + 'Content-Type': 'application/json', + }, + }, responseData: { data: [ { @@ -8057,7 +8074,7 @@ export const createTestResults = (): ApiTestResults => ({ }, fetchDetails: [ { - url: `${INITIAL_OPENAPI_URL}/v1/account/transfers?address=0x2cdc100430551409&after=&limit=10`, + url: `${WEB_NEXT_URL}/api/v1/account/transfers?address=0x2cdc100430551409&after=&limit=10`, params: { address: '0x2cdc100430551409', after: '', diff --git a/src/shared/test-data/test-groups.ts b/src/shared/test-data/test-groups.ts index 73ee8af6..f59a3fe5 100644 --- a/src/shared/test-data/test-groups.ts +++ b/src/shared/test-data/test-groups.ts @@ -135,10 +135,10 @@ export const createTestGroups = (commonParams: CommonParams): ApiTestGroups => { ], fetch: [ { - name: 'fetchGitTokenList', - params: { network: commonParams.network, chainType: 'flow', childType: '' }, + name: 'fetchFTList', + params: { network: commonParams.network, chainType: 'flow' }, }, - { name: 'getNFTListFromGithub', params: { network: commonParams.network } }, + { name: 'getNFTList', params: { network: commonParams.network } }, ], approval: [ { diff --git a/src/ui/reducers/__tests__/transaction-reducer.test.ts b/src/ui/reducers/__tests__/transaction-reducer.test.ts new file mode 100644 index 00000000..44cee0c9 --- /dev/null +++ b/src/ui/reducers/__tests__/transaction-reducer.test.ts @@ -0,0 +1,383 @@ +import { type TokenInfo } from 'flow-native-token-registry'; +import { describe, expect, it } from 'vitest'; + +import { type Contact } from '@/shared/types/network-types'; +import { + type NetworkType, + type TokenType, + type TransactionStateString, +} from '@/shared/types/transaction-types'; +import { type CoinItem, type WalletAddress } from '@/shared/types/wallet-types'; + +import { + INITIAL_TRANSACTION_STATE, + getTransactionStateString, + transactionReducer, +} from '../transaction-reducer'; + +describe('Transaction Reducer', () => { + describe('Initial State', () => { + it('should have the correct initial state', () => { + expect(INITIAL_TRANSACTION_STATE).toMatchObject({ + currentTxState: '', + rootAddress: '', + fromAddress: '', + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Evm' as NetworkType, + toAddress: '', + amount: '0.0', + fiatAmount: '0.0', + fiatCurrency: 'USD', + fiatOrCoin: 'coin', + balanceExceeded: false, + }); + }); + }); + + describe('getTransactionStateString', () => { + it('should return empty string when required fields are missing', () => { + const state = { ...INITIAL_TRANSACTION_STATE, tokenType: '' as TokenType }; + expect(getTransactionStateString(state)).toBe(''); + }); + + it('should return correct transaction state string', () => { + const state = { + ...INITIAL_TRANSACTION_STATE, + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }; + expect(getTransactionStateString(state)).toBe('FlowFromEvmToCadence'); + }); + }); + + describe('Action Handlers', () => { + describe('initTransactionState', () => { + it('should initialize transaction state with EVM address', () => { + const action = { + type: 'initTransactionState' as const, + payload: { + rootAddress: '0x123' as WalletAddress, + fromAddress: '0x1234567890123456789012345678901234567890' as WalletAddress, + }, + }; + + const newState = transactionReducer(INITIAL_TRANSACTION_STATE, action); + expect(newState.rootAddress).toBe('0x123'); + expect(newState.fromAddress).toBe('0x1234567890123456789012345678901234567890'); + expect(newState.fromNetwork).toBe('Evm'); + }); + + it('should initialize transaction state with Cadence address', () => { + const rootAddress = '0x123abc' as WalletAddress; + const action = { + type: 'initTransactionState' as const, + payload: { + rootAddress, + fromAddress: rootAddress, + }, + }; + + const newState = transactionReducer(INITIAL_TRANSACTION_STATE, action); + expect(newState.fromNetwork).toBe('Cadence'); + }); + }); + + describe('setSelectedToken', () => { + const mockTokenInfo: TokenInfo = { + name: 'Test Token', + address: '0x123', + contractName: 'TestToken', + path: { + balance: '/public/testBalance', + receiver: '/public/testReceiver', + vault: '/storage/testVault', + }, + logoURI: 'test.svg', + decimals: 8, + symbol: 'TEST', + }; + + const mockCoinInfo: CoinItem = { + coin: 'test', + unit: 'TEST', + balance: 100, + price: 1, + change24h: 0, + total: 100, + icon: 'test.svg', + }; + + it('should set token info and update token type for non-Flow token', () => { + const action = { + type: 'setSelectedToken' as const, + payload: { + tokenInfo: mockTokenInfo, + coinInfo: mockCoinInfo, + }, + }; + + const newState = transactionReducer(INITIAL_TRANSACTION_STATE, action); + expect(newState.selectedToken).toEqual(mockTokenInfo); + expect(newState.tokenType).toBe('FT'); + expect(newState.coinInfo).toEqual(mockCoinInfo); + }); + + it('should adjust amount decimals when switching to token with different decimals', () => { + // First set an amount with the initial Flow token (8 decimals) + const stateWithAmount = transactionReducer(INITIAL_TRANSACTION_STATE, { + type: 'setAmount', + payload: '123.456789012345', + }); + + // Then switch to a token with 6 decimals + const token6Decimals: TokenInfo = { + ...mockTokenInfo, + decimals: 6, + }; + + const newState = transactionReducer(stateWithAmount, { + type: 'setSelectedToken', + payload: { + tokenInfo: token6Decimals, + coinInfo: mockCoinInfo, + }, + }); + + // Should truncate to 6 decimals + expect(newState.amount).toBe('123.456789'); + }); + + it('should handle switching to token with more decimals', () => { + // First set state with a 2 decimal token + const token2Decimals: TokenInfo = { + ...mockTokenInfo, + decimals: 2, + }; + + const stateWith2Decimals = transactionReducer(INITIAL_TRANSACTION_STATE, { + type: 'setSelectedToken', + payload: { + tokenInfo: token2Decimals, + coinInfo: mockCoinInfo, + }, + }); + + // Set an amount + const stateWithAmount = transactionReducer(stateWith2Decimals, { + type: 'setAmount', + payload: '123.456', + }); + + // Should be truncated to 2 decimals + expect(stateWithAmount.amount).toBe('123.45'); + + // Switch to 8 decimal token + const token8Decimals: TokenInfo = { + ...mockTokenInfo, + decimals: 8, + }; + + const finalState = transactionReducer(stateWithAmount, { + type: 'setSelectedToken', + payload: { + tokenInfo: token8Decimals, + coinInfo: mockCoinInfo, + }, + }); + + // Should maintain 2 decimals since that was the input + expect(finalState.amount).toBe('123.45'); + }); + }); + + describe('setToAddress', () => { + it('should set to address and determine correct network for EVM address', () => { + const action = { + type: 'setToAddress' as const, + payload: { + address: '0x1234567890123456789012345678901234567890' as WalletAddress, + }, + }; + + const newState = transactionReducer(INITIAL_TRANSACTION_STATE, action); + expect(newState.toAddress).toBe('0x1234567890123456789012345678901234567890'); + expect(newState.toNetwork).toBe('Evm'); + }); + + it('should set to address and determine correct network for Cadence address', () => { + const action = { + type: 'setToAddress' as const, + payload: { + address: '0x1234.5678' as WalletAddress, + }, + }; + + const newState = transactionReducer(INITIAL_TRANSACTION_STATE, action); + expect(newState.toAddress).toBe('0x1234.5678'); + expect(newState.toNetwork).toBe('Cadence'); + }); + }); + + describe('setAmount', () => { + const stateWithBalance = { + ...INITIAL_TRANSACTION_STATE, + coinInfo: { + ...INITIAL_TRANSACTION_STATE.coinInfo, + balance: 100, + price: 2, + }, + selectedToken: { + ...INITIAL_TRANSACTION_STATE.selectedToken, + decimals: 8, + }, + }; + + it('should handle coin amount input', () => { + const action = { + type: 'setAmount' as const, + payload: '50', + }; + + const newState = transactionReducer(stateWithBalance, action); + expect(newState.amount).toBe('50'); + expect(newState.fiatAmount).toBe('100.000'); + expect(newState.balanceExceeded).toBe(false); + }); + + it('should detect balance exceeded', () => { + const action = { + type: 'setAmount' as const, + payload: '150', + }; + + const newState = transactionReducer(stateWithBalance, action); + expect(newState.balanceExceeded).toBe(true); + }); + + it('should truncate decimals based on token decimals', () => { + // Create state with a 4 decimal token + const token4Decimals: TokenInfo = { + ...INITIAL_TRANSACTION_STATE.selectedToken, + decimals: 4, + }; + + const stateWith4Decimals = { + ...stateWithBalance, + selectedToken: token4Decimals, + tokenType: 'FT' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }; + + const action = { + type: 'setAmount' as const, + payload: '123.456789', + }; + + const newState = transactionReducer(stateWith4Decimals, action); + expect(newState.amount).toBe('123.4567'); + }); + + it('should handle amounts with no decimals', () => { + const action = { + type: 'setAmount' as const, + payload: '100', + }; + + const newState = transactionReducer( + { + ...stateWithBalance, + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }, + action + ); + expect(newState.amount).toBe('100'); + }); + + it('should preserve trailing zeros for precision', () => { + const action = { + type: 'setAmount' as const, + payload: '100.100000', + }; + + const newState = transactionReducer( + { + ...stateWithBalance, + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }, + action + ); + // Should preserve trailing zeros for precision in crypto transactions + expect(newState.amount).toBe('100.100000'); + }); + }); + + describe('setAmountToMax', () => { + const stateWithBalance = { + ...INITIAL_TRANSACTION_STATE, + coinInfo: { + ...INITIAL_TRANSACTION_STATE.coinInfo, + balance: 100, + price: 2, + }, + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }; + + it('should set maximum amount in coin mode', () => { + const action = { + type: 'setAmountToMax' as const, + }; + + const newState = transactionReducer(stateWithBalance, action); + expect(newState.amount).toBe('100'); + expect(newState.fiatAmount).toBe('200.000'); + }); + + it('should set maximum amount in fiat mode', () => { + const action = { + type: 'setAmountToMax' as const, + }; + + const stateInFiat = { + ...stateWithBalance, + fiatOrCoin: 'fiat' as const, + }; + + const newState = transactionReducer(stateInFiat, action); + expect(newState.amount).toBe('100'); + expect(newState.fiatAmount).toBe('200.000'); + expect(newState.fiatOrCoin).toBe('fiat'); + }); + }); + + describe('switchFiatOrCoin', () => { + it('should switch between fiat and coin modes', () => { + const action = { + type: 'switchFiatOrCoin' as const, + }; + + const state1 = transactionReducer( + { + ...INITIAL_TRANSACTION_STATE, + tokenType: 'Flow' as TokenType, + fromNetwork: 'Evm' as NetworkType, + toNetwork: 'Cadence' as NetworkType, + }, + action + ); + expect(state1.fiatOrCoin).toBe('fiat'); + + const state2 = transactionReducer(state1, action); + expect(state2.fiatOrCoin).toBe('coin'); + }); + }); + }); +}); diff --git a/src/ui/reducers/transaction-reducer.ts b/src/ui/reducers/transaction-reducer.ts index ed881a1f..8e425947 100644 --- a/src/ui/reducers/transaction-reducer.ts +++ b/src/ui/reducers/transaction-reducer.ts @@ -17,7 +17,7 @@ export const INITIAL_TRANSACTION_STATE: TransactionState = { currentTxState: '', rootAddress: '', fromAddress: '', - tokenType: 'FT', + tokenType: 'Flow', fromNetwork: 'Evm', toNetwork: 'Evm', toAddress: '', diff --git a/src/ui/utils/WalletContext.tsx b/src/ui/utils/WalletContext.tsx index 063df5cb..f9eab099 100644 --- a/src/ui/utils/WalletContext.tsx +++ b/src/ui/utils/WalletContext.tsx @@ -86,17 +86,6 @@ const WalletProvider = ({ ); }; -/** - * @deprecated The method should not be used - */ -const useWalletOld = () => { - const { wallet } = useContext(WalletContext) as { - wallet: WalletController; - }; - - return wallet; -}; - const useWallet = () => { const { wallet } = useContext(WalletContext) as unknown as { wallet: WalletControllerType; @@ -113,4 +102,4 @@ const useWalletLoaded = () => { return loaded; }; -export { WalletProvider, useWalletOld, useWallet, useWalletLoaded }; +export { WalletProvider, useWallet, useWalletLoaded }; diff --git a/src/ui/views/Approval/components/EthApproval/EthApprovalComponents/IconWithPlaceholder.tsx b/src/ui/views/Approval/components/EthApproval/EthApprovalComponents/IconWithPlaceholder.tsx new file mode 100644 index 00000000..ab59cd94 --- /dev/null +++ b/src/ui/views/Approval/components/EthApproval/EthApprovalComponents/IconWithPlaceholder.tsx @@ -0,0 +1,39 @@ +import { CardMedia } from '@mui/material'; +import React from 'react'; + +import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; + +interface IconWithPlaceholderProps { + imageUrl: string; + width?: string | number; + height?: string | number; + borderRadius?: string | number; + backgroundColor?: string; +} + +const IconWithPlaceholder: React.FC = ({ + imageUrl, + width = '60px', + height = '60px', + borderRadius = '12px', + backgroundColor = 'text.secondary', +}) => { + return ( + { + e.currentTarget.onerror = null; + e.currentTarget.src = `https://www.google.com/s2/favicons?sz=256&domain_url=${imageUrl}`; + }} + /> + ); +}; + +export default IconWithPlaceholder; diff --git a/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx b/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx index 66522cf8..81bcdfb3 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx @@ -7,11 +7,12 @@ import { Accordion, AccordionSummary, AccordionDetails, + CardMedia, } from '@mui/material'; import React from 'react'; import Highlight from 'react-highlight'; -import placeholder from 'ui/FRWAssets/image/placeholder.png'; +import IconWithPlaceholder from '../EthApprovalComponents/IconWithPlaceholder'; export const DefaultBlock = ({ title, host, data, logo }) => { const hexToString = (hex) => { @@ -38,15 +39,7 @@ export const DefaultBlock = ({ title, host, data, logo }) => { return ( - + {title} diff --git a/src/ui/views/Approval/components/EthApproval/EthConfirm/TransactionBlock.tsx b/src/ui/views/Approval/components/EthApproval/EthConfirm/TransactionBlock.tsx index 6cb7552b..fc35ec5b 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConfirm/TransactionBlock.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConfirm/TransactionBlock.tsx @@ -9,7 +9,7 @@ import { CopyButton } from 'ui/FRWComponent'; import { formatAddress, HexToDecimalConverter } from 'ui/utils'; import IconFlow from '../../../../../../components/iconfont/IconFlow'; - +import IconWithPlaceholder from '../EthApprovalComponents/IconWithPlaceholder'; const convertToFlow = (value) => { return Number(HexToDecimalConverter(value)) / 1_000_000_000_000_000_000; }; @@ -18,15 +18,7 @@ export const TransactionBlock = ({ title, data, logo, lilicoEnabled, decodedCall return ( - + Sign Transaction from diff --git a/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx b/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx index f63722c0..be63be78 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx @@ -11,6 +11,7 @@ import { LLPrimaryButton, LLSecondaryButton, LLSpinner, LLConnectLoading } from import { useApproval, useWallet, formatAddress } from 'ui/utils'; import CheckCircleIcon from '../../../../../../components/iconfont/IconCheckmark'; +import IconWithPlaceholder from '../EthApprovalComponents/IconWithPlaceholder'; // import EthMove from '../EthMove'; @@ -153,16 +154,7 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { {isEvm && ( - + { > {params.session && ( - - + + Sign Type Message from diff --git a/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx index fb20ee1c..e86e4395 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx @@ -10,6 +10,8 @@ import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; import { useApproval, useWallet } from 'ui/utils'; +import IconWithPlaceholder from '../EthApprovalComponents/IconWithPlaceholder'; + interface ConnectProps { params: any; } @@ -177,11 +179,8 @@ const EthSignV1 = ({ params }: ConnectProps) => { > {params.session && ( - - + + Sign Type Message from diff --git a/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx index c5d58e1f..c46607dc 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx @@ -117,7 +117,7 @@ const EthSuggest = (data) => { } await storage.set(`${network}evmCustomToken`, evmCustomToken); - await usewallet.openapi.refreshEvmGitToken(network); + await usewallet.openapi.refreshEvmToken(network); setLoading(false); }; diff --git a/src/ui/views/MoveBoard/MoveCollectionSelect.tsx b/src/ui/views/MoveBoard/MoveCollectionSelect.tsx index 80f6e5fe..60ad56fa 100644 --- a/src/ui/views/MoveBoard/MoveCollectionSelect.tsx +++ b/src/ui/views/MoveBoard/MoveCollectionSelect.tsx @@ -38,9 +38,10 @@ const MoveCollectionSelect = ({ setFilter(event.target.value); }; - const filteredCollectionList = collectionList.filter((obj) => - obj.CollectionName.toLowerCase().includes(filter.toLowerCase()) - ); + const filteredCollectionList = + collectionList?.filter((obj) => + obj.CollectionName.toLowerCase().includes(filter.toLowerCase()) + ) || []; return ( - {collectionList.length > 0 && ( + {collectionList?.length > 0 ? ( ))} + ) : ( + + + No NFT Collections Found + + )} diff --git a/src/ui/views/SendTo/TransferConfirmation.tsx b/src/ui/views/SendTo/TransferConfirmation.tsx index 1f0b3933..052e0e94 100644 --- a/src/ui/views/SendTo/TransferConfirmation.tsx +++ b/src/ui/views/SendTo/TransferConfirmation.tsx @@ -1,20 +1,15 @@ import CloseIcon from '@mui/icons-material/Close'; import InfoIcon from '@mui/icons-material/Info'; import { Box, Typography, Drawer, Stack, Grid, CardMedia, IconButton, Button } from '@mui/material'; -import BN from 'bignumber.js'; import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { type Contract } from 'web3'; import { type TransactionState } from '@/shared/types/transaction-types'; -import { ensureEvmAddressPrefix, isValidEthereumAddress } from '@/shared/utils/address'; import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import StorageExceededAlert from '@/ui/FRWComponent/StorageExceededAlert'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useContactHook } from '@/ui/hooks/useContactHook'; -import { useWeb3 } from '@/ui/hooks/useWeb3'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; -import erc20ABI from 'background/utils/erc20.abi.json'; import IconNext from 'ui/FRWAssets/svg/next.svg'; import { LLSpinner } from 'ui/FRWComponent'; import { Profile } from 'ui/FRWComponent/Send/Profile'; @@ -47,23 +42,6 @@ const TransferConfirmation = ({ const [tid, setTid] = useState(''); const [count, setCount] = useState(0); - const web3Instance = useWeb3(); - const [erc20Contract, setErc20Contract] = useState | null>(null); - - useEffect(() => { - if ( - isConfirmationOpen && - web3Instance && - isValidEthereumAddress(transactionState.selectedToken?.address) - ) { - const contractInstance = new web3Instance.eth.Contract( - erc20ABI, - transactionState.selectedToken.address - ); - setErc20Contract(contractInstance); - } - }, [web3Instance, transactionState.selectedToken.address, isConfirmationOpen]); - const transferAmount = transactionState.amount ? parseFloat(transactionState.amount) : undefined; // Check if the transfer is between EVM and Flow networks @@ -117,129 +95,14 @@ const TransferConfirmation = ({ setOccupied(false); }, []); - const transferTokensOnCadence = useCallback(async () => { - return wallet.transferInboxTokens( - transactionState.selectedToken.symbol, - transactionState.toAddress, - transactionState.amount - ); - }, [transactionState, wallet]); - - const transferTokensFromChildToCadence = useCallback(async () => { - return wallet.sendFTfromChild( - transactionState.fromAddress, - transactionState.toAddress, - 'flowTokenProvider', - transactionState.amount, - transactionState.selectedToken.symbol - ); - }, [transactionState, wallet]); - - const transferFlowFromEvmToCadence = useCallback(async () => { - return wallet.withdrawFlowEvm(transactionState.amount, transactionState.toAddress); - }, [wallet, transactionState]); - - const transferFTFromEvmToCadence = useCallback(async () => { - return wallet.transferFTFromEvm( - transactionState.selectedToken['flowIdentifier'], - transactionState.amount, - transactionState.toAddress, - transactionState.selectedToken - ); - }, [wallet, transactionState]); - - const transferTokensOnEvm = useCallback(async () => { - // the amount is always stored as a string in the transaction state - const amountStr: string = transactionState.amount; - // TODO: check if the amount is a valid number - // Create an integer string based on the required token decimals - const amountBN = new BN(amountStr.replace('.', '')); - - const decimalsCount = amountStr.split('.')[1]?.length || 0; - const decimalDifference = transactionState.selectedToken.decimals - decimalsCount; - if (decimalDifference < 0) { - throw new Error('Too many decimal places have been provided'); - } - const scaleFactor = new BN(10).pow(decimalDifference); - const integerAmount = amountBN.multipliedBy(scaleFactor); - const integerAmountStr = integerAmount.integerValue(BN.ROUND_DOWN).toFixed(); - - let address, gas, value, data; - - if (transactionState.selectedToken.symbol.toLowerCase() === 'flow') { - address = transactionState.toAddress; - gas = '1'; - // const amountBN = new BN(transactionState.amount).multipliedBy(new BN(10).pow(18)); - // the amount is always stored as a string in the transaction state - value = integerAmount.toString(16); - data = '0x'; - } else { - const encodedData = erc20Contract!.methods - .transfer(ensureEvmAddressPrefix(transactionState.toAddress), integerAmountStr) - .encodeABI(); - gas = '1312d00'; - address = ensureEvmAddressPrefix(transactionState.selectedToken.address); - value = '0x0'; // Zero value as hex - data = encodedData.startsWith('0x') ? encodedData : `0x${encodedData}`; - } - - // Send the transaction - return wallet.sendEvmTransaction(address, gas, value, data); - }, [transactionState, erc20Contract, wallet]); - - const transferFlowFromCadenceToEvm = useCallback(async () => { - return wallet.transferFlowEvm(transactionState.toAddress, transactionState.amount); - }, [transactionState, wallet]); - - const transferFTFromCadenceToEvm = useCallback(async () => { - const address = transactionState.selectedToken!.address.startsWith('0x') - ? transactionState.selectedToken!.address.slice(2) - : transactionState.selectedToken!.address; - - return wallet.transferFTToEvmV2( - `A.${address}.${transactionState.selectedToken!.contractName}.Vault`, - transactionState.amount, - transactionState.toAddress - ); - }, [transactionState, wallet]); - const transferTokens = useCallback(async () => { try { // Set the sending state to true setSending(true); // Initialize the transaction ID - let txId: string; + const txId: string = await wallet.transferTokens(transactionState); - // Switch on the current transaction state - switch (transactionState.currentTxState) { - case 'FTFromEvmToCadence': - txId = await transferFTFromEvmToCadence(); - break; - case 'FlowFromEvmToCadence': - txId = await transferFlowFromEvmToCadence(); - break; - case 'FTFromChildToCadence': - case 'FlowFromChildToCadence': - txId = await transferTokensFromChildToCadence(); - break; - case 'FTFromCadenceToCadence': - case 'FlowFromCadenceToCadence': - txId = await transferTokensOnCadence(); - break; - case 'FlowFromEvmToEvm': - case 'FTFromEvmToEvm': - txId = await transferTokensOnEvm(); - break; - case 'FlowFromCadenceToEvm': - txId = await transferFlowFromCadenceToEvm(); - break; - case 'FTFromCadenceToEvm': - txId = await transferFTFromCadenceToEvm(); - break; - default: - throw new Error(`Unsupported transaction state: ${transactionState.currentTxState}`); - } // Set the transaction ID so we can show that we're processing setTid(txId); @@ -267,18 +130,7 @@ const TransferConfirmation = ({ // Set the sending state to false regardless of whether the transaction was successful or not setSending(false); } - }, [ - transactionState, - wallet, - history, - transferFTFromEvmToCadence, - transferFlowFromEvmToCadence, - transferTokensFromChildToCadence, - transferTokensOnCadence, - transferTokensOnEvm, - transferFlowFromCadenceToEvm, - transferFTFromCadenceToEvm, - ]); + }, [transactionState, wallet, history]); const transactionDoneHandler = useCallback( (request) => { diff --git a/src/ui/views/TokenDetail/index.tsx b/src/ui/views/TokenDetail/index.tsx index 4410d923..898092d8 100644 --- a/src/ui/views/TokenDetail/index.tsx +++ b/src/ui/views/TokenDetail/index.tsx @@ -72,7 +72,7 @@ const TokenDetail = () => { await storage.set(`${network}evmCustomToken`, evmCustomToken); await usewallet.clearCoinList(); - await usewallet.openapi.refreshCustomEvmGitToken(network); + await usewallet.openapi.refreshCustomEvmToken(network); history.replace({ pathname: history.location.pathname, state: { refreshed: true } }); history.goBack(); }; diff --git a/src/ui/views/Wallet/AddCustom/AddCustomEvmToken.tsx b/src/ui/views/Wallet/AddCustom/AddCustomEvmToken.tsx index 8a0a75d2..d0833d58 100644 --- a/src/ui/views/Wallet/AddCustom/AddCustomEvmToken.tsx +++ b/src/ui/views/Wallet/AddCustom/AddCustomEvmToken.tsx @@ -144,7 +144,7 @@ const AddCustomEvmToken = () => { } await storage.set(`${network}evmCustomToken`, evmCustomToken); - await usewallet.openapi.refreshEvmGitToken(network); + await usewallet.openapi.refreshEvmToken(network); setLoading(false); history.replace({ pathname: history.location.pathname, state: { refreshed: true } }); history.goBack(); diff --git a/src/ui/views/api-test/openapi-methods.json b/src/ui/views/api-test/openapi-methods.json index 126bf5f2..370c0209 100644 --- a/src/ui/views/api-test/openapi-methods.json +++ b/src/ui/views/api-test/openapi-methods.json @@ -664,7 +664,7 @@ { "name": "getTokenInfo", "isAsync": true, - "fullBody": "async (name, network = '') => {\n // FIX ME: Get defaultTokenList from firebase remote config\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const tokens = await this.getTokenListFromGithub(network);\n // const coins = await remoteFetch.flowCoins();\n return tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n }", + "fullBody": "async (name, network = '') => {\n // FIX ME: Get defaultTokenList from firebase remote config\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const tokens = await this.getTokenList(network);\n // const coins = await remoteFetch.flowCoins();\n return tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n }", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (name, network = '') =>", @@ -673,7 +673,7 @@ { "name": "getEvmTokenInfo", "isAsync": true, - "fullBody": "async (name, network = '') => {\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const tokens = await this.getEvmListFromGithub(network);\n const tokenInfo = tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n if (tokenInfo && (0,_shared_utils_address__WEBPACK_IMPORTED_MODULE_7__.isValidEthereumAddress)(tokenInfo.address)) {\n return tokenInfo;\n }\n const freshTokens = await this.refreshEvmGitToken(network);\n return freshTokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n }", + "fullBody": "async (name, network = '') => {\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const tokens = await this.getEvmList(network);\n const tokenInfo = tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n if (tokenInfo && (0,_shared_utils_address__WEBPACK_IMPORTED_MODULE_7__.isValidEthereumAddress)(tokenInfo.address)) {\n return tokenInfo;\n }\n const freshTokens = await this.refreshEvmToken(network);\n return freshTokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase());\n }", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (name, network = '') =>", @@ -736,7 +736,7 @@ { "name": "getAllTokenInfo", "isAsync": true, - "fullBody": "async (fiterNetwork = true) => {\n const network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n const list = await this.getTokenListFromGithub(network);\n return fiterNetwork ? list.filter((item) => item.address) : list;\n }", + "fullBody": "async (fiterNetwork = true) => {\n const network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n const list = await this.getTokenList(network);\n return fiterNetwork ? list.filter((item) => item.address) : list;\n }", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (fiterNetwork = true) =>", @@ -806,13 +806,13 @@ "params": ["address", "token"] }, { - "name": "fetchGitTokenList", + "name": "fetchFTList", "isAsync": true, - "fullBody": "async (network, chainType, childType) => {\n const isProduction = \"development\" === 'production';\n let url;\n if (isProduction) {\n url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/default.json`;\n }\n else if (!isProduction &&\n childType !== 'evm' &&\n (network === 'testnet' || network === 'mainnet')) {\n url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/dev.json`;\n }\n else {\n url = `https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/${chainType}/default.json`;\n }\n const response = await fetch(url);\n const { tokens = [] } = await response.json();\n const hasFlowToken = tokens.some((token) => token.symbol.toLowerCase() === 'flow');\n if (!hasFlowToken) {\n tokens.push({\n name: 'Flow',\n address: '0x4445e7ad11568276',\n contractName: 'FlowToken',\n path: {\n balance: '/public/flowTokenBalance',\n receiver: '/public/flowTokenReceiver',\n vault: '/storage/flowTokenVault',\n },\n logoURI: 'https://cdn.jsdelivr.net/gh/FlowFans/flow-token-list@main/token-registry/A.1654653399040a61.FlowToken/logo.svg',\n decimals: 8,\n symbol: 'flow',\n });\n }\n return tokens;\n }", + "fullBody": "async (network, chainType) => {\n const config = this.store.config.get_ft_list;\n const data = await this.sendRequest(config.method, config.path, {\n network,\n chain_type: chainType,\n }, {}, consts__WEBPACK_IMPORTED_MODULE_13__.WEB_NEXT_URL);\n return data.tokens;\n}", "usesSendRequest": false, "usesFetchDirectly": true, - "basicSignature": "async (network, chainType, childType) =>", - "params": ["network", "chainType", "childType"] + "basicSignature": "async (network, chainType) =>", + "params": ["network", "chainType"] }, { "name": "addFlowTokenIfMissing", @@ -833,54 +833,45 @@ "params": ["tokens", "customTokens"] }, { - "name": "getTokenListFromGithub", + "name": "getTokenList", "isAsync": true, - "fullBody": "async (network) => {\n const childType = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getActiveWallet();\n const chainType = childType === 'evm' ? 'evm' : 'flow';\n const gitToken = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`GitTokenList${network}${chainType}`);\n if (gitToken)\n return gitToken;\n const tokens = await this.fetchGitTokenList(network, chainType, childType);\n if (chainType === 'evm') {\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(tokens, evmCustomToken);\n }\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`GitTokenList${network}${chainType}`, tokens, 600000);\n return tokens;\n }", + "fullBody": "async (network) => {\n const childType = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getActiveWallet();\n const chainType = childType === 'evm' ? 'evm' : 'flow';\n const ftList = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`TokenList${network}${chainType}`);\n if (ftList)\n return ftList;\n const tokens = await this.fetchFTList(network, chainType);\n if (chainType === 'evm') {\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(tokens, evmCustomToken);\n }\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`TokenList${network}${chainType}`, tokens, 600000);\n return tokens;\n}", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (network) =>", "params": ["network"] }, { - "name": "getEvmListFromGithub", + "name": "getEvmList", "isAsync": true, - "fullBody": "async (network) => {\n const chainType = 'evm';\n const gitToken = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`GitTokenList${network}${chainType}`);\n if (gitToken)\n return gitToken;\n const tokens = await this.fetchGitTokenList(network, chainType, chainType);\n if (chainType === 'evm') {\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(tokens, evmCustomToken);\n }\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`GitTokenList${network}${chainType}`, tokens, 600000);\n return tokens;\n }", + "fullBody": "async (network) => {\n const chainType = 'evm';\n const ftList = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`TokenList${network}${chainType}`);\n if (ftList)\n return ftList;\n const tokens = await this.fetchFTList(network, chainType);\n if (chainType === 'evm') {\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(tokens, evmCustomToken);\n }\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`TokenList${network}${chainType}`, tokens, 600000);\n return tokens;\n}", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (network) =>", "params": ["network"] }, { - "name": "refreshEvmGitToken", + "name": "refreshEvmToken", "isAsync": true, - "fullBody": "async (network) => {\n const chainType = 'evm';\n let gitToken = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`GitTokenList${network}${chainType}`);\n if (!gitToken)\n gitToken = await this.fetchGitTokenList(network, chainType, 'evm');\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(gitToken, evmCustomToken);\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`GitTokenList${network}${chainType}`, gitToken, 600000);\n return gitToken;\n }", + "fullBody": "async (network) => {\n const chainType = 'evm';\n let ftList = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`TokenList${network}${chainType}`);\n if (!ftList)\n ftList = await this.fetchFTList(network, chainType);\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(ftList, evmCustomToken);\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`TokenList${network}${chainType}`, ftList, 600000);\n return ftList;\n}", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (network) =>", "params": ["network"] }, { - "name": "refreshCustomEvmGitToken", + "name": "refreshCustomEvmToken", "isAsync": true, - "fullBody": "async (network) => {\n const chainType = 'evm';\n const gitToken = await this.fetchGitTokenList(network, chainType, 'evm');\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(gitToken, evmCustomToken);\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`GitTokenList${network}${chainType}`, gitToken, 600000);\n }", + "fullBody": "async (network) => {\n const chainType = 'evm';\n const ftList = await this.fetchFTList(network, chainType);\n const evmCustomToken = (await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.get(`${network}evmCustomToken`)) || [];\n this.mergeCustomTokens(ftList, evmCustomToken);\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`TokenList${network}${chainType}`, ftList, 600000);\n}", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (network) =>", "params": ["network"] }, - { - "name": "getNFTListFromGithub", - "isAsync": true, - "fullBody": "async (network) => {\n const childType = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getActiveWallet();\n let chainType = 'flow';\n if (childType === 'evm') {\n chainType = 'evm';\n }\n const gitToken = await _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.getExpiry(`GitNFTList${network}${chainType}`);\n if (gitToken && gitToken.length > 0) {\n return gitToken;\n }\n else {\n const response = await fetch(`https://raw.githubusercontent.com/Outblock/token-list-jsons/outblock/jsons/${network}/flow/nfts.json`);\n const res = await response.json();\n const { data = {} } = res;\n _background_webapi__WEBPACK_IMPORTED_MODULE_6__.storage.setExpiry(`GitNFTList${network}${chainType}`, data, 600000);\n return data;\n }\n }", - "usesSendRequest": false, - "usesFetchDirectly": true, - "basicSignature": "async (network) =>", - "params": ["network"] - }, { "name": "getEnabledTokenList", "isAsync": true, - "fullBody": "async (network = '') => {\n // const tokenList = await remoteFetch.flowCoins();\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const address = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getCurrentAddress();\n const tokenList = await this.getTokenListFromGithub(network);\n let values;\n const isChild = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getActiveWallet();\n try {\n if (isChild && isChild !== 'evm') {\n values = await this.isLinkedAccountTokenListEnabled(address);\n }\n else if (!isChild) {\n values = await this.isTokenListEnabled(address);\n }\n }\n catch (error) {\n console.error('Error isTokenListEnabled token:');\n values = {};\n }\n const tokenItems = [];\n const tokenMap = {};\n if (isChild !== 'evm') {\n tokenList.forEach((token) => {\n const tokenId = `A.${token.address.slice(2)}.${token.contractName}`;\n // console.log(tokenMap,'tokenMap',values)\n if (!!values[tokenId]) {\n tokenMap[token.name] = token;\n }\n });\n }\n // const data = values.map((value, index) => ({isEnabled: value, token: tokenList[index]}))\n // return values\n // .map((value, index) => {\n // if (value) {\n // return tokens[index];\n // }\n // })\n // .filter((item) => item);\n Object.keys(tokenMap).map((key, idx) => {\n const item = tokenMap[key];\n tokenItems.push(item);\n });\n return tokenItems;\n }", + "fullBody": "async (network = '') => {\n // const tokenList = await remoteFetch.flowCoins();\n if (!network) {\n network = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getNetwork();\n }\n const address = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getCurrentAddress();\n const tokenList = await this.getTokenList(network);\n let values;\n const isChild = await _index__WEBPACK_IMPORTED_MODULE_15__.userWalletService.getActiveWallet();\n try {\n if (isChild && isChild !== 'evm') {\n values = await this.isLinkedAccountTokenListEnabled(address);\n }\n else if (!isChild) {\n values = await this.isTokenListEnabled(address);\n }\n }\n catch (error) {\n console.error('Error isTokenListEnabled token:');\n values = {};\n }\n const tokenItems = [];\n const tokenMap = {};\n if (isChild !== 'evm') {\n tokenList.forEach((token) => {\n const tokenId = `A.${token.address.slice(2)}.${token.contractName}`;\n // console.log(tokenMap,'tokenMap',values)\n if (!!values[tokenId]) {\n tokenMap[token.name] = token;\n }\n });\n }\n // const data = values.map((value, index) => ({isEnabled: value, token: tokenList[index]}))\n // return values\n // .map((value, index) => {\n // if (value) {\n // return tokens[index];\n // }\n // })\n // .filter((item) => item);\n Object.keys(tokenMap).map((key, idx) => {\n const item = tokenMap[key];\n tokenItems.push(item);\n });\n return tokenItems;\n }", "usesSendRequest": false, "usesFetchDirectly": false, "basicSignature": "async (network = '') =>",