mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-01-12 17:53:19 +08:00
refactor: ft and nft query to new client
This commit is contained in:
@@ -129,7 +129,7 @@
|
||||
"@emotion/react": "11.10.5",
|
||||
"@emotion/styled": "11.10.5",
|
||||
"@fungible-systems/zone-file": "2.0.0",
|
||||
"@hirosystems/token-metadata-api-client": "1.0.1",
|
||||
"@hirosystems/token-metadata-api-client": "1.0.3",
|
||||
"@ledgerhq/hw-transport-webusb": "6.27.10",
|
||||
"@noble/hashes": "1.2.0",
|
||||
"@noble/secp256k1": "1.7.1",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { FungibleTokenMetadata } from '@stacks/stacks-blockchain-api-types';
|
||||
|
||||
import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model';
|
||||
|
||||
import {
|
||||
@@ -7,19 +5,6 @@ import {
|
||||
isTransferableStacksFungibleTokenAsset,
|
||||
} from './stacks-crypto-asset.utils';
|
||||
|
||||
// Metadata is used here temporarily until we have the new Hiro API types
|
||||
const metadata: FungibleTokenMetadata = {
|
||||
name: '',
|
||||
symbol: '',
|
||||
token_uri: '',
|
||||
decimals: 0,
|
||||
description: '',
|
||||
image_uri: '',
|
||||
image_canonical_uri: '',
|
||||
tx_id: '',
|
||||
sender_address: '',
|
||||
};
|
||||
|
||||
describe(isFtNameLikeStx.name, () => {
|
||||
it('detect impersonating token names', () => {
|
||||
expect(isFtNameLikeStx('STX')).toBeTruthy();
|
||||
@@ -48,7 +33,7 @@ describe(isTransferableStacksFungibleTokenAsset.name, () => {
|
||||
imageCanonicalUri: '',
|
||||
symbol: 'CAT',
|
||||
};
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset, metadata)).toBeTruthy();
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('a token with no decimals is transferable', () => {
|
||||
@@ -64,7 +49,7 @@ describe(isTransferableStacksFungibleTokenAsset.name, () => {
|
||||
imageCanonicalUri: '',
|
||||
symbol: 'CAT',
|
||||
};
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset, metadata)).toBeTruthy();
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('assets missing either name, symbol or decimals may not be transferred', () => {
|
||||
@@ -74,12 +59,12 @@ describe(isTransferableStacksFungibleTokenAsset.name, () => {
|
||||
decimals: undefined,
|
||||
type: 'fungible-token',
|
||||
} as unknown as StacksFungibleTokenAsset;
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset, metadata)).toBeFalsy();
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('NFTs cannot be sent', () => {
|
||||
const asset = { type: 'non-fungible-token' } as unknown as StacksFungibleTokenAsset;
|
||||
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset, metadata)).toBeFalsy();
|
||||
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { FungibleTokenMetadata } from '@stacks/blockchain-api-client';
|
||||
|
||||
import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model';
|
||||
import { isUndefined } from '@shared/utils';
|
||||
import { isValidUrl } from '@shared/utils/validate-url';
|
||||
@@ -27,20 +25,12 @@ export function isFtNameLikeStx(name: string) {
|
||||
return ['stx', 'stack', 'stacks'].includes(convertUnicodeToAscii(name).toLocaleLowerCase());
|
||||
}
|
||||
|
||||
export function getImageCanonicalUri(imageCanonicalUri?: string, name?: string) {
|
||||
return imageCanonicalUri && isValidUrl(imageCanonicalUri) && !isFtNameLikeStx(name ?? '')
|
||||
export function getImageCanonicalUri(imageCanonicalUri: string, name: string) {
|
||||
return imageCanonicalUri && isValidUrl(imageCanonicalUri) && !isFtNameLikeStx(name)
|
||||
? imageCanonicalUri
|
||||
: '';
|
||||
}
|
||||
|
||||
// Metadata is used here temporarily until we have the new Hiro API types
|
||||
export function isTransferableStacksFungibleTokenAsset(
|
||||
asset: StacksFungibleTokenAsset
|
||||
// metadata?: FungibleTokenMetadata
|
||||
) {
|
||||
return (
|
||||
// !isUndefined(metadata) &&
|
||||
// !('error' in metadata) &&
|
||||
!isUndefined(asset.decimals) && !isUndefined(asset.name) && !isUndefined(asset.symbol)
|
||||
);
|
||||
export function isTransferableStacksFungibleTokenAsset(asset: StacksFungibleTokenAsset) {
|
||||
return !isUndefined(asset.decimals) && !isUndefined(asset.name) && !isUndefined(asset.symbol);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FiArrowDown, FiArrowUp } from 'react-icons/fi';
|
||||
|
||||
import type { AddressTransactionWithTransfers } from '@stacks/stacks-blockchain-api-types';
|
||||
|
||||
import { logger } from '@shared/logger';
|
||||
import {
|
||||
FtTransfer,
|
||||
TxTransferDetails,
|
||||
@@ -15,7 +16,8 @@ import {
|
||||
import { pullContractIdFromIdentity } from '@app/common/utils';
|
||||
import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar';
|
||||
import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item';
|
||||
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/fungible-tokens/fungible-token-metadata.query';
|
||||
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query';
|
||||
import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
|
||||
import { TxTransferIconWrapper } from './tx-transfer-icon-wrapper';
|
||||
@@ -31,6 +33,11 @@ export function FtTransferItem({ ftTransfer, parentTx }: FtTransferItemProps) {
|
||||
const currentAccount = useCurrentStacksAccount();
|
||||
const isOriginator = ftTransfer.sender === currentAccount?.address;
|
||||
|
||||
if (!(assetMetadata && isFtAsset(assetMetadata))) {
|
||||
logger.error('Token metadata not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayAmount = calculateTokenTransferAmount(
|
||||
assetMetadata?.decimals ?? 0,
|
||||
ftTransfer.amount
|
||||
@@ -39,9 +46,11 @@ export function FtTransferItem({ ftTransfer, parentTx }: FtTransferItemProps) {
|
||||
|
||||
const caption = getTxCaption(parentTx.tx) ?? '';
|
||||
const ftImageCanonicalUri =
|
||||
assetMetadata && getImageCanonicalUri(assetMetadata.imageCanonicalUri, assetMetadata.name);
|
||||
assetMetadata.image_canonical_uri &&
|
||||
assetMetadata.name &&
|
||||
getImageCanonicalUri(assetMetadata.image_canonical_uri, assetMetadata.name);
|
||||
const icon = isOriginator ? FiArrowUp : FiArrowDown;
|
||||
const title = `${assetMetadata?.name || 'Token'} Transfer`;
|
||||
const title = `${assetMetadata.name || 'Token'} Transfer`;
|
||||
const value = `${isOriginator ? '-' : ''}${displayAmount.toFormat()}`;
|
||||
const transferIcon = ftImageCanonicalUri ? (
|
||||
<StacksAssetAvatar
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { parseIfValidPunycode } from '@app/common/utils';
|
||||
import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks';
|
||||
import { useNonFungibleTokensMetadata } from '@app/query/stacks/non-fungible-tokens/non-fungible-token-metadata.hooks';
|
||||
import { useNonFungibleTokensMetadata } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks';
|
||||
|
||||
import { StacksBnsName } from './stacks-bns-name';
|
||||
import { StacksNonFungibleTokens } from './stacks-non-fungible-tokens';
|
||||
@@ -14,9 +14,10 @@ export function StacksCryptoAssets() {
|
||||
{names.map(name => (
|
||||
<StacksBnsName bnsName={parseIfValidPunycode(name)} key={name} />
|
||||
))}
|
||||
{stacksNftsMetadataResp.map(nft =>
|
||||
nft ? <StacksNonFungibleTokens key={nft.token_uri} metadata={nft.metadata} /> : null
|
||||
)}
|
||||
{stacksNftsMetadataResp.map(nft => {
|
||||
if (!nft || !nft.metadata) return null;
|
||||
return <StacksNonFungibleTokens key={nft.token_uri} metadata={nft.metadata} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Metadata as StacksNftMetadata } from '@hirosystems/token-metadata-api-client';
|
||||
import { Spinner } from '@stacks/ui';
|
||||
|
||||
import { StacksNftMetadata } from '@shared/models/stacks-nft-metadata.model';
|
||||
import { isValidUrl } from '@shared/utils/validate-url';
|
||||
|
||||
import { figmaTheme } from '@app/common/utils/figma-theme';
|
||||
@@ -20,20 +20,16 @@ const backgroundProps = {
|
||||
interface StacksNftCryptoAssetsProps {
|
||||
metadata: StacksNftMetadata;
|
||||
}
|
||||
|
||||
export function StacksNonFungibleTokens({ metadata }: StacksNftCryptoAssetsProps) {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
if (!metadata) return null;
|
||||
|
||||
const isImageAvailable = isValidUrl(metadata.cached_image);
|
||||
const isImageAvailable = metadata.cached_image && isValidUrl(metadata.cached_image);
|
||||
|
||||
return (
|
||||
<CollectibleItemLayout
|
||||
backgroundElementProps={backgroundProps}
|
||||
subtitle="Stacks NFT"
|
||||
title={metadata.name}
|
||||
title={metadata.name ?? ''}
|
||||
collectibleTypeIcon={<StxAvatar size="30px" />}
|
||||
>
|
||||
{isError || !isImageAvailable ? (
|
||||
|
||||
@@ -5,8 +5,8 @@ import BigNumber from 'bignumber.js';
|
||||
import { SuggestedFirstStepStatus, SuggestedFirstSteps } from '@shared/models/onboarding-types';
|
||||
|
||||
import { useGetAnchoredAccountBalanceListQuery } from '@app/query/stacks/balance/stx-balance.query';
|
||||
import { useAllAccountsNonFungibleTokenHoldingsTotal } from '@app/query/stacks/non-fungible-tokens/non-fungible-token-holdings.hooks';
|
||||
import { useGetNonFungibleTokenHoldingsQuery } from '@app/query/stacks/non-fungible-tokens/non-fungible-token-holdings.query';
|
||||
import { useAllAccountsNonFungibleTokenHoldingsTotal } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.hooks';
|
||||
import { useGetNonFungibleTokenHoldingsQuery } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.query';
|
||||
import {
|
||||
useCurrentStacksAccount,
|
||||
useStacksAccounts,
|
||||
|
||||
@@ -20,7 +20,7 @@ export function CryptoAssetListItem(props: CryptoAssetListItemProps) {
|
||||
|
||||
function navigateToSendForm(state?: object) {
|
||||
if (blockchain === 'bitcoin' && !isBitcoinSendEnabled) navigate(RouteUrls.SendBtcDisabled);
|
||||
navigate(`${RouteUrls.SendCryptoAsset}/${asset.symbol.toLowerCase()}`, { state });
|
||||
navigate(`${RouteUrls.SendCryptoAsset}/${asset.symbol?.toLowerCase()}`, { state });
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
@@ -33,7 +33,10 @@ function FungiblePostConditionItemSuspense(
|
||||
const pendingTransaction = useTransactionRequestState();
|
||||
// Use token meta data if available
|
||||
const asset = useAssetFromFungiblePostCondition(pc);
|
||||
const imageCanonicalUri = asset && getImageCanonicalUri(asset.imageCanonicalUri, asset.name);
|
||||
const imageCanonicalUri =
|
||||
asset?.image_canonical_uri &&
|
||||
asset.name &&
|
||||
getImageCanonicalUri(asset.image_canonical_uri, asset.name);
|
||||
|
||||
const title = getPostConditionTitle(pc);
|
||||
const iconString = imageCanonicalUri ?? getIconStringFromPostCondition(pc);
|
||||
|
||||
@@ -5,7 +5,8 @@ import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asse
|
||||
import { formatContractId, getFullyQualifiedStacksAssetName } from '@app/common/utils';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
|
||||
import { useGetFungibleTokenMetadataListQuery } from '../fungible-tokens/fungible-token-metadata.query';
|
||||
import { useGetFungibleTokenMetadataListQuery } from '../tokens/fungible-tokens/fungible-token-metadata.query';
|
||||
import { isFtAsset } from '../tokens/token-metadata.utils';
|
||||
import {
|
||||
addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance,
|
||||
convertFtBalancesToStacksFungibleTokenAssetBalanceType,
|
||||
@@ -61,7 +62,7 @@ export function useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(address:
|
||||
() =>
|
||||
initializedAssetBalances.map((assetBalance, i) => {
|
||||
const metadata = ftAssetsMetadata[i].data;
|
||||
if (!metadata) return assetBalance;
|
||||
if (!(metadata && isFtAsset(metadata))) return assetBalance;
|
||||
return addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
|
||||
assetBalance,
|
||||
metadata
|
||||
@@ -87,7 +88,7 @@ function useStacksFungibleTokenAssetBalancesUnanchoredWithMetadata(
|
||||
() =>
|
||||
initializedAssetBalances.map((assetBalance, i) => {
|
||||
const metadata = ftAssetsMetadata[i].data;
|
||||
if (!metadata) return assetBalance;
|
||||
if (!(metadata && isFtAsset(metadata))) return assetBalance;
|
||||
return addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
|
||||
assetBalance,
|
||||
metadata
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FungibleTokenMetadata } from '@stacks/stacks-blockchain-api-types';
|
||||
import { FtMetadataResponse } from '@hirosystems/token-metadata-api-client';
|
||||
import { getAssetStringParts } from '@stacks/ui-utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
@@ -99,7 +99,7 @@ export function convertNftBalancesToStacksNonFungibleTokenAssetBalanceType(
|
||||
|
||||
export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
|
||||
assetBalance: StacksFungibleTokenAssetBalance,
|
||||
metadata: FungibleTokenMetadata
|
||||
metadata: FtMetadataResponse
|
||||
) {
|
||||
return {
|
||||
...assetBalance,
|
||||
@@ -111,11 +111,11 @@ export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
|
||||
asset: {
|
||||
...assetBalance.asset,
|
||||
canTransfer: isTransferableStacksFungibleTokenAsset(assetBalance.asset),
|
||||
decimals: metadata.decimals,
|
||||
decimals: metadata.decimals ?? 0,
|
||||
hasMemo: isTransferableStacksFungibleTokenAsset(assetBalance.asset),
|
||||
imageCanonicalUri: metadata.image_canonical_uri,
|
||||
name: metadata.name,
|
||||
symbol: metadata.symbol,
|
||||
imageCanonicalUri: metadata.image_canonical_uri ?? '',
|
||||
name: metadata.name ?? '',
|
||||
symbol: metadata.symbol ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { useGetNonFungibleTokenMetadataListQuery } from './non-fungible-token-metadata.query';
|
||||
|
||||
export function useNonFungibleTokensMetadata() {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery();
|
||||
return respList.map(resp => resp.data).filter(data => data?.token_uri);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { FtMetadataResponse } from '@hirosystems/token-metadata-api-client';
|
||||
import { UseQueryResult, useQueries, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useTokenMetadataClient } from '@app/store/common/api-clients.hooks';
|
||||
import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
|
||||
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../rate-limiter';
|
||||
import { TokenMetadataClient } from '../token-metadata-client';
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
import { TokenMetadataClient } from '../../token-metadata-client';
|
||||
import { FtAssetResponse } from '../token-metadata.utils';
|
||||
|
||||
const staleTime = 12 * 60 * 60 * 1000;
|
||||
|
||||
@@ -29,7 +29,7 @@ function fetchFungibleTokenMetadata(client: TokenMetadataClient, limiter: RateLi
|
||||
|
||||
export function useGetFungibleTokenMetadataQuery(
|
||||
principal: string
|
||||
): UseQueryResult<FtMetadataResponse> {
|
||||
): UseQueryResult<FtAssetResponse> {
|
||||
const client = useTokenMetadataClient();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
@@ -43,7 +43,7 @@ export function useGetFungibleTokenMetadataQuery(
|
||||
|
||||
export function useGetFungibleTokenMetadataListQuery(
|
||||
principals: string[]
|
||||
): UseQueryResult<FtMetadataResponse>[] {
|
||||
): UseQueryResult<FtAssetResponse>[] {
|
||||
const client = useTokenMetadataClient();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
@@ -7,7 +7,7 @@ import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-acco
|
||||
import { useStacksClientUnanchored } from '@app/store/common/api-clients.hooks';
|
||||
import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
|
||||
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../rate-limiter';
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
|
||||
const staleTime = 15 * 60 * 1000; // 15 min
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { isNftAsset } from '../token-metadata.utils';
|
||||
import { useGetNonFungibleTokenMetadataListQuery } from './non-fungible-token-metadata.query';
|
||||
|
||||
export function useNonFungibleTokensMetadata() {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery();
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
respList
|
||||
.filter(resp => resp.status === 'success')
|
||||
.map(resp => {
|
||||
if (resp.data && isNftAsset(resp.data)) return resp.data;
|
||||
return;
|
||||
}),
|
||||
[respList]
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { hexToCV } from '@stacks/transactions';
|
||||
import { useQueries } from '@tanstack/react-query';
|
||||
import { UseQueryResult, useQueries } from '@tanstack/react-query';
|
||||
|
||||
import { StacksNftMetadataResponse } from '@shared/models/stacks-nft-metadata.model';
|
||||
|
||||
import { fetcher } from '@app/common/api/wrapped-fetch';
|
||||
import { pullContractIdFromIdentity } from '@app/common/utils';
|
||||
import { QueryPrefixes } from '@app/query/query-prefixes';
|
||||
import { useTokenMetadataClient } from '@app/store/common/api-clients.hooks';
|
||||
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../rate-limiter';
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
import { TokenMetadataClient } from '../../token-metadata-client';
|
||||
import { NftAssetResponse } from '../token-metadata.utils';
|
||||
import { useAccountNonFungibleTokenHoldings } from './non-fungible-token-holdings.hooks';
|
||||
|
||||
const queryOptions = {
|
||||
@@ -21,18 +21,17 @@ function getTokenId(hex: string) {
|
||||
return clarityValue.type === 1 ? Number(clarityValue.value) : 0;
|
||||
}
|
||||
|
||||
function fetchNonFungibleTokenMetadata(limiter: RateLimiter) {
|
||||
return async (principal: string, tokenId?: number) => {
|
||||
function fetchNonFungibleTokenMetadata(client: TokenMetadataClient, limiter: RateLimiter) {
|
||||
return (principal: string, tokenId: number) => async () => {
|
||||
await limiter.removeTokens(1);
|
||||
const resp = await fetcher(`https://api.hiro.so/metadata/v1/nft/${principal}/${tokenId}`);
|
||||
const data = await resp.json();
|
||||
return data as Promise<StacksNftMetadataResponse>;
|
||||
return client.tokensApi.getNftMetadata(principal, tokenId);
|
||||
};
|
||||
}
|
||||
|
||||
export function useGetNonFungibleTokenMetadataListQuery() {
|
||||
const nftHoldings = useAccountNonFungibleTokenHoldings();
|
||||
export function useGetNonFungibleTokenMetadataListQuery(): UseQueryResult<NftAssetResponse>[] {
|
||||
const client = useTokenMetadataClient();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
const nftHoldings = useAccountNonFungibleTokenHoldings();
|
||||
|
||||
return useQueries({
|
||||
queries: nftHoldings.map(nft => {
|
||||
@@ -42,7 +41,7 @@ export function useGetNonFungibleTokenMetadataListQuery() {
|
||||
return {
|
||||
enabled: !!tokenId,
|
||||
queryKey: [QueryPrefixes.GetNftMetadata, principal, tokenId],
|
||||
queryFn: () => fetchNonFungibleTokenMetadata(limiter)(principal, tokenId),
|
||||
queryFn: fetchNonFungibleTokenMetadata(client, limiter)(principal, tokenId),
|
||||
...queryOptions,
|
||||
};
|
||||
}),
|
||||
22
src/app/query/stacks/tokens/token-metadata.utils.ts
Normal file
22
src/app/query/stacks/tokens/token-metadata.utils.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
FtMetadataResponse,
|
||||
NftMetadataResponse,
|
||||
TokenNotFoundResponse,
|
||||
} from '@hirosystems/token-metadata-api-client';
|
||||
|
||||
export type FtAssetResponse = FtMetadataResponse | TokenNotFoundResponse;
|
||||
export type NftAssetResponse = NftMetadataResponse | TokenNotFoundResponse;
|
||||
|
||||
function isAssetMetadataNotFoundResponse(
|
||||
resp: FtAssetResponse | NftAssetResponse
|
||||
): resp is TokenNotFoundResponse {
|
||||
return 'error' in resp;
|
||||
}
|
||||
|
||||
export function isFtAsset(resp: FtAssetResponse): resp is FtMetadataResponse {
|
||||
return !isAssetMetadataNotFoundResponse(resp);
|
||||
}
|
||||
|
||||
export function isNftAsset(resp: NftAssetResponse): resp is NftMetadataResponse {
|
||||
return !isAssetMetadataNotFoundResponse(resp);
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import { ChainID } from '@stacks/transactions';
|
||||
import {
|
||||
BITCOIN_API_BASE_URL_MAINNET,
|
||||
BITCOIN_API_BASE_URL_TESTNET,
|
||||
TOKEN_METADATA_API_BASE_URL_MAINNET,
|
||||
TOKEN_METADATA_API_BASE_URL_TESTNET,
|
||||
HIRO_API_BASE_URL_MAINNET,
|
||||
HIRO_API_BASE_URL_TESTNET,
|
||||
} from '@shared/constants';
|
||||
|
||||
import { whenStxChainId } from '@app/common/utils';
|
||||
@@ -56,8 +56,8 @@ export function useTokenMetadataClient() {
|
||||
const network = useCurrentNetworkState();
|
||||
|
||||
const basePath = whenStxChainId(network.chain.stacks.chainId)({
|
||||
[ChainID.Mainnet]: TOKEN_METADATA_API_BASE_URL_MAINNET,
|
||||
[ChainID.Testnet]: TOKEN_METADATA_API_BASE_URL_TESTNET,
|
||||
[ChainID.Mainnet]: HIRO_API_BASE_URL_MAINNET,
|
||||
[ChainID.Testnet]: HIRO_API_BASE_URL_TESTNET,
|
||||
});
|
||||
|
||||
const config = createTokenMetadataConfig(basePath);
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
getPostCondition,
|
||||
handlePostConditions,
|
||||
} from '@app/common/transactions/stacks/post-condition.utils';
|
||||
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/fungible-tokens/fungible-token-metadata.query';
|
||||
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query';
|
||||
import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils';
|
||||
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
|
||||
import { useTransactionRequestState } from './requests.hooks';
|
||||
@@ -42,5 +43,6 @@ export function useAssetFromFungiblePostCondition(pc: FungiblePostCondition) {
|
||||
const contractName = pc.assetInfo.contractName.content;
|
||||
const contractId = `${contractAddress}.${contractName}`;
|
||||
const { data: asset } = useGetFungibleTokenMetadataQuery(contractId);
|
||||
return !asset || 'error' in asset ? undefined : asset;
|
||||
|
||||
return !(asset && isFtAsset(asset)) ? undefined : asset;
|
||||
}
|
||||
|
||||
@@ -35,11 +35,6 @@ export enum DefaultNetworkConfigurationIds {
|
||||
|
||||
export type DefaultNetworkConfigurations = keyof typeof DefaultNetworkConfigurationIds;
|
||||
|
||||
export const chainIdToNetworkModeMap: Record<ChainID, NetworkModes> = {
|
||||
[ChainID.Mainnet]: 'mainnet',
|
||||
[ChainID.Testnet]: 'testnet',
|
||||
};
|
||||
|
||||
interface BaseChainConfig {
|
||||
blockchain: Blockchains;
|
||||
}
|
||||
@@ -75,8 +70,8 @@ export type NetworkModes = keyof typeof DefaultNetworkModes;
|
||||
const DEFAULT_SERVER_MAINNET = 'https://stacks-node-api.stacks.co';
|
||||
export const DEFAULT_SERVER_TESTNET = 'https://stacks-node-api.testnet.stacks.co';
|
||||
|
||||
export const TOKEN_METADATA_API_BASE_URL_MAINNET = 'https://api.hiro.so';
|
||||
export const TOKEN_METADATA_API_BASE_URL_TESTNET = 'https://api.testnet.hiro.so';
|
||||
export const HIRO_API_BASE_URL_MAINNET = 'https://api.hiro.so';
|
||||
export const HIRO_API_BASE_URL_TESTNET = 'https://api.testnet.hiro.so';
|
||||
|
||||
export const BITCOIN_API_BASE_URL_MAINNET = 'https://blockstream.info/api';
|
||||
export const BITCOIN_API_BASE_URL_TESTNET = 'https://blockstream.info/testnet/api';
|
||||
|
||||
@@ -18,11 +18,11 @@ export interface StacksFungibleTokenAsset {
|
||||
contractAddress: string;
|
||||
contractAssetName: string;
|
||||
contractName: string;
|
||||
decimals?: number;
|
||||
decimals: number;
|
||||
hasMemo: boolean;
|
||||
imageCanonicalUri?: string;
|
||||
name?: string;
|
||||
symbol?: string;
|
||||
imageCanonicalUri: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
export interface StacksNonFungibleTokenAsset {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
interface NftMetadataAttribute {
|
||||
trait_type: string;
|
||||
display_type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface NftMetadataProperty {
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface StacksNftMetadata {
|
||||
sip: number;
|
||||
name: string;
|
||||
description: string;
|
||||
image: string;
|
||||
cached_image: string;
|
||||
attributes: NftMetadataAttribute[];
|
||||
properties: { [key: string]: NftMetadataProperty };
|
||||
}
|
||||
|
||||
export interface StacksNftMetadataResponse {
|
||||
token_uri: string;
|
||||
metadata: StacksNftMetadata;
|
||||
}
|
||||
@@ -1241,10 +1241,10 @@
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@hirosystems/token-metadata-api-client@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@hirosystems/token-metadata-api-client/-/token-metadata-api-client-1.0.1.tgz#b7dd8cdd7c17d47cdde8fca2a53f7af5903c6eb9"
|
||||
integrity sha512-JN2PVx4K0FzBjqOxxJP1x3+YxAB4E1RFvFepgknFQJePIa2RVxFnVyJD3fck7+s8WW27RuEDWQfpOxbspzBbNw==
|
||||
"@hirosystems/token-metadata-api-client@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@hirosystems/token-metadata-api-client/-/token-metadata-api-client-1.0.3.tgz#3d941449d32f2e73848c76dfc2b39784229b5a13"
|
||||
integrity sha512-FGQAsuEELIYo/boOy9EScVtrcud+8BAcSuPM+BDN6PRUZNqLXHlR7G5ufB1oHA1RnNioP/Mzc+yQkzatD6ak2A==
|
||||
dependencies:
|
||||
isomorphic-fetch "^3.0.0"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user