diff --git a/.changeset/chatty-llamas-worry.md b/.changeset/chatty-llamas-worry.md new file mode 100644 index 000000000..f68fb8229 --- /dev/null +++ b/.changeset/chatty-llamas-worry.md @@ -0,0 +1,6 @@ +--- +"@fuel-wallet/types": minor +"fuels-wallet": minor +--- + +added SRC20 custom assets name, symbol and decimal resolve from indexer diff --git a/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx b/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx index 09254b34c..7956c3c25 100644 --- a/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx +++ b/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx @@ -35,7 +35,6 @@ export const BalanceAssets = ({ function toggle() { setShowUnknown((s) => !s); } - return ( {balancesToShow.map((balance) => ( diff --git a/packages/app/src/systems/Account/services/account.ts b/packages/app/src/systems/Account/services/account.ts index 0e99a099a..3c2005bb0 100644 --- a/packages/app/src/systems/Account/services/account.ts +++ b/packages/app/src/systems/Account/services/account.ts @@ -6,6 +6,7 @@ import type { CoinAsset, } from '@fuel-wallet/types'; import { Address, type Provider, bn } from 'fuels'; +import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; import { AssetService } from '~/systems/Asset/services'; import { getFuelAssetByAssetId } from '~/systems/Asset/utils'; import type { Maybe } from '~/systems/Core/types'; @@ -109,16 +110,34 @@ export class AccountService { const nextBalancesWithAssets = await balances.reduce( async (acc, balance) => { const prev = await acc; - const asset = await getFuelAssetByAssetId({ - assets, - assetId: balance.assetId, - }); + const asset = { + fuel: await getFuelAssetByAssetId({ + assets, + assetId: balance.assetId, + }), + }; + try { + const assetCached = await AssetsCache.getInstance().getAsset({ + chainId: provider.getChainId(), + assetId: balance.assetId, + provider, + }); + + if (assetCached && asset.fuel) { + asset.fuel = { + ...asset.fuel, + ...assetCached, + indexed: true, + }; + } + } catch (_) {} + return [ ...prev, { ...balance, amount: balance.amount, - asset, + asset: asset.fuel, }, ]; }, diff --git a/packages/app/src/systems/Asset/cache/AssetsCache.ts b/packages/app/src/systems/Asset/cache/AssetsCache.ts new file mode 100644 index 000000000..5a63fab3a --- /dev/null +++ b/packages/app/src/systems/Asset/cache/AssetsCache.ts @@ -0,0 +1,119 @@ +import type { AssetData } from '@fuel-wallet/types'; +import type { Asset, Provider } from 'fuels'; +import { db } from '~/systems/Core/utils/database'; +import { isNft } from '../utils/isNft'; + +type Endpoint = { + chainId: number; + url: string; +}; + +export class AssetsCache { + private cache: { [chainId: number]: { [assetId: string]: Asset } }; + private static instance: AssetsCache; + private endpoints: Endpoint[] = [ + { + chainId: 9889, + url: 'https://explorer-indexer-mainnet.fuel.network', + }, + { + chainId: 0, + url: 'https://explorer-indexer-testnet.fuel.network', + }, + ]; + private storage: IndexedAssetsDB; + + private constructor() { + this.cache = {}; + this.storage = new IndexedAssetsDB(); + } + + private getIndexerEndpoint(chainId: number) { + return this.endpoints.find( + (endpoint: Endpoint) => endpoint.chainId === chainId + ); + } + + private async fetchAssetFromIndexer(url: string, assetId: string) { + try { + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('Timeout')), 2000) + ); + // limit on 2000ms request time + const response = await Promise.race([ + fetch(`${url}/assets/${assetId}`), + timeout, + ]); + if (response instanceof Response) { + return response.json(); + } + } catch (_e: unknown) {} + } + + async getAsset({ + chainId, + assetId, + provider, + }: { chainId: number; assetId: string; provider: Provider }) { + if (chainId == null || !assetId) { + return; + } + const endpoint = this.getIndexerEndpoint(chainId); + if (!endpoint) return; + // try to get from memory cache first + this.cache[endpoint.chainId] = this.cache[endpoint.chainId] || {}; + if (this.cache[endpoint.chainId][assetId]) { + return this.cache[endpoint.chainId][assetId]; + } + // get from indexed db if not in memory + const savedAsset = await this.storage.getItem( + `${endpoint.chainId}/${assetId}` + ); + if (savedAsset) { + this.cache[endpoint.chainId][assetId] = savedAsset; + return savedAsset; + } + const assetFromIndexer = await this.fetchAssetFromIndexer( + endpoint.url, + assetId + ); + if (!assetFromIndexer) return; + + const isNftAsset = await isNft({ + assetId, + contractId: assetFromIndexer.contractId, + provider, + }); + + const asset = { + ...assetFromIndexer, + isNft: isNftAsset, + }; + + this.cache[endpoint.chainId][assetId] = asset; + this.storage.setItem(`${endpoint.chainId}/${assetId}`, asset); + return asset; + } + + static getInstance() { + if (!AssetsCache.instance) { + AssetsCache.instance = new AssetsCache(); + } + return AssetsCache.instance; + } +} + +class IndexedAssetsDB { + async getItem(key: string) { + return db.transaction('r', db.indexedAssets, async () => { + const asset = await db.indexedAssets.get({ key }); + return asset; + }); + } + + async setItem(key: string, data: AssetData) { + await db.transaction('rw', db.indexedAssets, async () => { + await db.indexedAssets.put({ key, ...data }); + }); + } +} diff --git a/packages/app/src/systems/Asset/components/AssetItem/AssetItem.tsx b/packages/app/src/systems/Asset/components/AssetItem/AssetItem.tsx index d710049d4..b5021e8be 100644 --- a/packages/app/src/systems/Asset/components/AssetItem/AssetItem.tsx +++ b/packages/app/src/systems/Asset/components/AssetItem/AssetItem.tsx @@ -1,6 +1,7 @@ import { cssObj } from '@fuel-ui/css'; import { Avatar, + Badge, Box, Button, CardList, @@ -126,7 +127,9 @@ export const AssetItem: AssetItemComponent = ({ delayDuration={0} open={visibility && tooltip ? undefined : false} > - + + This asset is flagged as suspicious, +
it may mimicking another asset. +
Proceed with caution. + + ); + return ( {icon ? ( @@ -161,20 +177,35 @@ export const AssetItem: AssetItemComponent = ({ )} - {name || ( - - Unknown + + {name || 'Unknown'} + {asset.suspicious ? ( + + + + ) : ( + '' + )} + {asset.isNft && ( + + NFT + + )} + {(!name || asset.indexed) && !asset.isNft && ( - - )} + )} + {symbol ? ( @@ -207,6 +238,11 @@ const styles = { textSize: 'sm', fontWeight: '$normal', }), + assetSuspicious: cssObj({ + marginLeft: 5, + marginRight: 5, + color: 'orange', + }), addAssetBtn: cssObj({ p: '0', ml: '$1', @@ -229,4 +265,9 @@ const styles = { color: '$intentsBase11 !important', }, }), + assetNft: cssObj({ + ml: '$2', + fontSize: '$sm', + lineHeight: 'normal', + }), }; diff --git a/packages/app/src/systems/Asset/components/AssetSelect/AssetSelect.tsx b/packages/app/src/systems/Asset/components/AssetSelect/AssetSelect.tsx index 754ab0014..eb8412b9d 100644 --- a/packages/app/src/systems/Asset/components/AssetSelect/AssetSelect.tsx +++ b/packages/app/src/systems/Asset/components/AssetSelect/AssetSelect.tsx @@ -1,6 +1,7 @@ import { cssObj, cx } from '@fuel-ui/css'; import { Avatar, + Badge, Box, Button, Dropdown, @@ -107,7 +108,7 @@ function AssetSelectBase({ items, selected, onSelect }: AssetSelectProps) { {(items || []).map((item) => { const assetId = item.assetId?.toString(); const itemAsset = items?.find((a) => a.assetId === assetId); - const { name, symbol, icon } = itemAsset || {}; + const { name, symbol, icon, isNft } = itemAsset || {}; return ( {name || 'Unknown'} + {isNft && ( + + NFT + + )} {symbol || shortAddress(assetId)} @@ -230,4 +240,9 @@ const styles = { dropdownRoot: cssObj({ zIndex: '1 !important', }), + assetNft: cssObj({ + ml: '$2', + fontSize: '$sm', + lineHeight: 'normal', + }), }; diff --git a/packages/app/src/systems/Asset/pages/UpsertAsset/UpsertAsset.tsx b/packages/app/src/systems/Asset/pages/UpsertAsset/UpsertAsset.tsx index a250083a9..af3a3fe60 100644 --- a/packages/app/src/systems/Asset/pages/UpsertAsset/UpsertAsset.tsx +++ b/packages/app/src/systems/Asset/pages/UpsertAsset/UpsertAsset.tsx @@ -1,7 +1,7 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Button, Focus, Text } from '@fuel-ui/react'; import { isB256 } from 'fuels'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { Layout } from '~/systems/Core'; import { AssetItem } from '../../components'; @@ -13,6 +13,7 @@ import useFuelAsset from '../../hooks/useFuelAsset'; export function UpsertAsset() { const navigate = useNavigate(); + const { state } = useLocation(); const params = useParams<{ name: string; assetId: string }>(); const name = params.name; @@ -22,11 +23,11 @@ export function UpsertAsset() { const { handlers, isLoading } = useAssets(); const form = useAssetForm({ defaultValues: { - name: '', - symbol: '', + name: state?.name || '', + symbol: state?.symbol || '', icon: '', ...asset, - decimals: fuelAsset?.decimals || 0, + decimals: state?.decimals || fuelAsset?.decimals || 0, assetId: fuelAsset?.assetId || params.assetId || '', }, }); diff --git a/packages/app/src/systems/Asset/services/assets.ts b/packages/app/src/systems/Asset/services/assets.ts index 1e49e2853..7d8c8ebe7 100644 --- a/packages/app/src/systems/Asset/services/assets.ts +++ b/packages/app/src/systems/Asset/services/assets.ts @@ -227,6 +227,12 @@ export class AssetService { }); } + static async getAssetById(assetId: string) { + return db.transaction('r', db.assets, async () => { + return db.assets.get({ assetId }); + }); + } + static async clearAssets() { return db.transaction('rw', db.assets, async () => { return db.assets.clear(); diff --git a/packages/app/src/systems/Asset/utils/isNft.ts b/packages/app/src/systems/Asset/utils/isNft.ts new file mode 100644 index 000000000..4d8c91cff --- /dev/null +++ b/packages/app/src/systems/Asset/utils/isNft.ts @@ -0,0 +1,650 @@ +import { Contract, type Provider } from 'fuels'; + +export const isNft = async ({ + assetId, + contractId, + provider, +}: { assetId: string; contractId: string; provider: Provider }) => { + const contract = new Contract(contractId, SRC_20_ABI, provider); + + const result = await contract + .multiCall([ + contract.functions.total_supply({ bits: assetId }), + contract.functions.decimals({ bits: assetId }), + ]) + .dryRun(); + + const [total_supply, decimals] = result.value; + + /* + according to sway standards this is how you recognize an NFT: + https://docs.fuel.network/docs/sway-standards/src-20-native-asset/#non-fungible-asset-restrictions + */ + return total_supply.toNumber() === 1 && !decimals; +}; + +const SRC_20_ABI = { + programType: 'contract', + specVersion: '1', + encodingVersion: '1', + concreteTypes: [ + { + type: '()', + concreteTypeId: + '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + type: 'b256', + concreteTypeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + type: 'enum std::identity::Identity', + concreteTypeId: + 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + metadataTypeId: 0, + }, + { + type: 'enum std::option::Option', + concreteTypeId: + '7c06d929390a9aeeb8ffccf8173ac0d101a9976d99dda01cce74541a81e75ac0', + metadataTypeId: 1, + typeArguments: [ + '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + ], + }, + { + type: 'enum std::option::Option', + concreteTypeId: + 'd852149004cc9ec0bbe7dc4e37bffea1d41469b759512b6136f2e865a4c06e7d', + metadataTypeId: 1, + typeArguments: [ + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + ], + }, + { + type: 'enum std::option::Option', + concreteTypeId: + '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + metadataTypeId: 1, + typeArguments: [ + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + ], + }, + { + type: 'str', + concreteTypeId: + '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + }, + { + type: 'str[11]', + concreteTypeId: + '48e8455800b58e79d9db5ac584872b19d307a74a81dcad1d1f9ca34da17e1b31', + }, + { + type: 'str[6]', + concreteTypeId: + 'ed705f920eb2c423c81df912430030def10f03218f0a064bfab81b68de71ae21', + }, + { + type: 'struct std::asset_id::AssetId', + concreteTypeId: + 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + metadataTypeId: 5, + }, + { + type: 'struct std::string::String', + concreteTypeId: + '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + metadataTypeId: 9, + }, + { + type: 'u64', + concreteTypeId: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + type: 'u8', + concreteTypeId: + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + metadataTypes: [ + { + type: 'enum std::identity::Identity', + metadataTypeId: 0, + components: [ + { + name: 'Address', + typeId: 4, + }, + { + name: 'ContractId', + typeId: 8, + }, + ], + }, + { + type: 'enum std::option::Option', + metadataTypeId: 1, + components: [ + { + name: 'None', + typeId: + '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'Some', + typeId: 2, + }, + ], + typeParameters: [2], + }, + { + type: 'generic T', + metadataTypeId: 2, + }, + { + type: 'raw untyped ptr', + metadataTypeId: 3, + }, + { + type: 'struct std::address::Address', + metadataTypeId: 4, + components: [ + { + name: 'bits', + typeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::asset_id::AssetId', + metadataTypeId: 5, + components: [ + { + name: 'bits', + typeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::bytes::Bytes', + metadataTypeId: 6, + components: [ + { + name: 'buf', + typeId: 7, + }, + { + name: 'len', + typeId: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'struct std::bytes::RawBytes', + metadataTypeId: 7, + components: [ + { + name: 'ptr', + typeId: 3, + }, + { + name: 'cap', + typeId: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'struct std::contract_id::ContractId', + metadataTypeId: 8, + components: [ + { + name: 'bits', + typeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::string::String', + metadataTypeId: 9, + components: [ + { + name: 'bytes', + typeId: 6, + }, + ], + }, + ], + functions: [ + { + inputs: [ + { + name: 'sub_id', + concreteTypeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + name: 'amount', + concreteTypeId: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + name: 'burn', + output: + '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'doc-comment', + arguments: [ + ' Unconditionally burns assets sent with the default SubId.', + ], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Arguments'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' * `sub_id`: [SubId] - The default SubId.'], + }, + { + name: 'doc-comment', + arguments: [' * `amount`: [u64] - The quantity of coins to burn.'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Number of Storage Accesses'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' * Reads: `1`'], + }, + { + name: 'doc-comment', + arguments: [' * Writes: `1`'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Reverts'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' * When the `sub_id` is not the default SubId.'], + }, + { + name: 'doc-comment', + arguments: [ + ' * When the transaction did not include at least `amount` coins.', + ], + }, + { + name: 'doc-comment', + arguments: [ + ' * When the transaction did not include the asset minted by this contract.', + ], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Examples'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' ```sway'], + }, + { + name: 'doc-comment', + arguments: [' use src3::SRC3;'], + }, + { + name: 'doc-comment', + arguments: [' use std::constants::DEFAULT_SUB_ID;'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' fn foo(contract_id: ContractId, asset_id: AssetId) {'], + }, + { + name: 'doc-comment', + arguments: [' let contract_abi = abi(SRC3, contract_id);'], + }, + { + name: 'doc-comment', + arguments: [' contract_abi {'], + }, + { + name: 'doc-comment', + arguments: [' gas: 10000,'], + }, + { + name: 'doc-comment', + arguments: [' coins: 100,'], + }, + { + name: 'doc-comment', + arguments: [' asset_id: asset_id,'], + }, + { + name: 'doc-comment', + arguments: [' }.burn(DEFAULT_SUB_ID, 100);'], + }, + { + name: 'doc-comment', + arguments: [' }'], + }, + { + name: 'doc-comment', + arguments: [' ```'], + }, + { + name: 'payable', + arguments: [], + }, + { + name: 'storage', + arguments: ['read', 'write'], + }, + ], + }, + { + inputs: [ + { + name: 'recipient', + concreteTypeId: + 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + }, + { + name: 'sub_id', + concreteTypeId: + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + name: 'amount', + concreteTypeId: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + name: 'mint', + output: + '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'doc-comment', + arguments: [ + ' Unconditionally mints new assets using the default SubId.', + ], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Arguments'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [ + ' * `recipient`: [Identity] - The user to which the newly minted asset is transferred to.', + ], + }, + { + name: 'doc-comment', + arguments: [' * `sub_id`: [SubId] - The default SubId.'], + }, + { + name: 'doc-comment', + arguments: [' * `amount`: [u64] - The quantity of coins to mint.'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Number of Storage Accesses'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' * Reads: `1`'], + }, + { + name: 'doc-comment', + arguments: [' * Writes: `1`'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Reverts'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' * When the `sub_id` is not the default SubId.'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' # Examples'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' ```sway'], + }, + { + name: 'doc-comment', + arguments: [' use src3::SRC3;'], + }, + { + name: 'doc-comment', + arguments: [' use std::constants::DEFAULT_SUB_ID;'], + }, + { + name: 'doc-comment', + arguments: [''], + }, + { + name: 'doc-comment', + arguments: [' fn foo(contract_id: ContractId) {'], + }, + { + name: 'doc-comment', + arguments: [' let contract_abi = abi(SRC3, contract);'], + }, + { + name: 'doc-comment', + arguments: [ + ' contract_abi.mint(Identity::ContractId(contract_id), DEFAULT_SUB_ID, 100);', + ], + }, + { + name: 'doc-comment', + arguments: [' }'], + }, + { + name: 'doc-comment', + arguments: [' ```'], + }, + { + name: 'storage', + arguments: ['read', 'write'], + }, + ], + }, + { + inputs: [ + { + name: 'asset', + concreteTypeId: + 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + ], + name: 'decimals', + output: + '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + { + inputs: [ + { + name: 'asset', + concreteTypeId: + 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + ], + name: 'name', + output: + '7c06d929390a9aeeb8ffccf8173ac0d101a9976d99dda01cce74541a81e75ac0', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + { + inputs: [ + { + name: 'asset', + concreteTypeId: + 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + ], + name: 'symbol', + output: + '7c06d929390a9aeeb8ffccf8173ac0d101a9976d99dda01cce74541a81e75ac0', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + { + inputs: [], + name: 'total_assets', + output: + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + { + inputs: [ + { + name: 'asset', + concreteTypeId: + 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + ], + name: 'total_supply', + output: + 'd852149004cc9ec0bbe7dc4e37bffea1d41469b759512b6136f2e865a4c06e7d', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + ], + loggedTypes: [ + { + logId: '10098701174489624218', + concreteTypeId: + '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + }, + ], + messagesTypes: [], + configurables: [ + { + name: 'DECIMALS', + concreteTypeId: + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + offset: 15736, + }, + { + name: 'NAME', + concreteTypeId: + '48e8455800b58e79d9db5ac584872b19d307a74a81dcad1d1f9ca34da17e1b31', + offset: 15744, + }, + { + name: 'SYMBOL', + concreteTypeId: + 'ed705f920eb2c423c81df912430030def10f03218f0a064bfab81b68de71ae21', + offset: 15760, + }, + ], +}; diff --git a/packages/app/src/systems/Core/utils/database.ts b/packages/app/src/systems/Core/utils/database.ts index 089dfa8ff..848a8d399 100644 --- a/packages/app/src/systems/Core/utils/database.ts +++ b/packages/app/src/systems/Core/utils/database.ts @@ -24,6 +24,7 @@ export class FuelDB extends Dexie { connections!: Table; transactionsCursors!: Table; assets!: Table; + indexedAssets!: Table; abis!: Table; errors!: Table; integrityCheckInterval?: NodeJS.Timeout; @@ -91,6 +92,7 @@ export class FuelDB extends Dexie { this.connections.clear(), this.transactionsCursors.clear(), this.assets.clear(), + this.indexedAssets.clear(), this.abis.clear(), this.errors.clear(), ]); diff --git a/packages/app/src/systems/Core/utils/databaseVersioning.ts b/packages/app/src/systems/Core/utils/databaseVersioning.ts index 5dc153477..9389dbeec 100644 --- a/packages/app/src/systems/Core/utils/databaseVersioning.ts +++ b/packages/app/src/systems/Core/utils/databaseVersioning.ts @@ -187,6 +187,7 @@ export const applyDbVersioning = (db: Dexie) => { }); // DB VERSION 26 + // cleanup networks and add defaults db.version(26) .stores({ vaults: 'key', @@ -217,4 +218,18 @@ export const applyDbVersioning = (db: Dexie) => { }); } }); + + // DB VERSION 27 + // add indexedAssets table + db.version(27).stores({ + vaults: 'key', + accounts: '&address, &name', + networks: '&id, &url, &name, chainId', + connections: 'origin', + transactionsCursors: '++id, address, size, providerUrl, endCursor', + assets: '&name, &symbol', + indexedAssets: 'key', + abis: '&contractId', + errors: '&id', + }); }; diff --git a/packages/types/src/accounts.ts b/packages/types/src/accounts.ts index 4b52ce3da..83a1145a9 100644 --- a/packages/types/src/accounts.ts +++ b/packages/types/src/accounts.ts @@ -8,7 +8,11 @@ export type Vault = { }; export interface CoinAsset extends Coin { - asset?: AssetFuel; + asset?: AssetFuel & { + indexed?: boolean; + suspicious?: boolean; + isNft?: boolean; + }; } export type Account = { diff --git a/packages/types/src/asset.ts b/packages/types/src/asset.ts index 89d6eee35..5b52655e8 100644 --- a/packages/types/src/asset.ts +++ b/packages/types/src/asset.ts @@ -4,6 +4,9 @@ export type AssetData = Asset & { // override icon to don't be required icon?: string; isCustom?: boolean; + indexed?: boolean; + suspicious?: boolean; + isNft?: boolean; }; export type AssetAmount = AssetData & { @@ -14,6 +17,9 @@ export type AssetFuelData = AssetFuel & { // override icon to don't be required icon?: string; isCustom?: boolean; + indexed?: boolean; + suspicious?: boolean; + isNft?: boolean; }; export type AssetFuelAmount = AssetFuelData & {