From f3f807b205c2d3da995f97cc413d42f13d0ed67e Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Thu, 14 Nov 2024 11:10:28 -0500 Subject: [PATCH] feat: migrate stacks generate txs, closes LEA-1732 --- apps/mobile/package.json | 2 + .../transactions/stacks-transactions.hooks.ts | 39 ++ .../accounts/account-list/account-list.tsx | 2 +- .../balances/bitcoin/bitcoin-balance.tsx | 5 +- .../balances/stacks/stacks-balance.tsx | 5 +- .../src/features/balances/token-balance.tsx | 21 +- .../send/{utils.ts => send-form.utils.ts} | 2 +- .../components/send-form-amount-field.tsx | 7 - .../send-form/components/send-form-asset.tsx | 10 +- .../send-form/components/send-form-button.tsx | 9 +- .../send-form/components/send-form-memo.tsx | 2 +- .../components/send-form-recipient.tsx | 2 +- .../send-form/hooks/use-send-form-btc.tsx | 24 ++ .../send-form/hooks/use-send-form-stx.tsx | 57 +++ .../loaders/send-form-btc-loader.tsx | 29 ++ .../loaders/send-form-stx-loader.tsx | 32 ++ .../providers/send-form-btc-provider.tsx | 41 ++ .../providers/send-form-stx-provider.tsx | 55 +++ .../send-form/schemas/send-form-stx.schema.ts | 2 + .../send/send-form/send-form-context.tsx | 3 + .../src/features/send/send-form/send-form.tsx | 29 +- .../send/send-form/sheets/recipient-sheet.tsx | 7 +- .../features/send/send-sheet-navigator.tsx | 2 +- .../send/send-sheets/select-account-sheet.tsx | 2 +- .../send/send-sheets/select-asset-sheet.tsx | 28 +- .../send/send-sheets/send-form-btc-sheet.tsx | 52 +-- .../send/send-sheets/send-form-stx-sheet.tsx | 56 +-- apps/mobile/src/store/keychains/keychains.ts | 19 + .../src/store/settings/settings.read.ts | 22 ++ packages/models/src/types.utils.ts | 7 + packages/stacks/package.json | 9 +- packages/stacks/src/index.ts | 1 + packages/stacks/src/stacks.utils.spec.ts | 62 +++ packages/stacks/src/stacks.utils.ts | 64 +++- .../generate-unsigned-transaction.spec.ts | 101 +++++ .../generate-unsigned-transaction.ts | 77 ++++ packages/stacks/src/transactions/index.ts | 3 + .../transactions/post-condition.utils.spec.ts | 59 +++ .../src/transactions/post-condition.utils.ts | 23 ++ .../src/transactions/transaction.mocks.ts | 31 ++ .../src/transactions/transaction.types.ts | 52 +++ .../item-layout/item-layout.native.tsx | 2 +- packages/utils/src/money/calculate-money.ts | 1 - packages/utils/src/money/format-money.spec.ts | 35 ++ packages/utils/src/money/format-money.ts | 14 + pnpm-lock.yaml | 358 +++++++++--------- 46 files changed, 1128 insertions(+), 337 deletions(-) create mode 100644 apps/mobile/src/common/transactions/stacks-transactions.hooks.ts rename apps/mobile/src/features/send/{utils.ts => send-form.utils.ts} (92%) create mode 100644 apps/mobile/src/features/send/send-form/hooks/use-send-form-btc.tsx create mode 100644 apps/mobile/src/features/send/send-form/hooks/use-send-form-stx.tsx create mode 100644 apps/mobile/src/features/send/send-form/loaders/send-form-btc-loader.tsx create mode 100644 apps/mobile/src/features/send/send-form/loaders/send-form-stx-loader.tsx create mode 100644 apps/mobile/src/features/send/send-form/providers/send-form-btc-provider.tsx create mode 100644 apps/mobile/src/features/send/send-form/providers/send-form-stx-provider.tsx create mode 100644 packages/stacks/src/transactions/generate-unsigned-transaction.spec.ts create mode 100644 packages/stacks/src/transactions/generate-unsigned-transaction.ts create mode 100644 packages/stacks/src/transactions/index.ts create mode 100644 packages/stacks/src/transactions/post-condition.utils.spec.ts create mode 100644 packages/stacks/src/transactions/post-condition.utils.ts create mode 100644 packages/stacks/src/transactions/transaction.mocks.ts create mode 100644 packages/stacks/src/transactions/transaction.types.ts create mode 100644 packages/utils/src/money/format-money.spec.ts diff --git a/apps/mobile/package.json b/apps/mobile/package.json index d75501b0a..a27c61e57 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -68,6 +68,7 @@ "@segment/sovran-react-native": "1.1.2", "@shopify/restyle": "2.4.2", "@stacks/common": "6.13.0", + "@stacks/network": "6.13.0", "@stacks/stacks-blockchain-api-types": "7.8.2", "@stacks/transactions": "6.17.0", "@stacks/wallet-sdk": "6.15.0", @@ -107,6 +108,7 @@ "metro-resolver": "0.80.5", "prism-react-renderer": "2.4.0", "react": "18.2.0", + "react-async-hook": "4.0.0", "react-dom": "18.2.0", "react-hook-form": "7.53.2", "react-native": "0.74.1", diff --git a/apps/mobile/src/common/transactions/stacks-transactions.hooks.ts b/apps/mobile/src/common/transactions/stacks-transactions.hooks.ts new file mode 100644 index 000000000..c6757f0af --- /dev/null +++ b/apps/mobile/src/common/transactions/stacks-transactions.hooks.ts @@ -0,0 +1,39 @@ +import { useNetworkPreferenceStacksNetwork } from '@/store/settings/settings.read'; +import { AnchorMode } from '@stacks/transactions'; + +import { + StacksUnsignedTokenTransferOptions, + TransactionTypes, + generateUnsignedTransaction, +} from '@leather.io/stacks'; +import { createMoney } from '@leather.io/utils'; + +export function useStxAccountTransferDetails(address: string, publicKey: string) { + const network = useNetworkPreferenceStacksNetwork(); + + return { + network, + publicKey, + // Fallback for fee estimation + recipient: address, + }; +} + +const defaultRequiredStxTokenTransferOptions = { + amount: createMoney(0, 'STX'), + anchorMode: AnchorMode.Any, + fee: createMoney(0, 'STX'), + nonce: '', +}; + +export function useGenerateStxTokenTransferUnsignedTransaction( + stxAccountDetails: ReturnType +) { + return (values: Partial) => + generateUnsignedTransaction({ + txType: TransactionTypes.StxTokenTransfer, + ...defaultRequiredStxTokenTransferOptions, + ...stxAccountDetails, + ...values, + }); +} diff --git a/apps/mobile/src/features/accounts/account-list/account-list.tsx b/apps/mobile/src/features/accounts/account-list/account-list.tsx index f3f5911d1..e1ce05674 100644 --- a/apps/mobile/src/features/accounts/account-list/account-list.tsx +++ b/apps/mobile/src/features/accounts/account-list/account-list.tsx @@ -34,7 +34,7 @@ export function AccountList({ accounts, onPress, showWalletInfo }: AccountListPr iconTestID={defaultIconTestId(account.icon)} onPress={() => onPress(account)} testID={TestId.walletListAccountCard} - walletName={showWalletInfo ? wallet.name : ' '} + walletName={showWalletInfo ? wallet.name : undefined} /> )} diff --git a/apps/mobile/src/features/balances/bitcoin/bitcoin-balance.tsx b/apps/mobile/src/features/balances/bitcoin/bitcoin-balance.tsx index ffeb598d0..2dd33de19 100644 --- a/apps/mobile/src/features/balances/bitcoin/bitcoin-balance.tsx +++ b/apps/mobile/src/features/balances/bitcoin/bitcoin-balance.tsx @@ -27,10 +27,7 @@ export function BitcoinTokenBalance({ id: 'asset_name.bitcoin', message: 'Bitcoin', })} - chain={t({ - id: 'asset_name.layer_1', - message: 'Layer 1', - })} + protocol="nativeBtc" fiatBalance={fiatBalance} availableBalance={availableBalance} onPress={onPress} diff --git a/apps/mobile/src/features/balances/stacks/stacks-balance.tsx b/apps/mobile/src/features/balances/stacks/stacks-balance.tsx index 8e30bb726..80803e746 100644 --- a/apps/mobile/src/features/balances/stacks/stacks-balance.tsx +++ b/apps/mobile/src/features/balances/stacks/stacks-balance.tsx @@ -28,10 +28,7 @@ export function StacksTokenBalance({ id: 'asset_name.stacks', message: 'Stacks', })} - chain={t({ - id: 'asset_name.layer_1', - message: 'Layer 1', - })} + protocol="nativeStx" fiatBalance={fiatBalance} availableBalance={availableBalance} onPress={onPress} diff --git a/apps/mobile/src/features/balances/token-balance.tsx b/apps/mobile/src/features/balances/token-balance.tsx index 0320393f3..674802af5 100644 --- a/apps/mobile/src/features/balances/token-balance.tsx +++ b/apps/mobile/src/features/balances/token-balance.tsx @@ -1,16 +1,29 @@ import { ReactNode } from 'react'; import { Balance } from '@/components/balance/balance'; +import { t } from '@lingui/macro'; -import { Money } from '@leather.io/models'; +import { CryptoAssetProtocol, Money } from '@leather.io/models'; import { Flag, ItemLayout, Pressable } from '@leather.io/ui/native'; +export function getChainLayerFromAssetProtocol(protocol: CryptoAssetProtocol) { + switch (protocol) { + case 'nativeBtc': + case 'nativeStx': + return t({ id: 'account_balance.caption_left.native', message: 'Layer 1' }); + case 'sip10': + return t({ id: 'account_balance.caption_left.sip10', message: 'Layer 2 ยท Stacks' }); + default: + return ''; + } +} + interface TokenBalanceProps { ticker: string; icon: ReactNode; tokenName: string; availableBalance?: Money; - chain: string; + protocol: CryptoAssetProtocol; fiatBalance: Money; onPress?(): void; } @@ -19,7 +32,7 @@ export function TokenBalance({ icon, tokenName, availableBalance, - chain, + protocol, fiatBalance, onPress, }: TokenBalanceProps) { @@ -29,7 +42,7 @@ export function TokenBalance({ } - captionLeft={chain} + captionLeft={getChainLayerFromAssetProtocol(protocol)} captionRight={ } diff --git a/apps/mobile/src/features/send/utils.ts b/apps/mobile/src/features/send/send-form.utils.ts similarity index 92% rename from apps/mobile/src/features/send/utils.ts rename to apps/mobile/src/features/send/send-form.utils.ts index f8a68cd61..85f5de8a7 100644 --- a/apps/mobile/src/features/send/utils.ts +++ b/apps/mobile/src/features/send/send-form.utils.ts @@ -11,7 +11,7 @@ export interface SendSheetNavigatorParamList { 'send-select-account': undefined; 'send-select-asset': { account: Account }; 'send-form-btc': { account: Account }; - 'send-form-stx': { account: Account }; + 'send-form-stx': { account: Account; address: string; publicKey: string }; 'sign-psbt': { psbtHex: string }; } diff --git a/apps/mobile/src/features/send/send-form/components/send-form-amount-field.tsx b/apps/mobile/src/features/send/send-form/components/send-form-amount-field.tsx index f6b9581af..f6dff7194 100644 --- a/apps/mobile/src/features/send/send-form/components/send-form-amount-field.tsx +++ b/apps/mobile/src/features/send/send-form/components/send-form-amount-field.tsx @@ -1,7 +1,6 @@ import { Controller, useFormContext } from 'react-hook-form'; import { TextInput } from '@/components/text-input'; -import { t } from '@lingui/macro'; import { z } from 'zod'; import { useSendFormContext } from '../send-form-context'; @@ -29,12 +28,6 @@ export function SendFormAmountField() { value={value} /> )} - rules={{ - required: t({ - id: 'send-form.amount-field.error.amount-required', - message: 'Amount is required', - }), - }} /> ); } diff --git a/apps/mobile/src/features/send/send-form/components/send-form-asset.tsx b/apps/mobile/src/features/send/send-form/components/send-form-asset.tsx index 9b2dd61e0..7d73441ab 100644 --- a/apps/mobile/src/features/send/send-form/components/send-form-asset.tsx +++ b/apps/mobile/src/features/send/send-form/components/send-form-asset.tsx @@ -5,24 +5,22 @@ import { Box, Pressable } from '@leather.io/ui/native'; import { useSendFormContext } from '../send-form-context'; interface SendFormAssetProps { - assetName: string; - chain: string; icon: React.ReactNode; onPress(): void; } -export function SendFormAsset({ assetName, chain, icon, onPress }: SendFormAssetProps) { - const { availableBalance, fiatBalance, symbol } = useSendFormContext(); +export function SendFormAsset({ icon, onPress }: SendFormAssetProps) { + const { name, protocol, availableBalance, fiatBalance, symbol } = useSendFormContext(); return ( diff --git a/apps/mobile/src/features/send/send-form/components/send-form-button.tsx b/apps/mobile/src/features/send/send-form/components/send-form-button.tsx index 0c2652817..eca169d59 100644 --- a/apps/mobile/src/features/send/send-form/components/send-form-button.tsx +++ b/apps/mobile/src/features/send/send-form/components/send-form-button.tsx @@ -10,27 +10,26 @@ import { useSendFormContext } from '../send-form-context'; export function SendFormButton() { const { displayToast } = useToastContext(); - const { schema } = useSendFormContext(); + const { schema, onInitSendTransfer } = useSendFormContext(); const { formState: { isDirty, isValid }, handleSubmit, } = useFormContext>(); - function onSubmit(data: z.infer) { + function onSubmitForm(values: z.infer) { + onInitSendTransfer(values); // Temporary toast for testing displayToast({ title: t`Form submitted`, type: 'success', }); - // eslint-disable-next-line no-console - console.log(t`submit data:`, data); } return (