diff --git a/public/assets/images/bitcoin-stamp.png b/public/assets/images/bitcoin-stamp.png
new file mode 100644
index 00000000..cc129286
Binary files /dev/null and b/public/assets/images/bitcoin-stamp.png differ
diff --git a/public/assets/images/generic-error-icon.png b/public/assets/images/generic-error-icon.png
deleted file mode 100644
index 3393601d..00000000
Binary files a/public/assets/images/generic-error-icon.png and /dev/null differ
diff --git a/src/app/components/receive/receive-collectible-item.tsx b/src/app/components/receive/receive-collectible-item.tsx
new file mode 100644
index 00000000..45d4b382
--- /dev/null
+++ b/src/app/components/receive/receive-collectible-item.tsx
@@ -0,0 +1,38 @@
+import { FiCopy } from 'react-icons/fi';
+
+import { Box, Button, Flex, Stack } from '@stacks/ui';
+import { truncateMiddle } from '@stacks/ui-utils';
+
+import { Flag } from '../layout/flag';
+import { Caption } from '../typography';
+
+interface ReceiveCollectibleItemProps {
+ address: string;
+ icon: JSX.Element;
+ onCopyAddress(): void;
+ title: string;
+}
+export function ReceiveCollectibleItem({
+ address,
+ icon,
+ onCopyAddress,
+ title,
+}: ReceiveCollectibleItemProps) {
+ return (
+
+
+
+ {title}
+ {truncateMiddle(address, 6)}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/components/receive/receive-collectible.tsx b/src/app/components/receive/receive-collectible.tsx
index 9cac95ad..a9aeb291 100644
--- a/src/app/components/receive/receive-collectible.tsx
+++ b/src/app/components/receive/receive-collectible.tsx
@@ -1,9 +1,8 @@
import toast from 'react-hot-toast';
-import { FiCopy } from 'react-icons/fi';
import { useLocation, useNavigate } from 'react-router-dom';
-import { Box, Button, Flex, Stack, useClipboard } from '@stacks/ui';
-import { truncateMiddle } from '@stacks/ui-utils';
+import BitcoinStampImg from '@assets/images/bitcoin-stamp.png';
+import { Box, Stack, useClipboard } from '@stacks/ui';
import get from 'lodash.get';
import { RouteUrls } from '@shared/route-urls';
@@ -11,77 +10,68 @@ import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { OrdinalIcon } from '@app/components/icons/ordinal-icon';
-import { Flag } from '@app/components/layout/flag';
-import { Caption } from '@app/components/typography';
import { useZeroIndexTaprootAddress } from '@app/query/bitcoin/ordinals/use-zero-index-taproot-address';
+import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+import { ReceiveCollectibleItem } from './receive-collectible-item';
+
export function ReceiveCollectible() {
const analytics = useAnalytics();
const location = useLocation();
const navigate = useNavigate();
const accountIndex = get(location.state, 'accountIndex', undefined);
- const btcAddress = useZeroIndexTaprootAddress(accountIndex);
+ const btcAddressNativeSegwit = useCurrentBtcNativeSegwitAccountAddressIndexZero();
+ const btcAddressTaproot = useZeroIndexTaprootAddress(accountIndex);
// TODO: Reuse later for privacy mode
// const { isLoading, isError, data: btcAddress } = useNextFreshTaprootAddressQuery(accountIndex);
const stxAddress = useCurrentAccountStxAddressState();
+ const { onCopy: onCopyBitcoin } = useClipboard(btcAddressNativeSegwit);
const { onCopy: onCopyStacks } = useClipboard(stxAddress);
- function copyToClipboard(copyHandler: () => void) {
+ function copyBitcoinAddressToClipboard(copyHandler: () => void) {
+ void analytics.track('select_stamp_to_add_new_collectible');
+ toast.success('Copied to clipboard!');
+ copyHandler();
+ }
+
+ function copyStacksAddressToClipboard(copyHandler: () => void) {
void analytics.track('select_nft_to_add_new_collectible');
toast.success('Copied to clipboard!');
copyHandler();
}
- if (!btcAddress) return null;
+ if (!btcAddressTaproot) return null;
return (
- } spacing="base">
-
+ }
+ onCopyAddress={() => {
+ void analytics.track('select_inscription_to_add_new_collectible');
+ navigate(RouteUrls.ReceiveCollectibleOrdinal, { state: { btcAddressTaproot } });
+ }}
+ title="Ordinal inscription"
+ />
+
- Ordinal inscription
- {truncateMiddle(btcAddress, 6)}
+
-
-
-
-
-
-
-
- } spacing="base">
-
-
- Stacks NFT
- {truncateMiddle(stxAddress, 6)}
-
-
-
-
-
-
-
-
+ }
+ onCopyAddress={() => copyBitcoinAddressToClipboard(onCopyBitcoin)}
+ title="Bitcoin Stamp"
+ />
+ }
+ onCopyAddress={() => copyStacksAddressToClipboard(onCopyStacks)}
+ title="Stacks NFT"
+ />
);
}
diff --git a/src/app/features/collectibles/components/_collectible-types/collectible-image.tsx b/src/app/features/collectibles/components/_collectible-types/collectible-image.tsx
index 8db6296d..25310617 100644
--- a/src/app/features/collectibles/components/_collectible-types/collectible-image.tsx
+++ b/src/app/features/collectibles/components/_collectible-types/collectible-image.tsx
@@ -3,30 +3,30 @@ import { useState } from 'react';
import { Spinner } from '@stacks/ui';
import { figmaTheme } from '@app/common/utils/figma-theme';
-import { OrdinalMinimalIcon } from '@app/components/icons/ordinal-minimal-icon';
import { CollectibleItemLayout, CollectibleItemLayoutProps } from '../collectible-item.layout';
import { ImageUnavailable } from '../image-unavailable';
interface CollectibleImageProps extends Omit {
alt?: string;
+ icon: JSX.Element;
src: string;
}
export function CollectibleImage(props: CollectibleImageProps) {
- const { alt, src, ...rest } = props;
+ const { alt, icon, src, ...rest } = props;
const [isError, setIsError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [width, setWidth] = useState(0);
if (isError)
return (
- } {...rest}>
+
);
return (
- } {...rest}>
+
{isLoading && }
{
+ icon: JSX.Element;
content: string;
}
export function CollectibleText(props: CollectibleTextProps) {
- const { content, ...rest } = props;
+ const { content, icon, ...rest } = props;
return (
- } {...rest}>
+
{content}
);
diff --git a/src/app/features/collectibles/components/add-collectible.tsx b/src/app/features/collectibles/components/add-collectible.tsx
index 26d52130..e1bda496 100644
--- a/src/app/features/collectibles/components/add-collectible.tsx
+++ b/src/app/features/collectibles/components/add-collectible.tsx
@@ -33,7 +33,7 @@ export function AddCollectible() {
void analytics.track('select_add_new_collectible');
navigate(RouteUrls.ReceiveCollectible);
}}
- subtitle="Ordinal or Stacks NFT"
+ subtitle="Collectible"
title="Add new"
>
diff --git a/src/app/features/collectibles/components/bitcoin/inscription-text.tsx b/src/app/features/collectibles/components/bitcoin/inscription-text.tsx
index 25714992..2b90171c 100644
--- a/src/app/features/collectibles/components/bitcoin/inscription-text.tsx
+++ b/src/app/features/collectibles/components/bitcoin/inscription-text.tsx
@@ -1,6 +1,7 @@
import { Spinner } from '@stacks/ui';
import { figmaTheme } from '@app/common/utils/figma-theme';
+import { OrdinalMinimalIcon } from '@app/components/icons/ordinal-minimal-icon';
import { useTextInscriptionContentQuery } from '@app/query/bitcoin/ordinals/use-text-ordinal-content.query';
import { CollectibleText } from '../_collectible-types/collectible-text';
@@ -24,6 +25,7 @@ export function InscriptionText({
return (
}
key={inscriptionNumber}
onClickCallToAction={onClickCallToAction}
onClickSend={onClickSend}
diff --git a/src/app/features/collectibles/components/bitcoin/inscription.tsx b/src/app/features/collectibles/components/bitcoin/inscription.tsx
index 5eb76604..3f2a43cf 100644
--- a/src/app/features/collectibles/components/bitcoin/inscription.tsx
+++ b/src/app/features/collectibles/components/bitcoin/inscription.tsx
@@ -4,6 +4,7 @@ import { RouteUrls } from '@shared/route-urls';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
import { OrdinalIconFull } from '@app/components/icons/ordinal-icon-full';
+import { OrdinalMinimalIcon } from '@app/components/icons/ordinal-minimal-icon';
import { useInscription } from '@app/query/bitcoin/ordinals/inscription.hooks';
import { TaprootUtxo } from '@app/query/bitcoin/ordinals/use-taproot-address-utxos.query';
@@ -32,6 +33,7 @@ export function Inscription({ path, utxo }: InscriptionProps) {
case 'image': {
return (
}
key={inscription.title}
onClickCallToAction={() => openInNewTab(inscription.infoUrl)}
onClickSend={() => openSendInscriptionModal()}
diff --git a/src/app/features/collectibles/components/bitcoin/stamp.tsx b/src/app/features/collectibles/components/bitcoin/stamp.tsx
index 4891e77c..cb768a2a 100644
--- a/src/app/features/collectibles/components/bitcoin/stamp.tsx
+++ b/src/app/features/collectibles/components/bitcoin/stamp.tsx
@@ -1,5 +1,8 @@
+import BitcoinStampImg from '@assets/images/bitcoin-stamp.png';
+import { Box } from '@stacks/ui';
+
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
-import { Stamp as BitcoinStamp } from '@app/query/bitcoin/stamps/stamp-collection.query';
+import { Stamp as BitcoinStamp } from '@app/query/bitcoin/stamps/stamps-by-address.query';
import { CollectibleImage } from '../_collectible-types/collectible-image';
@@ -10,6 +13,11 @@ export function Stamp(props: { bitcoinStamp: BitcoinStamp }) {
return (
+
+
+ }
key={bitcoinStamp.stamp}
onClickCallToAction={() => openInNewTab(`${stampChainAssetUrl}${bitcoinStamp.stamp}`)}
src={bitcoinStamp.stamp_url}
diff --git a/src/app/features/collectibles/components/stacks/stacks-bns-name.tsx b/src/app/features/collectibles/components/stacks/stacks-bns-name.tsx
index 112d4a68..fdf0d779 100644
--- a/src/app/features/collectibles/components/stacks/stacks-bns-name.tsx
+++ b/src/app/features/collectibles/components/stacks/stacks-bns-name.tsx
@@ -1,6 +1,7 @@
import StacksNftBns from '@assets/images/stacks-nft-bns.png';
import { figmaTheme } from '@app/common/utils/figma-theme';
+import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { CollectibleItemLayout } from '../collectible-item.layout';
@@ -16,6 +17,7 @@ export function StacksBnsName(props: { bnsName: string }) {
return (
}
subtitle="Bitcoin Naming System"
title={bnsName}
>
diff --git a/src/app/features/collectibles/components/stacks/stacks-non-fungible-tokens.tsx b/src/app/features/collectibles/components/stacks/stacks-non-fungible-tokens.tsx
index a4a2ea9a..77e3f08e 100644
--- a/src/app/features/collectibles/components/stacks/stacks-non-fungible-tokens.tsx
+++ b/src/app/features/collectibles/components/stacks/stacks-non-fungible-tokens.tsx
@@ -25,7 +25,7 @@ export function StacksNonFungibleTokens({ metadata }: StacksNonFungibleTokensPro
}
+ icon={}
src={metadata.cached_image ?? ''}
subtitle="Stacks NFT"
title={metadata.name ?? ''}
diff --git a/src/app/query/bitcoin/ordinals/use-taproot-address-utxos.query.ts b/src/app/query/bitcoin/ordinals/use-taproot-address-utxos.query.ts
index b8b8f156..f48cbace 100644
--- a/src/app/query/bitcoin/ordinals/use-taproot-address-utxos.query.ts
+++ b/src/app/query/bitcoin/ordinals/use-taproot-address-utxos.query.ts
@@ -7,7 +7,6 @@ import { useBitcoinClient } from '@app/store/common/api-clients.hooks';
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
import { UtxoResponseItem } from '../bitcoin-client';
-import { useIsStampedTx } from '../stamps/use-is-stamped-tx';
import { getTaprootAddress, hasInscriptions } from './utils';
const stopSearchAfterNumberAddressesWithoutOrdinals = 20;
@@ -24,7 +23,6 @@ export interface TaprootUtxo extends UtxoResponseItem {
export function useTaprootAccountUtxosQuery() {
const network = useCurrentNetwork();
const keychain = useCurrentTaprootAccountKeychain();
- const isStamped = useIsStampedTx();
const client = useBitcoinClient();
const currentAccountIndex = useCurrentAccountIndex();
@@ -62,6 +60,6 @@ export function useTaprootAccountUtxosQuery() {
currentNumberOfAddressesWithoutOrdinals = 0;
addressIndexCounter.increment();
}
- return foundUnspentTransactions.filter(tx => !isStamped(tx.txid));
+ return foundUnspentTransactions;
});
}
diff --git a/src/app/query/bitcoin/stamps/stamp-collection.query.ts b/src/app/query/bitcoin/stamps/stamp-collection.query.ts
deleted file mode 100644
index c2106a52..00000000
--- a/src/app/query/bitcoin/stamps/stamp-collection.query.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-
-import { AppUseQueryConfig } from '@app/query/query-config';
-import { QueryPrefixes } from '@app/query/query-prefixes';
-
-export interface Stamp {
- asset: string;
- block_index: number;
- message_index: number;
- stamp: number;
- stamp_mimetype: string;
- stamp_url: string;
- timestamp: number;
- tx_hash: string;
- tx_index: number;
-}
-
-async function fetchStampCollection() {
- return (
- fetch('https://stampchain.io/stamp.json')
- .then(res => res.json())
- // Currently we only filter UTXOs for stamps. As we collect, and cache,
- // all stamps, we cache only the ID to lower the amount of storage used
- .then((allStamps: Stamp[]) => allStamps.map(s => ({ tx_hash: s.tx_hash })))
- );
-}
-
-type FetchStampCollectionResp = Awaited>;
-
-export function useStampCollectionQuery(
- options?: AppUseQueryConfig
-) {
- return useQuery({
- queryKey: [QueryPrefixes.StampCollection],
- queryFn: () => fetchStampCollection(),
- refetchOnMount: false,
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- staleTime: 1000 * 60 * 60 * 5,
- ...options,
- });
-}
diff --git a/src/app/query/bitcoin/stamps/stamps-by-address.query.ts b/src/app/query/bitcoin/stamps/stamps-by-address.query.ts
index 638bd07d..e786ac88 100644
--- a/src/app/query/bitcoin/stamps/stamps-by-address.query.ts
+++ b/src/app/query/bitcoin/stamps/stamps-by-address.query.ts
@@ -3,12 +3,17 @@ import { useQuery } from '@tanstack/react-query';
import { AppUseQueryConfig } from '@app/query/query-config';
import { QueryPrefixes } from '@app/query/query-prefixes';
-import { Stamp } from './stamp-collection.query';
-
-const stampsByAddressQueryOptions = {
- cacheTime: Infinity,
- staleTime: 15 * 60 * 1000,
-} as const;
+export interface Stamp {
+ asset: string;
+ block_index: number;
+ message_index: number;
+ stamp: number;
+ stamp_mimetype: string;
+ stamp_url: string;
+ timestamp: number;
+ tx_hash: string;
+ tx_index: number;
+}
async function fetchStampsByAddress(address: string): Promise {
return fetch(`https://stampchain.io/api/stamps?wallet_address=${address}`).then(res =>
@@ -25,7 +30,6 @@ export function useStampsByAddressQuery fetchStampsByAddress(address),
- ...stampsByAddressQueryOptions,
...options,
});
}
diff --git a/src/app/query/bitcoin/stamps/use-is-stamped-tx.ts b/src/app/query/bitcoin/stamps/use-is-stamped-tx.ts
index 5457e888..76a2b089 100644
--- a/src/app/query/bitcoin/stamps/use-is-stamped-tx.ts
+++ b/src/app/query/bitcoin/stamps/use-is-stamped-tx.ts
@@ -1,12 +1,12 @@
import { useCallback } from 'react';
-import { useStampCollectionQuery } from './stamp-collection.query';
+import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
+
+import { useStampsByAddressQuery } from './stamps-by-address.query';
export function useIsStampedTx() {
- const { data: stampCollection = [] } = useStampCollectionQuery();
+ const currentAccountBtcAddress = useCurrentBtcNativeSegwitAccountAddressIndexZero();
+ const { data: stamps = [] } = useStampsByAddressQuery(currentAccountBtcAddress);
- return useCallback(
- (txid: string) => stampCollection.some(stamp => stamp.tx_hash === txid),
- [stampCollection]
- );
+ return useCallback((txid: string) => stamps.some(stamp => stamp.tx_hash === txid), [stamps]);
}