Skip to content

Commit

Permalink
Merge pull request #5969 from leather-io/release/bns-v2-send
Browse files Browse the repository at this point in the history
Release/bns v2 send
  • Loading branch information
alter-eggo authored Nov 15, 2024
2 parents eecfb2a + 62b3813 commit e7a2817
Show file tree
Hide file tree
Showing 21 changed files with 1,073 additions and 995 deletions.
4 changes: 2 additions & 2 deletions .github/actions/provision/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ runs:
node-version: 18

- name: Set up pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4

- name: Get pnpm store directory
shell: bash
Expand All @@ -24,7 +24,7 @@ runs:
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: nick-fields/retry@v2
- uses: nick-fields/retry@v3
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
with:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@
"@leather.io/constants": "0.13.1",
"@leather.io/crypto": "1.6.8",
"@leather.io/models": "0.19.0",
"@leather.io/query": "2.20.0",
"@leather.io/query": "2.22.0",
"@leather.io/stacks": "1.3.1",
"@leather.io/tokens": "0.10.0",
"@leather.io/ui": "1.32.2",
"@leather.io/ui": "1.33.0",
"@leather.io/utils": "0.17.0",
"@ledgerhq/hw-transport-webusb": "6.27.19",
"@noble/hashes": "1.5.0",
Expand Down
1,872 changes: 947 additions & 925 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/app/common/hooks/account/use-account-names.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';

import { bitcoinNetworkModeToCoreNetworkMode } from '@leather.io/bitcoin';
import { createGetBnsNamesOwnedByAddressQueryOptions } from '@leather.io/query';
import { createGetBnsNamesOwnedByAddressQueryOptions, useBnsV2Client } from '@leather.io/query';
import { isUndefined } from '@leather.io/utils';

