From f375463c1117176a4991bf07f12dd4afc2cc31e9 Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Wed, 14 Aug 2024 22:28:58 +0200 Subject: [PATCH 1/7] Fix NFT display; reduce API calls --- packages/extension/src/background/index.ts | 5 ++- .../extension/src/background/networkStatus.ts | 2 +- .../accountNfts/alephium-nft.service.ts | 10 +++++- .../features/accountNfts/useNFTCollections.ts | 1 + .../ui/features/accountTokens/tokens.state.ts | 34 +++++++++++++------ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/packages/extension/src/background/index.ts b/packages/extension/src/background/index.ts index 48567cd6..976b7f7c 100644 --- a/packages/extension/src/background/index.ts +++ b/packages/extension/src/background/index.ts @@ -1,4 +1,5 @@ import browser from "webextension-polyfill" +import { getAccountSelector, withoutHiddenSelector } from "../shared/account/selectors" import { accountStore, getAccounts } from "../shared/account/store" import { globalActionQueueStore } from "../shared/actionQueue/store" @@ -44,7 +45,9 @@ setTokenListUpdateAlarm() browser.alarms.onAlarm.addListener(async (alarm) => { if (alarm.name === transactionTrackerHistoryAlarm) { console.info("~> fetching transaction history") - await transactionTracker.loadHistory(await getAccounts()) + const selectedAccount = await walletStore.get("selected") + const accountSelector = selectedAccount ? getAccountSelector(selectedAccount) : withoutHiddenSelector + await transactionTracker.loadHistory(await getAccounts(accountSelector)) } if (alarm.name === transactionTrackerUpdateAlarm) { diff --git a/packages/extension/src/background/networkStatus.ts b/packages/extension/src/background/networkStatus.ts index dece1e85..e823dc41 100644 --- a/packages/extension/src/background/networkStatus.ts +++ b/packages/extension/src/background/networkStatus.ts @@ -13,7 +13,7 @@ const swrStorage = new KeyValueStorage>( // see: https://github.com/jperasmus/stale-while-revalidate-cache#configuration const swr = createStaleWhileRevalidateCache({ storage: swrStorage, // can be any object with getItem and setItem methods - minTimeToStale: 60e3, // 1 minute + minTimeToStale: 2 * 60e3, // 2 minutes maxTimeToLive: 30 * 60e3, // 30 minutes }) diff --git a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts index a9cd3bc0..fe6c14b9 100644 --- a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts +++ b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts @@ -3,14 +3,22 @@ import { NFTCollection } from "./alephium-nft.model" import { Network } from "../../../shared/network" import { ExplorerProvider, NodeProvider } from "@alephium/web3" import { fetchImmutable } from "../../../shared/utils/fetchImmutable" +import { chunk } from 'lodash' export const fetchCollectionAndNfts = async ( nonFungibleTokenIds: string[], network: Network ): Promise => { + const maxSizeTokens = 80 const explorerProvider = new ExplorerProvider(network.explorerApiUrl) const parentAndTokenIds: CollectionAndNFTMap = {} - const nftMetadataz = await explorerProvider.tokens.postTokensNftMetadata(nonFungibleTokenIds) + + const nftMetadataz = (await Promise.all( + chunk(nonFungibleTokenIds, maxSizeTokens).map((nftIds) => + explorerProvider.tokens.postTokensNftMetadata(nftIds) + ) + )).flat() + for (const nftMetadata of nftMetadataz) { const tokenId = nftMetadata.id const collectionId = nftMetadata.collectionId diff --git a/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts b/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts index 1f309b6c..b6788f83 100644 --- a/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts +++ b/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts @@ -22,6 +22,7 @@ export const useCollectionAndNFTs = ( () => fetchCollectionAndNfts(nonFungibleTokenIds, network), { ...config, + dedupingInterval: 5000, refreshInterval: 30000, } ) diff --git a/packages/extension/src/ui/features/accountTokens/tokens.state.ts b/packages/extension/src/ui/features/accountTokens/tokens.state.ts index b14597fa..318b8361 100644 --- a/packages/extension/src/ui/features/accountTokens/tokens.state.ts +++ b/packages/extension/src/ui/features/accountTokens/tokens.state.ts @@ -1,4 +1,4 @@ -import { ALPH_TOKEN_ID, HexString, NodeProvider } from "@alephium/web3" +import { ALPH_TOKEN_ID, NodeProvider } from "@alephium/web3" import { BigNumber } from "ethers" import { memoize } from "lodash-es" import { useEffect, useMemo, useRef } from "react" @@ -268,6 +268,7 @@ export const useAllTokensWithBalance = ( }, { refreshInterval: 30000, + dedupingInterval: 5000, shouldRetryOnError: (error: any) => { const errorCode = error?.status || error?.errorCode const suppressError = @@ -337,17 +338,28 @@ async function fetchFungibleTokenFromFullNode(network: Network, tokenId: string) return undefined } - const metadata = await fetchImmutable(`${tokenId}-token-metadata`, () => nodeProvider.fetchFungibleTokenMetaData(tokenId)) - const token: Token = { - id: tokenId, - networkId: network.id, - name: Buffer.from(metadata.name, 'hex').toString('utf8'), - symbol: Buffer.from(metadata.symbol, 'hex').toString('utf8'), - decimals: metadata.decimals, - verified: false + const metadata = await fetchImmutable(`${tokenId}-token-metadata`, async () => { + try { + return (await nodeProvider.fetchFungibleTokenMetaData(tokenId)) + } catch (e: any) { + if (e.message.startsWith('Failed to call contract')) { + return { name: undefined, symbol: undefined, decimals: 0 } + } else { + throw e + } + } + }) + + if (metadata.name && metadata.symbol) { + return { + id: tokenId, + networkId: network.id, + name: Buffer.from(metadata.name, 'hex').toString('utf8'), + symbol: Buffer.from(metadata.symbol, 'hex').toString('utf8'), + decimals: metadata.decimals, + verified: false + } } - - return token } catch (e) { console.debug(`Failed to fetch token metadata for ${tokenId}`, e) return undefined From cccfe66d79ebc7e66ec6448893317cf130e07003 Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 12:31:38 +0200 Subject: [PATCH 2/7] Cache NFT metadata --- .../src/shared/utils/fetchImmutable.ts | 8 ++++ .../accountNfts/alephium-nft.service.ts | 43 ++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/extension/src/shared/utils/fetchImmutable.ts b/packages/extension/src/shared/utils/fetchImmutable.ts index b7c44986..997b5ab2 100644 --- a/packages/extension/src/shared/utils/fetchImmutable.ts +++ b/packages/extension/src/shared/utils/fetchImmutable.ts @@ -20,6 +20,14 @@ export async function fetchImmutable(key: string, fetcher: Fetcher(key: string): Promise { + return await fetchImmutableStorage.get(key) +} + +export async function storeImmutable(key: string, data: Data): Promise { + return await fetchImmutableStorage.set(key, data) +} + export async function clearCache(key: string): Promise { await fetchImmutableStorage.delete(key) } diff --git a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts index fe6c14b9..b70dafc4 100644 --- a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts +++ b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts @@ -2,23 +2,18 @@ import { NFT, CollectionAndNFTMap } from "./alephium-nft.model" import { NFTCollection } from "./alephium-nft.model" import { Network } from "../../../shared/network" import { ExplorerProvider, NodeProvider } from "@alephium/web3" -import { fetchImmutable } from "../../../shared/utils/fetchImmutable" +import { fetchImmutable, getImmutable, storeImmutable } from "../../../shared/utils/fetchImmutable" import { chunk } from 'lodash' +import { NFTMetadata } from "@alephium/web3/dist/src/api/api-explorer" export const fetchCollectionAndNfts = async ( - nonFungibleTokenIds: string[], + nftIds: string[], network: Network ): Promise => { - const maxSizeTokens = 80 const explorerProvider = new ExplorerProvider(network.explorerApiUrl) const parentAndTokenIds: CollectionAndNFTMap = {} - const nftMetadataz = (await Promise.all( - chunk(nonFungibleTokenIds, maxSizeTokens).map((nftIds) => - explorerProvider.tokens.postTokensNftMetadata(nftIds) - ) - )).flat() - + const nftMetadataz = await getNftMetadataz(nftIds, explorerProvider) for (const nftMetadata of nftMetadataz) { const tokenId = nftMetadata.id const collectionId = nftMetadata.collectionId @@ -30,9 +25,9 @@ export const fetchCollectionAndNfts = async ( } // TODO: Fix explorer backend for 000301 NFTs - if (nftMetadataz.length != nonFungibleTokenIds.length) { + if (nftMetadataz.length != nftIds.length) { const nodeProvider = new NodeProvider(network.nodeUrl) - const otherNonFungibleTokenIds = nonFungibleTokenIds.filter(id => nftMetadataz.findIndex((m) => m.id == id) == -1) + const otherNonFungibleTokenIds = nftIds.filter(id => nftMetadataz.findIndex((m) => m.id == id) == -1) for (const tokenId of otherNonFungibleTokenIds) { const nftMetadata = await fetchImmutable(`${tokenId}-nft-metadata`, () => nodeProvider.fetchNFTMetaData(tokenId)) const collectionId = nftMetadata.collectionId @@ -94,3 +89,29 @@ async function getCollectionMetadata( const metadataResponse = await fetch(metadata.collectionUri) return await metadataResponse.json() } + +async function getNftMetadataz(nftIds: string[], explorerProvider: ExplorerProvider) { + const maxSizeTokens = 80 + const cachedNftMetadataz: NFTMetadata[] = [] + const nftIdsWithoutMetadata: string[] = [] + for (const nftId of nftIds) { + const metadata = await getImmutable(`${nftId}-nft-metadata`) + if (metadata?.id) { + cachedNftMetadataz.push(metadata) + } else { + nftIdsWithoutMetadata.push(nftId) + } + } + + const newNftMetadataz = (await Promise.all( + chunk(nftIdsWithoutMetadata, maxSizeTokens).map((ids) => + explorerProvider.tokens.postTokensNftMetadata(ids) + ) + )).flat() + + for (const newNftMetadata of newNftMetadataz) { + await storeImmutable(`${newNftMetadata.id}-nft-metadata`, newNftMetadata) + } + + return cachedNftMetadataz.concat(newNftMetadataz) +} \ No newline at end of file From 4e7aefadb1418f4725b594baf41e6dfff59e82f9 Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 12:37:04 +0200 Subject: [PATCH 3/7] Minor fix --- .../ui/features/accountNfts/alephium-nft.service.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts index b70dafc4..54f5ac12 100644 --- a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts +++ b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts @@ -103,11 +103,14 @@ async function getNftMetadataz(nftIds: string[], explorerProvider: ExplorerProvi } } - const newNftMetadataz = (await Promise.all( - chunk(nftIdsWithoutMetadata, maxSizeTokens).map((ids) => - explorerProvider.tokens.postTokensNftMetadata(ids) - ) - )).flat() + let newNftMetadataz: NFTMetadata[] = [] + if (nftIdsWithoutMetadata.length !== 0) { + newNftMetadataz = (await Promise.all( + chunk(nftIdsWithoutMetadata, maxSizeTokens).map((ids) => + explorerProvider.tokens.postTokensNftMetadata(ids) + ) + )).flat() + } for (const newNftMetadata of newNftMetadataz) { await storeImmutable(`${newNftMetadata.id}-nft-metadata`, newNftMetadata) From 8069285e729e6d0d003c83a826d9788aacf072cd Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 13:57:38 +0200 Subject: [PATCH 4/7] Improve NFT metadata caching --- .../accountNfts/alephium-nft.service.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts index 54f5ac12..01101aab 100644 --- a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts +++ b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts @@ -103,17 +103,15 @@ async function getNftMetadataz(nftIds: string[], explorerProvider: ExplorerProvi } } - let newNftMetadataz: NFTMetadata[] = [] + const newNftMetadataz: NFTMetadata[] = [] if (nftIdsWithoutMetadata.length !== 0) { - newNftMetadataz = (await Promise.all( - chunk(nftIdsWithoutMetadata, maxSizeTokens).map((ids) => - explorerProvider.tokens.postTokensNftMetadata(ids) - ) - )).flat() - } - - for (const newNftMetadata of newNftMetadataz) { - await storeImmutable(`${newNftMetadata.id}-nft-metadata`, newNftMetadata) + for (const ids of chunk(nftIdsWithoutMetadata, maxSizeTokens)) { + const newMetadataz = await explorerProvider.tokens.postTokensNftMetadata(ids) + newNftMetadataz.push(...newMetadataz) + for (const newMetadata of newMetadataz) { + await storeImmutable(`${newMetadata.id}-nft-metadata`, newMetadata) + } + } } return cachedNftMetadataz.concat(newNftMetadataz) From 5f9fe1a1b8cec8c1e0d8147f4fac25de5f938cfd Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 16:44:09 +0200 Subject: [PATCH 5/7] Address review comments --- .../accountNfts/alephium-nft.service.ts | 6 ++--- .../features/accountNfts/useNFTCollections.ts | 4 +++- .../src/ui/features/accountNfts/useNfts.ts | 3 ++- .../ui/features/accountTokens/tokens.state.ts | 24 +++++++------------ packages/extension/src/ui/services/swr.ts | 5 ++++ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts index 01101aab..b3267604 100644 --- a/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts +++ b/packages/extension/src/ui/features/accountNfts/alephium-nft.service.ts @@ -29,7 +29,7 @@ export const fetchCollectionAndNfts = async ( const nodeProvider = new NodeProvider(network.nodeUrl) const otherNonFungibleTokenIds = nftIds.filter(id => nftMetadataz.findIndex((m) => m.id == id) == -1) for (const tokenId of otherNonFungibleTokenIds) { - const nftMetadata = await fetchImmutable(`${tokenId}-nft-metadata`, () => nodeProvider.fetchNFTMetaData(tokenId)) + const nftMetadata = await fetchImmutable(`nft-metadata-${tokenId}`, () => nodeProvider.fetchNFTMetaData(tokenId)) const collectionId = nftMetadata.collectionId if (parentAndTokenIds[collectionId]) { parentAndTokenIds[collectionId].push(tokenId) @@ -64,7 +64,7 @@ export const fetchNFTCollection = async ( export async function getNFT(collectionId: string, nftId: string, network: Network): Promise { try { const nodeProvider = new NodeProvider(network.nodeUrl) - const nftMetadata = await fetchImmutable(`${nftId}-nft-metadata`, () => nodeProvider.fetchNFTMetaData(nftId)) + const nftMetadata = await fetchImmutable(`nft-metadata-${nftId}`, () => nodeProvider.fetchNFTMetaData(nftId)) const metadataResponse = await fetch(nftMetadata.tokenUri) const metadata = await metadataResponse.json() return { @@ -85,7 +85,7 @@ async function getCollectionMetadata( collectionId: string, nodeProvider: NodeProvider ) { - const metadata = await fetchImmutable(`${collectionId}-nft-collection-metadata`, () => nodeProvider.fetchNFTCollectionMetaData(collectionId)) + const metadata = await fetchImmutable(`nft-collection-metadata-${collectionId}`, () => nodeProvider.fetchNFTCollectionMetaData(collectionId)) const metadataResponse = await fetch(metadata.collectionUri) return await metadataResponse.json() } diff --git a/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts b/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts index b6788f83..00f7d32c 100644 --- a/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts +++ b/packages/extension/src/ui/features/accountNfts/useNFTCollections.ts @@ -2,7 +2,7 @@ import useSWR from "swr" import { BaseWalletAccount } from "../../../shared/wallet.model" import { getAccountIdentifier } from "../../../shared/wallet.service" -import { SWRConfigCommon } from "../../services/swr" +import { SWRConfigCommon, retryWhenRateLimited } from "../../services/swr" import { Network } from "../../../shared/network" import { fetchNFTCollection, fetchCollectionAndNfts } from "./alephium-nft.service" @@ -24,6 +24,7 @@ export const useCollectionAndNFTs = ( ...config, dedupingInterval: 5000, refreshInterval: 30000, + shouldRetryOnError: retryWhenRateLimited } ) @@ -45,6 +46,7 @@ export const useNFTCollection = ( ...config, refreshInterval: 60e3 /* 1 minute */, use: [laggy], + shouldRetryOnError: retryWhenRateLimited }, ) return { collection, ...rest } diff --git a/packages/extension/src/ui/features/accountNfts/useNfts.ts b/packages/extension/src/ui/features/accountNfts/useNfts.ts index e0788774..adb97a8b 100644 --- a/packages/extension/src/ui/features/accountNfts/useNfts.ts +++ b/packages/extension/src/ui/features/accountNfts/useNfts.ts @@ -2,7 +2,7 @@ import useSWR from "swr" import { Network } from "../../../shared/network" import { BaseWalletAccount } from "../../../shared/wallet.model" import { getAccountIdentifier } from "../../../shared/wallet.service" -import { SWRConfigCommon } from "../../services/swr" +import { SWRConfigCommon, retryWhenRateLimited } from "../../services/swr" import { getNFT } from "./alephium-nft.service" import { laggy } from "./laggy" @@ -22,6 +22,7 @@ export const useNFT = ( revalidateOnMount: false, use: [laggy], suspense: true, + shouldRetryOnError: retryWhenRateLimited }, ) return { nft, ...rest } diff --git a/packages/extension/src/ui/features/accountTokens/tokens.state.ts b/packages/extension/src/ui/features/accountTokens/tokens.state.ts index 318b8361..bea5502e 100644 --- a/packages/extension/src/ui/features/accountTokens/tokens.state.ts +++ b/packages/extension/src/ui/features/accountTokens/tokens.state.ts @@ -17,6 +17,7 @@ import { sortBy } from "lodash" import { addTokenToBalances } from "../../../shared/token/balance" import { Transaction, compareTransactions } from "../../../shared/transactions" import { fetchImmutable } from "../../../shared/utils/fetchImmutable" +import { retryWhenRateLimited } from "../../services/swr" type UseTokensWithBalance = UseTokensBase type UseBaseTokensWithBalance = UseTokensBase @@ -136,7 +137,7 @@ export const useNonFungibleTokensWithBalance = ( const nonFungibleTokens: BaseTokenWithBalance[] = [] for (const token of potentialNonFungibleTokens) { if (nonFungibleTokens.findIndex((t) => t.id == token.id) === -1) { - const tokenType = await fetchImmutable(`${token.id}-token-type`, () => nodeProvider.guessStdTokenType(token.id)) + const tokenType = await fetchImmutable(`token-type-${token.id}`, () => nodeProvider.guessStdTokenType(token.id)) if (tokenType === 'non-fungible') { nonFungibleTokens.push({ id: token.id, networkId: networkId, balance: token.balance }) } @@ -146,7 +147,8 @@ export const useNonFungibleTokensWithBalance = ( return nonFungibleTokens }, { - refreshInterval: 30000 + refreshInterval: 30000, + shouldRetryOnError: retryWhenRateLimited } ) @@ -204,12 +206,7 @@ export const useFungibleTokensWithBalance = ( }, { refreshInterval: 30000, - shouldRetryOnError: (error: any) => { - const errorCode = error?.status || error?.errorCode - const suppressError = - errorCode && SUPPRESS_ERROR_STATUS.includes(errorCode) - return suppressError - }, + shouldRetryOnError: retryWhenRateLimited } ) const tokenDetailsIsInitialising = !error && !fungibleTokens @@ -269,12 +266,7 @@ export const useAllTokensWithBalance = ( { refreshInterval: 30000, dedupingInterval: 5000, - shouldRetryOnError: (error: any) => { - const errorCode = error?.status || error?.errorCode - const suppressError = - errorCode && SUPPRESS_ERROR_STATUS.includes(errorCode) - return suppressError - }, + shouldRetryOnError: retryWhenRateLimited } ) @@ -333,12 +325,12 @@ async function getBalances(nodeProvider: NodeProvider, address: string): Promise async function fetchFungibleTokenFromFullNode(network: Network, tokenId: string): Promise { const nodeProvider = new NodeProvider(network.nodeUrl) try { - const tokenType = await fetchImmutable(`${tokenId}-token-type`, () => nodeProvider.guessStdTokenType(tokenId)) + const tokenType = await fetchImmutable(`token-type-${tokenId}`, () => nodeProvider.guessStdTokenType(tokenId)) if (tokenType !== 'fungible') { return undefined } - const metadata = await fetchImmutable(`${tokenId}-token-metadata`, async () => { + const metadata = await fetchImmutable(`token-metadata-${tokenId}`, async () => { try { return (await nodeProvider.fetchFungibleTokenMetaData(tokenId)) } catch (e: any) { diff --git a/packages/extension/src/ui/services/swr.ts b/packages/extension/src/ui/services/swr.ts index 4a0d94df..1040b9c5 100644 --- a/packages/extension/src/ui/services/swr.ts +++ b/packages/extension/src/ui/services/swr.ts @@ -104,3 +104,8 @@ export const swrCacheProvider: Cache = { } }, } + +export const retryWhenRateLimited = (error: any) => { + const errorCode = error?.status || error?.errorCode + return errorCode && errorCode === 429 +} From cf0c46dd91dd48172809703601080383e8c1f5a6 Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 22:28:34 +0200 Subject: [PATCH 6/7] Upgrade sdk to 1.5.0 --- packages/dapp/.project.json | 17 ++--- packages/dapp/artifacts/Destroy.ral.json | 5 +- packages/dapp/artifacts/ShinyToken.ral.json | 20 +---- packages/dapp/artifacts/Transfer.ral.json | 5 +- packages/dapp/artifacts/ts/ShinyToken.ts | 53 ++++++------- packages/dapp/artifacts/ts/contracts.ts | 1 + packages/dapp/artifacts/ts/scripts.ts | 5 +- packages/dapp/package.json | 6 +- packages/extension/package.json | 8 +- yarn.lock | 84 +++++++++++++-------- 10 files changed, 102 insertions(+), 102 deletions(-) diff --git a/packages/dapp/.project.json b/packages/dapp/.project.json index 94a06ff5..7250fe3f 100644 --- a/packages/dapp/.project.json +++ b/packages/dapp/.project.json @@ -1,41 +1,38 @@ { - "fullNodeVersion": "v3.3.0", + "fullNodeVersion": "v3.5.0", "compilerOptionsUsed": { "ignoreUnusedConstantsWarnings": false, "ignoreUnusedVariablesWarnings": false, "ignoreUnusedFieldsWarnings": false, "ignoreUnusedPrivateFunctionsWarnings": false, "ignoreUpdateFieldsCheckWarnings": false, - "ignoreCheckExternalCallerWarnings": false + "ignoreCheckExternalCallerWarnings": false, + "ignoreUnusedFunctionReturnWarnings": false }, "infos": { "Destroy": { "sourceFile": "destroy.ral", "sourceCodeHash": "81d9d935ecd97844aa8bb59687865673567402bdc1b9fa610ce2e435f3f4c87d", "bytecodeDebugPatch": "", - "codeHashDebug": "", - "warnings": [] + "codeHashDebug": "" }, "IFungibleToken": { "sourceFile": "../../../node_modules/@alephium/web3/std/fungible_token_interface.ral", "sourceCodeHash": "62910bf11e1eeb6cb2fd468626ff606a9b06306b2b81590c3b10f6deb5966bde", "bytecodeDebugPatch": "", - "codeHashDebug": "", - "warnings": [] + "codeHashDebug": "" }, "ShinyToken": { "sourceFile": "shiny-token.ral", "sourceCodeHash": "b6fdc5d87df9a51ee1611196216db85f14ee257cf7487ad2d7676f6518336c09", "bytecodeDebugPatch": "", - "codeHashDebug": "9bdc139154d4e611dd391a5b262cc081d2519b9a3ccc95df943a98a9e3c67661", - "warnings": [] + "codeHashDebug": "9bdc139154d4e611dd391a5b262cc081d2519b9a3ccc95df943a98a9e3c67661" }, "Transfer": { "sourceFile": "transfer.ral", "sourceCodeHash": "2e2589a93623a069b127fd94f27a155d1794a80d1ee5f354457f61814e15bb40", "bytecodeDebugPatch": "", - "codeHashDebug": "", - "warnings": [] + "codeHashDebug": "" } } } \ No newline at end of file diff --git a/packages/dapp/artifacts/Destroy.ral.json b/packages/dapp/artifacts/Destroy.ral.json index 7573456f..ba2bcc3d 100644 --- a/packages/dapp/artifacts/Destroy.ral.json +++ b/packages/dapp/artifacts/Destroy.ral.json @@ -1,5 +1,5 @@ { - "version": "v3.3.0", + "version": "v3.5.0", "name": "Destroy", "bytecodeTemplate": "01010300000005{1}0d0c{0}0105", "fieldsSig": { @@ -19,9 +19,6 @@ "functions": [ { "name": "main", - "usePreapprovedAssets": true, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], diff --git a/packages/dapp/artifacts/ShinyToken.ral.json b/packages/dapp/artifacts/ShinyToken.ral.json index e5e59447..6f8f1da7 100644 --- a/packages/dapp/artifacts/ShinyToken.ral.json +++ b/packages/dapp/artifacts/ShinyToken.ral.json @@ -1,5 +1,5 @@ { - "version": "v3.3.0", + "version": "v3.5.0", "name": "ShinyToken", "bytecode": "050609121b40244041404f010000000102ce0002010000000102ce0102010000000102ce0202010000000102ce0302010202020008d36ee15a7b1600b11601ab160013c3038d7ea4c68000a8010201010003d320f98f621600b0", "codeHash": "9bdc139154d4e611dd391a5b262cc081d2519b9a3ccc95df943a98a9e3c67661", @@ -30,9 +30,6 @@ "functions": [ { "name": "getSymbol", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], @@ -42,9 +39,6 @@ }, { "name": "getName", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], @@ -54,9 +48,6 @@ }, { "name": "getDecimals", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], @@ -66,9 +57,6 @@ }, { "name": "getTotalSupply", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], @@ -78,9 +66,6 @@ }, { "name": "transfer", - "usePreapprovedAssets": false, - "useAssetsInContract": true, - "isPublic": true, "paramNames": [ "to", "amount" @@ -97,9 +82,6 @@ }, { "name": "destroy", - "usePreapprovedAssets": false, - "useAssetsInContract": true, - "isPublic": true, "paramNames": [ "to" ], diff --git a/packages/dapp/artifacts/Transfer.ral.json b/packages/dapp/artifacts/Transfer.ral.json index 572b452e..e036027b 100644 --- a/packages/dapp/artifacts/Transfer.ral.json +++ b/packages/dapp/artifacts/Transfer.ral.json @@ -1,5 +1,5 @@ { - "version": "v3.3.0", + "version": "v3.5.0", "name": "Transfer", "bytecodeTemplate": "01010300000006{1}{2}0e0c{0}0104", "fieldsSig": { @@ -22,9 +22,6 @@ "functions": [ { "name": "main", - "usePreapprovedAssets": true, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], diff --git a/packages/dapp/artifacts/ts/ShinyToken.ts b/packages/dapp/artifacts/ts/ShinyToken.ts index 7f59e40a..76d218b9 100644 --- a/packages/dapp/artifacts/ts/ShinyToken.ts +++ b/packages/dapp/artifacts/ts/ShinyToken.ts @@ -4,33 +4,34 @@ import { Address, + CallContractParams, + CallContractResult, Contract, - ContractState, - TestContractResult, - HexString, + ContractEvent, ContractFactory, + ContractInstance, + ContractState, EventSubscribeOptions, EventSubscription, - CallContractParams, - CallContractResult, + HexString, + SignExecuteContractMethodParams, + SignExecuteScriptTxResult, TestContractParams, - ContractEvent, - subscribeContractEvent, - subscribeContractEvents, - testMethod, - callMethod, - multicallMethods, - fetchContractState, - ContractInstance, - getContractEventsCurrentCount, TestContractParamsWithoutMaps, + TestContractResult, TestContractResultWithoutMaps, - SignExecuteContractMethodParams, - SignExecuteScriptTxResult, - signExecuteMethod, addStdIdToFields, + callMethod, encodeContractFields, + fetchContractState, + getContractEventsCurrentCount, + multicallMethods, + signExecuteMethod, + subscribeContractEvent, + subscribeContractEvents, + testMethod, } from "@alephium/web3"; + import { default as ShinyTokenContractJson } from "../ShinyToken.ral.json"; import { getContractByCodeHash } from "./contracts"; @@ -83,6 +84,10 @@ export namespace ShinyTokenTypes { ? CallMethodTable[MaybeName]["result"] : undefined; }; + export type MulticallReturnType = + Callss["length"] extends 1 + ? MultiCallResults + : { [index in keyof Callss]: MultiCallResults }; export interface SignExecuteMethodTable { getSymbol: { @@ -128,10 +133,6 @@ class Factory extends ContractFactory< ); } - getInitialFieldsWithDefaultValues() { - return this.contract.getInitialFieldsWithDefaultValues() as ShinyTokenTypes.Fields; - } - at(address: string): ShinyTokenInstance { return new ShinyTokenInstance(address); } @@ -310,14 +311,14 @@ export class ShinyTokenInstance extends ContractInstance { }, }; - async multicall( - calls: Calls - ): Promise> { + async multicall( + ...callss: Callss + ): Promise> { return (await multicallMethods( ShinyToken, this, - calls, + callss, getContractByCodeHash - )) as ShinyTokenTypes.MultiCallResults; + )) as ShinyTokenTypes.MulticallReturnType; } } diff --git a/packages/dapp/artifacts/ts/contracts.ts b/packages/dapp/artifacts/ts/contracts.ts index e8873052..8caa68e5 100644 --- a/packages/dapp/artifacts/ts/contracts.ts +++ b/packages/dapp/artifacts/ts/contracts.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import { Contract, ContractFactory } from "@alephium/web3"; + import { ShinyToken } from "."; let contracts: ContractFactory[] | undefined = undefined; diff --git a/packages/dapp/artifacts/ts/scripts.ts b/packages/dapp/artifacts/ts/scripts.ts index 992e302b..516c631d 100644 --- a/packages/dapp/artifacts/ts/scripts.ts +++ b/packages/dapp/artifacts/ts/scripts.ts @@ -7,13 +7,14 @@ import { ExecutableScript, ExecuteScriptParams, ExecuteScriptResult, + HexString, Script, SignerProvider, - HexString, } from "@alephium/web3"; -import { getContractByCodeHash } from "./contracts"; + import { default as DestroyScriptJson } from "../Destroy.ral.json"; import { default as TransferScriptJson } from "../Transfer.ral.json"; +import { getContractByCodeHash } from "./contracts"; export const Destroy = new ExecutableScript<{ shinyToken: HexString; diff --git a/packages/dapp/package.json b/packages/dapp/package.json index 682fa0c7..2de12e76 100644 --- a/packages/dapp/package.json +++ b/packages/dapp/package.json @@ -10,15 +10,15 @@ "lint": "next lint" }, "dependencies": { - "@alephium/get-extension-wallet": "^1.2.2", - "@alephium/web3": "^1.2.2", + "@alephium/get-extension-wallet": "^1.5.0", + "@alephium/web3": "^1.5.0", "ethers": "^5.5.1", "next": "^13.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, "devDependencies": { - "@alephium/cli": "^1.2.2", + "@alephium/cli": "^1.5.0", "@types/node": "18.11.18", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", diff --git a/packages/extension/package.json b/packages/extension/package.json index 8ca7d8cc..bd4dd37b 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -4,12 +4,12 @@ "main": "index.js", "license": "MIT", "devDependencies": { - "@alephium/get-extension-wallet": "^1.2.2", + "@alephium/get-extension-wallet": "^1.5.0", "@alephium/ledger-app": "0.5.2", "@alephium/token-list": "0.0.19", - "@alephium/web3": "^1.2.2", - "@alephium/web3-test": "^1.2.2", - "@alephium/web3-wallet": "^1.2.2", + "@alephium/web3": "^1.5.0", + "@alephium/web3-test": "^1.5.0", + "@alephium/web3-wallet": "^1.5.0", "@ledgerhq/hw-transport-webusb": "6.29.0", "@ledgerhq/hw-transport-webhid": "6.29.0", "@playwright/test": "^1.23.0", diff --git a/yarn.lock b/yarn.lock index 0c491081..b6be4933 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,13 @@ # yarn lockfile v1 -"@alephium/cli@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@alephium/cli/-/cli-1.2.2.tgz#3e2ef490a7f7d1d747fca1b2c264c437c0f21fb2" - integrity sha512-wQqEzLj+4ULbIt8mm09BCNMoUuY5i+94aSsPs4RAPuzrX7qLEMcRLhG7Jubu5tWZ9oTuf9AJjog9oLH7qH5wkw== +"@alephium/cli@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@alephium/cli/-/cli-1.5.0.tgz#4d8d1ac4ab86a9ea606495e537148c5beebdb117" + integrity sha512-/A8wqC1ylfdC1ED+15YtBe6SB61shJmhh67TS+FywCcj4ujzihg8Qn/V6lGnhTAk+Deb3tVP0ojEo7zSCeZqVg== dependencies: - "@alephium/web3" "^1.2.2" - "@alephium/web3-wallet" "^1.2.2" + "@alephium/web3" "^1.5.0" + "@alephium/web3-wallet" "^1.5.0" "@swc/core" "^1.4.1" commander "^9.5.0" cross-fetch "^3.1.5" @@ -18,12 +18,12 @@ jest "^28.1.3" prettier "^2.8.7" -"@alephium/get-extension-wallet@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@alephium/get-extension-wallet/-/get-extension-wallet-1.2.2.tgz#129bfcfefe520d56281940ca6d11d9476535e00a" - integrity sha512-ETZO2EILVqsKSQyc9HIEs8B1d5Plw7fC/R6R9jsSbORBdmDg6strubKkRH6RB+ciRMgDakumeAfRyvJJEZi2Qg== +"@alephium/get-extension-wallet@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@alephium/get-extension-wallet/-/get-extension-wallet-1.5.0.tgz#77554e47fbbf63d9c2744c8d48aebc9414019d4b" + integrity sha512-5uuM6i10oVGiacrI+EacA1Z2mZB557maVW3DZ5NMMYuA/mbpSDKphMBxRTH/EdgN6SjEo0UiHVGH6sYGiPZIQQ== dependencies: - "@alephium/web3" "^1.2.2" + "@alephium/web3" "^1.5.0" bowser "^2.11.0" "@alephium/ledger-app@0.5.2": @@ -41,20 +41,20 @@ dependencies: cross-fetch "^3.1.8" -"@alephium/web3-test@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@alephium/web3-test/-/web3-test-1.2.2.tgz#6354992a6d93c4aa0425abbd5cbaffbb8e8e93bd" - integrity sha512-uo3HrmdZXINk6J5C3Sf383KQbbxCX2KhFP+JihypxeOcenQLlLoTFC3BAjSiFO3s17FtABTbft2QOaBUqvF9/w== +"@alephium/web3-test@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@alephium/web3-test/-/web3-test-1.5.0.tgz#553bb729032e88fd33ff78de974bd93eee09e753" + integrity sha512-IKFsMYgOnFcyjkVLnFzubZYXx/p+X4AMNQQaiCmryRMV6xe4z5+zMgg3FRPL0oJ+ZLe86v84TTqi0ZiHNn+zgg== dependencies: - "@alephium/web3" "^1.2.2" - "@alephium/web3-wallet" "^1.2.2" + "@alephium/web3" "^1.5.0" + "@alephium/web3-wallet" "^1.5.0" -"@alephium/web3-wallet@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@alephium/web3-wallet/-/web3-wallet-1.2.2.tgz#8ec56009830d96a883f21d0f9df4722ba570d3dd" - integrity sha512-UDhqvyWwZ4A31yakdIb2dBN74tRgfFoZznjzWFBTruGKex+h47HX7jC9f+AsZ+JxOOX8A9pkZ60v4TYIvGfeWw== +"@alephium/web3-wallet@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@alephium/web3-wallet/-/web3-wallet-1.5.0.tgz#f012ca9900061e47c6d82ea0043577fe459e3f9a" + integrity sha512-WBXA4Nm/0PnLkSK1lbjaSWMohQy9JZOHSA/uk9N4PXOn8OvCeF31aT/XnXauFCR6PTjE9YhyKlt3PYKCw4PxLw== dependencies: - "@alephium/web3" "^1.2.2" + "@alephium/web3" "^1.5.0" "@noble/secp256k1" "1.7.1" "@types/node" "^16.18.23" bip32 "3.1.0" @@ -80,15 +80,14 @@ path-browserify "^1.0.1" stream-browserify "^3.0.0" -"@alephium/web3@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@alephium/web3/-/web3-1.2.2.tgz#2327c97de039f9147f289db107a63d85c00a669e" - integrity sha512-T/WYEELWX1qhFETPYiWXaeVTxNmDkrFW9G+zH998W+g9TzZoR0BBj/RVvy+FPWxaYbWvkrv+ykB78z5U4xYsnA== +"@alephium/web3@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@alephium/web3/-/web3-1.5.0.tgz#2949fd08fb1650a930c66730353957cdbef0dce0" + integrity sha512-VLc7lDAYHecLqbF3Kj9oiuRehAf6l2ZDJu1JP0OKR/6zD28iV66nUEoU6Ulk23xFNFupKy8ogLXYFgIcM0rEbA== dependencies: "@noble/secp256k1" "1.7.1" base-x "4.0.0" bignumber.js "^9.1.1" - binary-parser "2.2.1" blakejs "1.2.1" bn.js "5.2.1" cross-fetch "^3.1.5" @@ -21292,7 +21291,16 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -21392,7 +21400,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -21406,6 +21414,13 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -23725,7 +23740,7 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -23743,6 +23758,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From ce77bab18a1e86e393f515907974fb55f6c61a2c Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Thu, 15 Aug 2024 22:30:17 +0200 Subject: [PATCH 7/7] Extract error code from web3 API response --- packages/extension/src/shared/utils/error.ts | 8 ++++++++ packages/extension/src/ui/services/swr.ts | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/shared/utils/error.ts b/packages/extension/src/shared/utils/error.ts index 9010e943..d2faba3c 100644 --- a/packages/extension/src/shared/utils/error.ts +++ b/packages/extension/src/shared/utils/error.ts @@ -31,3 +31,11 @@ export const getErrorObject = (error: any, includeStack = true) => { // ignore parsing error } } + +const statusCodeRegex = /Status\s+code:\s+(\d{3})/ +export const extractStatusCode = (error: any): number | undefined => { + const match = (error instanceof Error) && error.message.match(statusCodeRegex) + if (match) { + return Number(match[1]) + } +} \ No newline at end of file diff --git a/packages/extension/src/ui/services/swr.ts b/packages/extension/src/ui/services/swr.ts index 1040b9c5..f8680c30 100644 --- a/packages/extension/src/ui/services/swr.ts +++ b/packages/extension/src/ui/services/swr.ts @@ -9,6 +9,7 @@ import useSWR, { } from "swr" import { reviveJsonBigNumber } from "../../shared/json" +import { extractStatusCode } from "../../shared/utils/error" export interface SWRConfigCommon { suspense?: boolean @@ -106,6 +107,6 @@ export const swrCacheProvider: Cache = { } export const retryWhenRateLimited = (error: any) => { - const errorCode = error?.status || error?.errorCode + const errorCode = error?.status || error?.errorCode || extractStatusCode(error) return errorCode && errorCode === 429 }