diff --git a/.changeset/brown-badgers-wave.md b/.changeset/brown-badgers-wave.md new file mode 100644 index 000000000..95e233a44 --- /dev/null +++ b/.changeset/brown-badgers-wave.md @@ -0,0 +1,5 @@ +--- +"fuels-wallet": patch +--- + +Fetch and display NFT data directly from indexer. diff --git a/.changeset/light-cooks-dress.md b/.changeset/light-cooks-dress.md new file mode 100644 index 000000000..d4b907850 --- /dev/null +++ b/.changeset/light-cooks-dress.md @@ -0,0 +1,5 @@ +--- +"fuels-wallet": minor +--- + +Support SRC-7, 9 nft asset data. diff --git a/.changeset/rotten-grapes-itch.md b/.changeset/rotten-grapes-itch.md new file mode 100644 index 000000000..b8acb9b14 --- /dev/null +++ b/.changeset/rotten-grapes-itch.md @@ -0,0 +1,5 @@ +--- +"fuels-wallet": patch +--- + +Fixes intermittent long loading state on the wallet diff --git a/packages/app/src/systems/Account/services/account.ts b/packages/app/src/systems/Account/services/account.ts index 3c2005bb0..8b11d9691 100644 --- a/packages/app/src/systems/Account/services/account.ts +++ b/packages/app/src/systems/Account/services/account.ts @@ -104,40 +104,22 @@ export class AccountService { try { const provider = await createProvider(providerUrl!); const balances = await getBalances(provider, account.publicKey); - - const assets = await AssetService.getAssets(); + const balanceAssets = await AssetsCache.fetchAllAssets( + provider.getChainId(), + balances.map((balance) => balance.assetId) + ); // includes "asset" prop in balance, centralizing the complexity here instead of in rest of UI const nextBalancesWithAssets = await balances.reduce( async (acc, balance) => { const prev = await acc; - 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 (_) {} + const cachedAsset = balanceAssets.get(balance.assetId); return [ ...prev, { ...balance, amount: balance.amount, - asset: asset.fuel, + asset: cachedAsset, }, ]; }, diff --git a/packages/app/src/systems/Asset/cache/AssetsCache.ts b/packages/app/src/systems/Asset/cache/AssetsCache.ts index b91db2291..ceb4779ef 100644 --- a/packages/app/src/systems/Asset/cache/AssetsCache.ts +++ b/packages/app/src/systems/Asset/cache/AssetsCache.ts @@ -1,15 +1,24 @@ import type { AssetData } from '@fuel-wallet/types'; -import type { Asset, Provider } from 'fuels'; +import type { Asset, AssetFuel, Provider } from 'fuels'; +import { AssetService } from '~/systems/Asset/services'; +import { getFuelAssetByAssetId } from '~/systems/Asset/utils'; import { db } from '~/systems/Core/utils/database'; -import { fetchNftData } from '../utils/nft'; type Endpoint = { chainId: number; url: string; }; +const FIVE_MINUTES = 5 * 60 * 1000; export class AssetsCache { - private cache: { [chainId: number]: { [assetId: string]: Asset } }; + private cache: { + [chainId: number]: { + [assetId: string]: Asset & { fetchedAt?: number }; + }; + }; + private dbAssetsCache: { + [chainId: number]: Array; + }; private static instance: AssetsCache; private endpoints: Endpoint[] = [ { @@ -25,15 +34,44 @@ export class AssetsCache { private constructor() { this.cache = {}; + this.dbAssetsCache = {}; this.storage = new IndexedAssetsDB(); } + asset = { + name: '', + symbol: '', + metadata: {}, + }; + private getIndexerEndpoint(chainId: number) { return this.endpoints.find( (endpoint: Endpoint) => endpoint.chainId === chainId ); } + static async fetchAllAssets(chainId: number, assetsIds: string[]) { + const instance = AssetsCache.getInstance(); + const assetData = new Map(); + const dbAssets = await AssetService.getAssets(); + const promises = []; + for (const assetId of assetsIds) { + promises.push( + instance + .getAsset({ chainId, assetId, dbAssets }) + .then((asset) => { + assetData.set(assetId, asset); + }) + .catch((e) => { + console.error('Error fetching asset from indexer', e); + assetData.set(assetId, { name: '' } as AssetFuel); + }) + ); + } + await Promise.all(promises); + return assetData; + } + private async fetchAssetFromIndexer(url: string, assetId: string) { try { const timeout = new Promise((_, reject) => @@ -51,11 +89,23 @@ export class AssetsCache { } catch (_e: unknown) {} } + assetIsValid(asset: AssetData) { + return ( + asset.name != null && 'fetchedAt' in asset && asset.fetchedAt != null + ); + } + async getAsset({ chainId, assetId, - provider, - }: { chainId: number; assetId: string; provider: Provider }) { + dbAssets, + save = true, + }: { + chainId: number; + assetId: string; + dbAssets: AssetData[]; + save?: boolean; + }) { if (chainId == null || !assetId) { return; } @@ -63,48 +113,80 @@ export class AssetsCache { if (!endpoint) return; // try to get from memory cache first this.cache[chainId] = this.cache[chainId] || {}; - const assetFromCache = this.cache[chainId][assetId]; - if (assetFromCache?.name) { - return assetFromCache; + const cachedEntry = this.cache[chainId][assetId]; + const now = Date.now(); + + if (dbAssets?.length) { + this.dbAssetsCache[chainId] = dbAssets; + } + + if ( + cachedEntry?.name !== undefined && + cachedEntry.fetchedAt && + now - cachedEntry.fetchedAt < FIVE_MINUTES + ) { + return cachedEntry; } // get from indexed db if not in memory const assetFromDb = await this.storage.getItem(`${chainId}/${assetId}`); - if (assetFromDb?.name) { + if ( + assetFromDb?.name && + assetFromDb.fetchedAt && + now - assetFromDb.fetchedAt < FIVE_MINUTES + ) { this.cache[chainId][assetId] = assetFromDb; return assetFromDb; } + const dbAsset = await getFuelAssetByAssetId({ + assets: dbAssets.length ? dbAssets : this.dbAssetsCache[chainId], + assetId: assetId, + chainId, + }).catch((e) => { + console.error('Error fetching asset from db', e); + return undefined; + }); const assetFromIndexer = await this.fetchAssetFromIndexer( endpoint.url, assetId - ); + ).catch((e) => { + console.error('Error fetching asset from indexer', e); + return undefined; + }); + console.log('asd assetFromIndexer', assetFromIndexer); - if (!assetFromIndexer) return; + const { + isNFT, + metadata, + name: indexerAssetName, + symbol: indexerAssetSymbol, + ...rest + } = assetFromIndexer ?? {}; const asset = { - ...assetFromIndexer, - isNft: false, + ...dbAsset, + isNft: !!isNFT, + ...rest, + metadata, + fetchedAt: now, + name: indexerAssetName || dbAsset?.name, + symbol: indexerAssetSymbol || dbAsset?.symbol, }; - if (assetFromIndexer.contractId) { - const nftData = await fetchNftData({ - assetId, - contractId: assetFromIndexer.contractId, - provider, - }); - Object.assign(asset, nftData); + if (asset.name != null) { + asset.indexed = true; + } else { + // @TODO: Remove once we have a proper caching pattern/mechanism + asset.name = ''; } - this.cache[chainId][assetId] = asset; - this.storage.setItem(`${chainId}/${assetId}`, asset); + if (save) { + this.cache[chainId][assetId] = asset; + this.storage.setItem(`${chainId}/${assetId}`, asset); + } return asset; } - asset = { - name: '', - symbol: '', - metadata: {}, - }; static getInstance() { if (!AssetsCache.instance) { diff --git a/packages/app/src/systems/Asset/utils/nft.ts b/packages/app/src/systems/Asset/utils/nft.ts deleted file mode 100644 index 2122f0de6..000000000 --- a/packages/app/src/systems/Asset/utils/nft.ts +++ /dev/null @@ -1,656 +0,0 @@ -import { Contract, type Provider } from 'fuels'; - -export const fetchNftData = 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 }), - contract.functions.name({ bits: assetId }), - contract.functions.symbol({ bits: assetId }), - ]) - .dryRun(); - - const [total_supply, decimals, name, symbol] = result.value; - - return { - /* - 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 - */ - isNft: total_supply.toNumber() === 1 && !decimals, - name: name as string, - symbol: symbol as string, - }; -}; - -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 872cf71f5..783654770 100644 --- a/packages/app/src/systems/Core/utils/database.ts +++ b/packages/app/src/systems/Core/utils/database.ts @@ -22,7 +22,10 @@ export class FuelDB extends Dexie { connections!: Table; transactionsCursors!: Table; assets!: Table; - indexedAssets!: Table; + indexedAssets!: Table< + AssetData & { key: string; fetchedAt?: number }, + string + >; abis!: Table; errors!: Table; integrityCheckInterval?: NodeJS.Timeout; diff --git a/packages/app/src/systems/Core/utils/databaseVersioning.ts b/packages/app/src/systems/Core/utils/databaseVersioning.ts index 9389dbeec..8477ff938 100644 --- a/packages/app/src/systems/Core/utils/databaseVersioning.ts +++ b/packages/app/src/systems/Core/utils/databaseVersioning.ts @@ -232,4 +232,15 @@ export const applyDbVersioning = (db: Dexie) => { abis: '&contractId', errors: '&id', }); + db.version(28).stores({ + vaults: 'key', + accounts: '&address, &name', + networks: '&id, &url, &name, chainId', + connections: 'origin', + transactionsCursors: '++id, address, size, providerUrl, endCursor', + assets: '&name, &symbol', + indexedAssets: 'key, fetchedAt', + abis: '&contractId', + errors: '&id', + }); }; diff --git a/packages/app/src/systems/Send/hooks/useSend.tsx b/packages/app/src/systems/Send/hooks/useSend.tsx index 6342bac91..c4ddf5b77 100644 --- a/packages/app/src/systems/Send/hooks/useSend.tsx +++ b/packages/app/src/systems/Send/hooks/useSend.tsx @@ -143,10 +143,14 @@ const schemaFactory = (provider?: Provider) => const assetCached = await AssetsCache.getInstance().getAsset({ chainId: provider.getChainId(), assetId: value, - provider, + dbAssets: [], + save: false, }); - if (assetCached) { + if ( + assetCached && + !AssetsCache.getInstance().assetIsValid(assetCached) + ) { return ctx.createError({ message: `You can't send to Asset address`, });