import { parseIfValidPunycode } from '@app/common/utils';
Expand All @@ -18,9 +18,10 @@ export function useCurrentAccountDisplayName() {
const address = useCurrentStacksAccountAddress();
const { chain } = useCurrentNetworkState();
const network = bitcoinNetworkModeToCoreNetworkMode(chain.bitcoin.mode);
const client = useBnsV2Client();

return useQuery({
...createGetBnsNamesOwnedByAddressQueryOptions({ address, network }),
...createGetBnsNamesOwnedByAddressQueryOptions({ address, network, client }),
select: resp => {
if (isUndefined(account?.index) && (!account || typeof account?.index !== 'number'))
return 'Account';
Expand All @@ -34,11 +35,12 @@ export function useCurrentAccountDisplayName() {
export function useAccountDisplayName({ address, index }: { index: number; address: string }) {
const { chain } = useCurrentNetworkState();
const network = bitcoinNetworkModeToCoreNetworkMode(chain.bitcoin.mode);

const client = useBnsV2Client();
const query = useQuery({
...createGetBnsNamesOwnedByAddressQueryOptions({
address,
network,
client,
}),
select: resp => {
const names = resp.names ?? [];
Expand Down
9 changes: 6 additions & 3 deletions src/app/common/hooks/use-key-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMemo } from 'react';

import { generateSecretKey } from '@stacks/wallet-sdk';

import { useBitcoinClient } from '@leather.io/query';
import { useBitcoinClient, useBnsV2Client } from '@leather.io/query';

import { logger } from '@shared/logger';
import { InternalMethods } from '@shared/message-types';
Expand All @@ -29,11 +29,14 @@ export function useKeyActions() {
const defaultKeyDetails = useCurrentKeyDetails();
const btcClient = useBitcoinClient();
const stxClient = useStacksClient();
const bnsV2Client = useBnsV2Client();

return useMemo(
() => ({
async setPassword(password: string) {
return dispatch(keyActions.setWalletEncryptionPassword({ password, stxClient, btcClient }));
return dispatch(
keyActions.setWalletEncryptionPassword({ password, stxClient, btcClient, bnsV2Client })
);
},

generateWalletKey() {
Expand Down Expand Up @@ -76,6 +79,6 @@ export function useKeyActions() {
return dispatch(inMemoryKeyActions.lockWallet());
},
}),
[btcClient, defaultKeyDetails, dispatch, stxClient]
[bnsV2Client, btcClient, defaultKeyDetails, dispatch, stxClient]
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function CollectibleItemLayout({
}: CollectibleItemLayoutProps) {
const [isHovered, bind] = useHover();

const { ref, inView } = useInView();
const { ref, inView } = useInView({ triggerOnce: true });

return (
<Box
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useIsFetching } from '@tanstack/react-query';

import { BitcoinQueryPrefixes, StacksQueryPrefixes } from '@leather.io/query';
import { BitcoinQueryPrefixes, BnsV2QueryPrefixes, StacksQueryPrefixes } from '@leather.io/query';
import { sumNumbers } from '@leather.io/utils';

function areAnyQueriesFetching(...args: number[]) {
Expand All @@ -16,7 +16,7 @@ export function useIsFetchingCollectiblesRelatedQuery() {
const n5 = useIsFetching({ queryKey: [BitcoinQueryPrefixes.GetInscriptions] });

// BNS
const n6 = useIsFetching({ queryKey: [StacksQueryPrefixes.GetBnsNamesByAddress] });
const n6 = useIsFetching({ queryKey: [BnsV2QueryPrefixes.GetBnsNamesByAddress] });

// NFTs
const n7 = useIsFetching({ queryKey: [StacksQueryPrefixes.GetNftMetadata] });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
import { useCallback } from 'react';

import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors';
import { HStack, styled } from 'leather-styles/jsx';
import { motion } from 'framer-motion';
import { HStack } from 'leather-styles/jsx';

import { CopyIcon } from '@leather.io/ui';
import { AddressDisplayer, CopyIcon } from '@leather.io/ui';

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

import { useClipboard } from '@app/common/hooks/use-copy-to-clipboard';
import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip';
import { useToast } from '@app/features/toasts/use-toast';

interface RecipientAddressDisplayerProps {
address: string;
}
export function RecipientAddressDisplayer({ address }: RecipientAddressDisplayerProps) {
const { onCopy, hasCopied } = useClipboard(address);
const { onCopy } = useClipboard(address);
const toast = useToast();

function copyToClipboard() {
toast.success('Copied to clipboard!');

const copyToClipboard = useCallback(() => {
void analytics.track('copy_recipient_bns_address_to_clipboard');
onCopy();
}, [onCopy]);
}

return (
<HStack mb="space.04" width="100%" px="space.04" mt="space.02">
<styled.span
textStyle="caption.01"
data-testid={SendCryptoAssetSelectors.RecipientBnsAddressLabel}
>
{address}
</styled.span>
{/** TODO: We need to persist the tooltip after it is clicked.
Current implementation of radix-ui tooltip doesn't allow that, ref: https://github.com/radix-ui/primitives/issues/2029 */}
<BasicTooltip label={hasCopied ? 'Copied!' : 'Copy address'} side="right" asChild>
<styled.button
_hover={{ cursor: 'pointer' }}
data-testid={SendCryptoAssetSelectors.RecipientBnsAddressCopyToClipboard}
onClick={copyToClipboard}
<HStack mb="space.04" width="100%" mt="space.02">
<HStack onClick={copyToClipboard} cursor="pointer">
<AddressDisplayer
data-testid={SendCryptoAssetSelectors.RecipientBnsAddressLabel}
address={address}
/>

<motion.button
type="button"
whileTap={{ scale: 0.85 }}
data-testid={SendCryptoAssetSelectors.RecipientBnsAddressCopyToClipboard}
>
<CopyIcon />
</styled.button>
</BasicTooltip>
</motion.button>
</HStack>
</HStack>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@ import { useCallback, useState } from 'react';

import { useFormikContext } from 'formik';

import { type StacksClient, useStacksClient } from '@leather.io/query';
import { type BnsV2Client, useBnsV2Client } from '@leather.io/query';

import { FormErrorMessages } from '@shared/error-messages';
import { logger } from '@shared/logger';
import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model';

import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';

// Handles validating the BNS name lookup
export function useRecipientBnsName() {
const { setFieldError, setFieldValue, values } = useFormikContext<
BitcoinSendFormValues | StacksSendFormValues
>();
const [bnsAddress, setBnsAddress] = useState('');
const currentNetwork = useCurrentNetworkState();
const client = useStacksClient();

const currentStacksAddress = useCurrentStacksAccountAddress();
const client = useBnsV2Client();

const getBnsAddressAndValidate = useCallback(
async (
fetchFn: (client: StacksClient, name: string, isTestnet?: boolean) => Promise<string | null>
fetchFn: (client: BnsV2Client, name: string, isTestnet?: boolean) => Promise<string | null>
) => {
setBnsAddress('');
if (!values.recipientBnsName) return;

try {
const owner = await fetchFn(client, values.recipientBnsName, currentNetwork.isTestnet);
const owner = await fetchFn(client, values.recipientBnsName);

if (owner) {
if (owner === currentStacksAddress) {
setFieldError('recipientBnsName', FormErrorMessages.SameAddress);
return;
}

setBnsAddress(owner);
setFieldError('recipient', undefined);
await setFieldValue('recipient', owner);
Expand All @@ -40,7 +47,7 @@ export function useRecipientBnsName() {
logger.error('Error fetching bns address', e);
}
},
[client, currentNetwork.isTestnet, setFieldError, setFieldValue, values.recipientBnsName]
[client, currentStacksAddress, setFieldError, setFieldValue, values.recipientBnsName]
);

return { bnsAddress, getBnsAddressAndValidate, setBnsAddress };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';

import { useFormikContext } from 'formik';

import type { StacksClient } from '@leather.io/query';
import type { BnsV2Client } from '@leather.io/query';

import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model';

Expand All @@ -12,7 +12,7 @@ import { RecipientAddressDisplayer } from './components/recipient-address-displa
import { useRecipientBnsName } from './hooks/use-recipient-bns-name';

interface RecipientBnsNameTypeFieldProps {
fetchFn(client: StacksClient, name: string, isTestnet?: boolean): Promise<string | null>;
fetchFn(client: BnsV2Client, name: string, isTestnet?: boolean): Promise<string | null>;
topInputOverlay: React.JSX.Element;
rightLabel: React.JSX.Element;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors';
import { styled } from 'leather-styles/jsx';

import { ChevronDownIcon, DropdownMenu, Flag } from '@leather.io/ui';

Expand Down Expand Up @@ -37,7 +38,7 @@ export function RecipientIdentifierTypeDropdown(props: RecipientIdentifierTypeDr
onSelect={() => onSelectRecipientIdentifierType(type.key)}
data-testid={`recipient-select-field-${type.key}`}
>
{type.label}
<styled.span textStyle="label.03">{type.label}</styled.span>
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fetchNameOwner } from '@leather.io/query';
import { fetchStacksNameOwner } from '@leather.io/query';

import { RecipientField } from '../../../components/recipient-fields/recipient-field';

export function StacksRecipientField() {
return <RecipientField bnsLookupFn={fetchNameOwner} />;
return <RecipientField bnsLookupFn={fetchStacksNameOwner} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export function BtcSendForm() {
<Button
aria-busy={props.isValidating}
data-testid={SendCryptoAssetSelectors.PreviewSendTxBtn}
onClick={() => props.handleSubmit()}
type="submit"
>
Continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import * as yup from 'yup';

import { bitcoinNetworkModeToCoreNetworkMode } from '@leather.io/bitcoin';

import { FormErrorMessages } from '@shared/error-messages';
import { btcAddressNetworkValidator, btcAddressValidator } from '@shared/forms/address-validators';
import {
btcAddressNetworkValidator,
btcAddressValidator,
nonEmptyStringValidator,
} from '@shared/forms/address-validators';
import { BitcoinSendFormValues } from '@shared/models/form.model';

import { formatPrecisionError } from '@app/common/error-formatters';
Expand Down Expand Up @@ -70,9 +73,7 @@ export function useBtcSendForm() {
utxos,
})
),
recipient: yup
.string()
.defined(FormErrorMessages.AddressRequired)
recipient: nonEmptyStringValidator()
.concat(btcAddressValidator())
.concat(btcAddressNetworkValidator(currentNetwork.chain.bitcoin.mode))
.concat(notCurrentAddressValidator(nativeSegwitSigner.address || ''))
Expand All @@ -88,7 +89,7 @@ export function useBtcSendForm() {
values: BitcoinSendFormValues,
formikHelpers: FormikHelpers<BitcoinSendFormValues>
) {
// Validate and check high fee warning firsts
// Validate and check high fee warning first
await formikHelpers.validateForm();
sendFormNavigate.toChooseTransactionFee(isSendingMax, utxos, values);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export function StacksCommonSendForm({
<Button
aria-busy={props.isValidating}
data-testid={SendCryptoAssetSelectors.PreviewSendTxBtn}
onClick={() => props.handleSubmit()}
type="submit"
fullWidth
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,12 @@ export function useSendFormNavigate() {

return useMemo(
() => ({
backToSendForm(state: any) {
return navigate('../', { relative: 'path', replace: true, state });
},
toChooseTransactionFee(
isSendingMax: boolean,
utxos: UtxoResponseItem[],
values: BitcoinSendFormValues
) {
return navigate(RouteUrls.SendBtcChooseFee, {
replace: true,
state: {
isSendingMax,
utxos,
Expand All @@ -72,7 +68,6 @@ export function useSendFormNavigate() {
},
toConfirmAndSignStxTransaction(tx: StacksTransaction, showFeeChangeWarning: boolean) {
return navigate(RouteUrls.SendStxConfirmation, {
replace: true,
state: {
tx: bytesToHex(tx.serialize()),
showFeeChangeWarning,
Expand Down
Loading

0 comments on commit e7a2817

Please sign in to comment.