Skip to content

Commit

Permalink
refactor: remove multiple recipients components
Browse files Browse the repository at this point in the history
  • Loading branch information
alter-eggo committed Apr 26, 2024
1 parent 2c36395 commit ba99041
Show file tree
Hide file tree
Showing 19 changed files with 82 additions and 383 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { validate } from 'bitcoin-address-validation';

import type { RpcSendTransferRecipient } from '@shared/rpc/methods/send-transfer';
import type { BitcoinRecipient } from '@shared/models/form.model';

import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client';

Expand Down Expand Up @@ -107,13 +107,13 @@ export function determineUtxosForSpend({
export interface DetermineUtxosForSpendArgsMultipleRecipients {
amount: number;
feeRate: number;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
utxos: UtxoResponseItem[];
}

interface DetermineUtxosForSpendAllArgsMultipleRecipients {
feeRate: number;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
utxos: UtxoResponseItem[];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useCallback } from 'react';
import * as btc from '@scure/btc-signer';

import { logger } from '@shared/logger';
import type { BitcoinRecipient } from '@shared/models/form.model';
import { Money } from '@shared/models/money.model';
import type { RpcSendTransferRecipient } from '@shared/rpc/methods/send-transfer';

import {
determineUtxosForSpend,
Expand Down Expand Up @@ -97,7 +97,7 @@ export function useGenerateUnsignedNativeSegwitSingleRecipientTx() {

interface GenerateNativeSegwitMultipleRecipientsTxValues {
amount: Money;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
}

export function useGenerateUnsignedNativeSegwitMultipleRecipientsTx() {
Expand Down
8 changes: 4 additions & 4 deletions src/app/common/transactions/bitcoin/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {
} from 'bitcoin-address-validation';

import { BTC_P2WPKH_DUST_AMOUNT } from '@shared/constants';
import type { BitcoinRecipient } from '@shared/models/form.model';
import {
BitcoinTransactionVectorOutput,
BitcoinTx,
} from '@shared/models/transactions/bitcoin-transaction.model';
import type { RpcSendTransferRecipient } from '@shared/rpc/methods/send-transfer';

import { sumNumbers } from '@app/common/math/helpers';
import { satToBtc } from '@app/common/money/unit-conversion';
Expand Down Expand Up @@ -159,7 +159,7 @@ function getSpendableAmountMultipleRecipients({
}: {
utxos: UtxoResponseItem[];
feeRate: number;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
}) {
const balance = utxos.map(utxo => utxo.value).reduce((prevVal, curVal) => prevVal + curVal, 0);

Expand All @@ -182,7 +182,7 @@ export function filterUneconomicalUtxosMultipleRecipients({
}: {
utxos: UtxoResponseItem[];
feeRate: number;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
}) {
const { spendableAmount: fullSpendableAmount } = getSpendableAmountMultipleRecipients({
utxos,
Expand All @@ -207,7 +207,7 @@ export function filterUneconomicalUtxosMultipleRecipients({

export function getSizeInfoMultipleRecipients(payload: {
inputLength: number;
recipients: RpcSendTransferRecipient[];
recipients: BitcoinRecipient[];
isSendMax?: boolean;
}) {
const { inputLength, recipients, isSendMax } = payload;
Expand Down
108 changes: 6 additions & 102 deletions src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useState } from 'react';
import { useField } from 'formik';
import { Stack } from 'leather-styles/jsx';

import type { BitcoinRecipient } from '@shared/models/form.model';
import { createMoney } from '@shared/models/money.model';
import type { RpcSendTransferRecipient } from '@shared/rpc/methods/send-transfer';

import { useOnMount } from '@app/common/hooks/use-on-mount';
import { satToBtc } from '@app/common/money/unit-conversion';
Expand All @@ -13,29 +13,26 @@ import { Input } from '@app/ui/components/input/input';

import { ErrorLabel } from '../error-label';
import { BitcoinCustomFeeFiat } from './bitcoin-custom-fee-fiat';
import {
useBitcoinCustomFee,
useBitcoinCustomFeeMultipleRecipients,
} from './hooks/use-bitcoin-custom-fee';
import { useBitcoinCustomFee } from './hooks/use-bitcoin-custom-fee';

const feeInputLabel = 'sats/vB';

interface Props {
onClick?(): void;
amount: number;
isSendingMax: boolean;
recipient: string;
recipients: BitcoinRecipient[];
hasInsufficientBalanceError: boolean;
errorMessage?: string;
setCustomFeeInitialValue?(value: string): void;
customFeeInitialValue: string;
}

const feeInputLabel = 'sats/vB';

export function BitcoinCustomFeeInput({
onClick,
amount,
isSendingMax,
recipient,
recipients,
hasInsufficientBalanceError,
setCustomFeeInitialValue,
customFeeInitialValue,
Expand All @@ -48,99 +45,6 @@ export function BitcoinCustomFeeInput({
}>(null);

const getCustomFeeValues = useBitcoinCustomFee({
amount: createMoney(amount, 'BTC'),
isSendingMax,
recipient,
});
const [unknownError, setUnknownError] = useState(false);
const [customInsufficientBalanceError, setCustomInsufficientBalanceError] = useState(false);

const hasError = hasInsufficientBalanceError || unknownError || customInsufficientBalanceError;
const errorMessage =
hasInsufficientBalanceError || customInsufficientBalanceError
? 'Insufficient funds'
: 'Unknown error';

function processFeeValue(feeRate: string) {
try {
const feeValues = getCustomFeeValues(Number(feeRate));
setFeeValue(feeValues);

setUnknownError(false);
setCustomInsufficientBalanceError(false);
} catch (err) {
if (err instanceof InsufficientFundsError) {
return setCustomInsufficientBalanceError(true);
}

setUnknownError(true);
}
}

function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value;
setCustomFeeInitialValue?.(e.target.value);
processFeeValue(value);
}

useOnMount(() => {
processFeeValue(customFeeInitialValue);
});
return (
<Stack gap="space.05">
<Stack>
<Input.Root hasError={hasError}>
<Input.Label>{feeInputLabel}</Input.Label>
<Input.Field
onClick={onClick}
{...field}
onChange={e => {
field.onChange(e);
onChange?.(e);
}}
/>
</Input.Root>
{hasError && <ErrorLabel>{errorMessage}</ErrorLabel>}
</Stack>

{!hasError && feeValue && (
<BitcoinCustomFeeFiat
feeInBtc={satToBtc(feeValue.fee).toString()}
fiatFeeValue={feeValue.fiatFeeValue}
/>
)}
</Stack>
);
}

interface PropsMultipleRecipients {
onClick?(): void;
amount: number;
isSendingMax: boolean;
recipients: RpcSendTransferRecipient[];
hasInsufficientBalanceError: boolean;
errorMessage?: string;
setCustomFeeInitialValue?(value: string): void;
customFeeInitialValue: string;
}

export function BitcoinCustomFeeInputMultipleRecipients({
onClick,
amount,
isSendingMax,
recipients,
hasInsufficientBalanceError,
setCustomFeeInitialValue,
customFeeInitialValue,
}: PropsMultipleRecipients) {
const [field] = useField('feeRate');

const [feeValue, setFeeValue] = useState<null | {
fee: number;
fiatFeeValue: string;
}>(null);

const getCustomFeeValues = useBitcoinCustomFeeMultipleRecipients({
amount: createMoney(amount, 'BTC'),
isSendingMax,
recipients,
Expand Down
124 changes: 6 additions & 118 deletions src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,16 @@ import { Stack, styled } from 'leather-styles/jsx';
import * as yup from 'yup';

import { BtcFeeType } from '@shared/models/fees/bitcoin-fees.model';
import type { BitcoinRecipient } from '@shared/models/form.model';
import { createMoney } from '@shared/models/money.model';
import type { RpcSendTransferRecipient } from '@shared/rpc/methods/send-transfer';

import { openInNewTab } from '@app/common/utils/open-in-new-tab';
import { Button } from '@app/ui/components/button/button';
import { Link } from '@app/ui/components/link/link';

import { OnChooseFeeArgs } from '../bitcoin-fees-list/bitcoin-fees-list';
import {
BitcoinCustomFeeInput,
BitcoinCustomFeeInputMultipleRecipients,
} from './bitcoin-custom-fee-input';
import {
useBitcoinCustomFee,
useBitcoinCustomFeeMultipleRecipients,
} from './hooks/use-bitcoin-custom-fee';
import { BitcoinCustomFeeInput } from './bitcoin-custom-fee-input';
import { useBitcoinCustomFee } from './hooks/use-bitcoin-custom-fee';

interface BitcoinCustomFeeProps {
amount: number;
Expand All @@ -31,7 +25,7 @@ interface BitcoinCustomFeeProps {
onChooseFee({ feeRate, feeValue, time, isCustomFee }: OnChooseFeeArgs): Promise<void>;
onSetSelectedFeeType(value: BtcFeeType | null): void;
onValidateBitcoinSpend(value: number): boolean;
recipient: string;
recipients: BitcoinRecipient[];
setCustomFeeInitialValue: Dispatch<SetStateAction<string>>;
maxCustomFeeRate: number;
}
Expand All @@ -44,118 +38,12 @@ export function BitcoinCustomFee({
onChooseFee,
onSetSelectedFeeType,
onValidateBitcoinSpend,
recipient,
recipients,
setCustomFeeInitialValue,
maxCustomFeeRate,
}: BitcoinCustomFeeProps) {
const feeInputRef = useRef<HTMLInputElement | null>(null);
const getCustomFeeValues = useBitcoinCustomFee({
amount: createMoney(amount, 'BTC'),
isSendingMax,
recipient,
});

const onChooseCustomBtcFee = useCallback(
async ({ feeRate }: { feeRate: string }) => {
onSetSelectedFeeType(null);
const { fee: feeValue } = getCustomFeeValues(Number(feeRate));
const isValid = onValidateBitcoinSpend(feeValue);
if (!isValid) return;
await onChooseFee({ feeRate: Number(feeRate), feeValue, time: '', isCustomFee: true });
},
[onSetSelectedFeeType, getCustomFeeValues, onValidateBitcoinSpend, onChooseFee]
);

const validationSchema = yup.object({
feeRate: yup
.number()
.required('Fee is required')
.integer('Fee must be a whole number')
.test({
message: 'Fee is too high',
test: value => {
return value <= maxCustomFeeRate;
},
}),
});

return (
<Formik
initialValues={{ feeRate: customFeeInitialValue.toString() }}
onSubmit={onChooseCustomBtcFee}
validateOnChange={false}
validateOnBlur={false}
validateOnMount={false}
validationSchema={validationSchema}
>
{props => (
<Form>
<Stack gap="space.06" mt="space.02">
<Stack gap="space.05">
<styled.span color="ink.text-subdued" textStyle="body.02" maxWidth="21.5rem">
Higher fee rates typically lead to faster confirmation times.
<Link
ml="space.01"
onClick={() => openInNewTab('https://buybitcoinworldwide.com/fee-calculator/')}
textStyle="body.02"
>
View fee calculator
</Link>
</styled.span>
<BitcoinCustomFeeInput
amount={amount}
isSendingMax={isSendingMax}
onClick={async () => {
feeInputRef.current?.focus();
await props.setValues({ ...props.values });
}}
customFeeInitialValue={customFeeInitialValue}
setCustomFeeInitialValue={setCustomFeeInitialValue}
recipient={recipient}
hasInsufficientBalanceError={hasInsufficientBalanceError}
/>
</Stack>
<Button
data-testid={SendCryptoAssetSelectors.PreviewSendTxBtn}
disabled={!props.values.feeRate}
type="submit"
>
Use custom fee
</Button>
</Stack>
</Form>
)}
</Formik>
);
}

interface BitcoinCustomFeeMultipleRecipientsProps {
amount: number;
customFeeInitialValue: string;
hasInsufficientBalanceError: boolean;
isSendingMax: boolean;
onChooseFee({ feeRate, feeValue, time, isCustomFee }: OnChooseFeeArgs): Promise<void>;
onSetSelectedFeeType(value: BtcFeeType | null): void;
onValidateBitcoinSpend(value: number): boolean;
recipients: RpcSendTransferRecipient[];
setCustomFeeInitialValue: Dispatch<SetStateAction<string>>;
maxCustomFeeRate: number;
}

export function BitcoinCustomFeeMultipleRecipients({
amount,
customFeeInitialValue,
hasInsufficientBalanceError,
isSendingMax,
onChooseFee,
onSetSelectedFeeType,
onValidateBitcoinSpend,
recipients,
setCustomFeeInitialValue,
maxCustomFeeRate,
}: BitcoinCustomFeeMultipleRecipientsProps) {
const feeInputRef = useRef<HTMLInputElement | null>(null);
const getCustomFeeValues = useBitcoinCustomFeeMultipleRecipients({
amount: createMoney(amount, 'BTC'),
isSendingMax,
recipients,
Expand Down Expand Up @@ -208,7 +96,7 @@ export function BitcoinCustomFeeMultipleRecipients({
View fee calculator
</Link>
</styled.span>
<BitcoinCustomFeeInputMultipleRecipients
<BitcoinCustomFeeInput
amount={amount}
isSendingMax={isSendingMax}
onClick={async () => {
Expand Down
Loading

0 comments on commit ba99041

Please sign in to comment.