Skip to content

Commit

Permalink
fix: incessant requerying of nfts with missing metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Aug 9, 2024
1 parent 1f9fc20 commit 4b54db2
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { useEffect } from 'react';

import {
useGetBnsNamesOwnedByAddressQuery,
useStacksNonFungibleTokensMetadata,
} from '@leather.io/query';
import { isNftAsset, useGetBnsNamesOwnedByAddressQuery } from '@leather.io/query';

import { analytics } from '@shared/utils/analytics';

import { parseIfValidPunycode } from '@app/common/utils';
import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/stacks-nft-token-metadata.query';

import { StacksBnsName } from './stacks-bns-name';
import { StacksNonFungibleTokens } from './stacks-non-fungible-tokens';
Expand All @@ -18,7 +16,13 @@ interface StacksCryptoAssetsProps {
export function StacksCryptoAssets({ address }: StacksCryptoAssetsProps) {
const names = useGetBnsNamesOwnedByAddressQuery(address).data?.names;

const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(address);
// NftMetadataResponse[]
const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(address)
.filter(resp => resp.status === 'success')
.map(resp => {
if (resp.data && isNftAsset(resp.data)) return resp.data;
return;
});

useEffect(() => {
if (stacksNftsMetadataResp.length > 0) {
Expand Down
53 changes: 53 additions & 0 deletions src/app/query/stacks/stacks-nft-token-metadata.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { hexToCV } from '@stacks/transactions';
import { useQueries } from '@tanstack/react-query';
import type { AxiosError } from 'axios';

import {
createGetNonFungibleTokenMetadataQueryOptions,
useGetNonFungibleTokenHoldingsQuery,
useStacksClient,
} from '@leather.io/query';

import { getPrincipalFromContractId } from '@app/common/utils';
import { useStacksNftMissingMetadata } from '@app/store/missing-stacks-nfts/missing-stacks-nfts.slice';

function getTokenId(hex: string) {
const clarityValue = hexToCV(hex);
return clarityValue.type === 1 ? Number(clarityValue.value) : 0;
}

function statusCodeNotFoundOrNotProcessable(status: number) {
return status === 404 || status === 422;
}

export function useStacksNonFungibleTokensMetadata(address: string) {
const nftHoldings = useGetNonFungibleTokenHoldingsQuery(address);
const stacksClient = useStacksClient();

const { addKnownMissingStacksNft, isMissingStacksNftMetadata } = useStacksNftMissingMetadata();

return useQueries({
queries: (nftHoldings.data?.results ?? []).map(nft => {
const address = getPrincipalFromContractId(nft.asset_identifier);
const tokenId = getTokenId(nft.value.hex);

const identifier = address + tokenId;

return {
...createGetNonFungibleTokenMetadataQueryOptions({
address,
client: stacksClient,
tokenId,
}),
enabled: !isMissingStacksNftMetadata(identifier),
retry(_count: number, error: AxiosError) {
if (statusCodeNotFoundOrNotProcessable(error.request.status)) {
addKnownMissingStacksNft(identifier);
return false;
}
return true;
},
};
}),
});
}
3 changes: 3 additions & 0 deletions src/app/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { stxChainSlice } from './chains/stx-chain.slice';
import { inMemoryKeySlice } from './in-memory-key/in-memory-key.slice';
import { bitcoinKeysSlice } from './ledger/bitcoin/bitcoin-key.slice';
import { stacksKeysSlice } from './ledger/stacks/stacks-key.slice';
import { missingDataSlice } from './missing-stacks-nfts/missing-stacks-nfts.slice';
import { networksSlice } from './networks/networks.slice';
import { ordinalsSlice } from './ordinals/ordinals.slice';
import { settingsSlice } from './settings/settings.slice';
Expand All @@ -47,6 +48,7 @@ export interface RootState {
};
ordinals: ReturnType<typeof ordinalsSlice.reducer>;
inMemoryKeys: ReturnType<typeof inMemoryKeySlice.reducer>;
missingData: ReturnType<typeof missingDataSlice.reducer>;
softwareKeys: ReturnType<typeof keySlice.reducer>;
networks: ReturnType<typeof networksSlice.reducer>;
submittedTransactions: ReturnType<typeof submittedTransactionsSlice.reducer>;
Expand All @@ -64,6 +66,7 @@ const appReducer = combineReducers({
}),
ordinals: ordinalsSlice.reducer,
inMemoryKeys: inMemoryKeySlice.reducer,
missingData: missingDataSlice.reducer,
softwareKeys: keySlice.reducer,
networks: networksSlice.reducer,
submittedTransactions: submittedTransactionsSlice.reducer,
Expand Down
39 changes: 39 additions & 0 deletions src/app/store/missing-stacks-nfts/missing-stacks-nfts.slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useMemo } from 'react';
import { useSelector } from 'react-redux';

import { type PayloadAction, createSlice } from '@reduxjs/toolkit';

import { type RootState, useAppDispatch } from '..';

export const missingDataSlice = createSlice({
name: 'missingStacksNfts',
initialState: {
stacksNfts: [] as string[],
},
reducers: {
addKnownMissingStacksNft(state, action: PayloadAction<string>) {
state.stacksNfts.push(action.payload);
},
},
});

const { addKnownMissingStacksNft } = missingDataSlice.actions;

const selectMisingStacksNfts = (state: RootState) => state.missingData;

export function useStacksNftMissingMetadata() {
const dispatch = useAppDispatch();
const missingData = useSelector(selectMisingStacksNfts);
return useMemo(
() => ({
missingNftIds: missingData.stacksNfts,
isMissingStacksNftMetadata(id: string) {
return missingData.stacksNfts.includes(id);
},
addKnownMissingStacksNft(id: string) {
dispatch(addKnownMissingStacksNft(id));
},
}),
[dispatch, missingData.stacksNfts]
);
}
1 change: 1 addition & 0 deletions src/shared/storage/redux-pesist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export const persistConfig: PersistConfig<RootState> & UntypedDeserializeOption
'ordinals',
'softwareKeys',
'ledger',
'missingData',
'networks',
'onboarding',
'settings',
Expand Down
6 changes: 6 additions & 0 deletions tests/page-object-models/onboarding.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export const testSoftwareAccountDefaultWalletState = {
ids: [],
},
},
missingData: {
stacksNfts: [],
},
networks: { ids: [], entities: {}, currentNetworkId: 'mainnet' },
ordinals: {
entities: {},
Expand Down Expand Up @@ -216,6 +219,9 @@ export function makeLedgerTestAccountWalletState(keysToInclude: SupportedBlockch
bitcoin: keysToInclude.includes('bitcoin') ? ledgerBitcoinKeysState : emptyKeysState,
stacks: keysToInclude.includes('stacks') ? ledgerStacksKeysState : emptyKeysState,
},
missingData: {
stacksNfts: [],
},
networks: { currentNetworkId: 'mainnet', entities: {}, ids: [] },
onboarding: {
hideSteps: false,
Expand Down

0 comments on commit 4b54db2

Please sign in to comment.