diff --git a/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts b/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts
index ade9b348719..b886597f563 100644
--- a/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts
+++ b/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts
@@ -11,6 +11,12 @@ export interface DetermineUtxosForSpendArgs {
utxos: UtxoResponseItem[];
}
+export class InsufficientFundsError extends Error {
+ constructor() {
+ super('Insufficient funds');
+ }
+}
+
export function determineUtxosForSpendAll({
amount,
feeRate,
@@ -71,7 +77,7 @@ export function determineUtxosForSpend({
neededUtxos.push(utxo);
}
- if (!sizeInfo) throw new Error('Transaction size must be defined');
+ if (!sizeInfo) throw new InsufficientFundsError();
const fee = Math.ceil(sizeInfo.txVBytes * feeRate);
diff --git a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-fiat.tsx b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-fiat.tsx
index 993cb5df93d..aed27511b5a 100644
--- a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-fiat.tsx
+++ b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-fiat.tsx
@@ -1,49 +1,18 @@
-import { useMemo } from 'react';
-
-import { useField } from 'formik';
import { Flex, styled } from 'leather-styles/jsx';
-import { createMoney } from '@shared/models/money.model';
-
-import { satToBtc } from '@app/common/money/unit-conversion';
-
-import { useBitcoinCustomFee } from './hooks/use-bitcoin-custom-fee';
-
interface BitcoinCustomFeeFiatProps {
- amount: number;
- isSendingMax: boolean;
- recipient: string;
+ feeInBtc: string;
+ fiatFeeValue: string;
}
-export function BitcoinCustomFeeFiat({
- amount,
- isSendingMax,
- recipient,
-}: BitcoinCustomFeeFiatProps) {
- const [field] = useField('feeRate');
- const getCustomFeeValues = useBitcoinCustomFee({
- amount: createMoney(amount, 'BTC'),
- isSendingMax,
- recipient,
- });
-
- const feeData = useMemo(() => {
- const { fee, fiatFeeValue } = getCustomFeeValues(Number(field.value));
- const feeInBtc = satToBtc(fee).toString();
-
- return { fiatFeeValue, feeInBtc };
- }, [getCustomFeeValues, field.value]);
-
- const canShow = !feeData.feeInBtc.includes('e') && Number(field.value) > 0;
- if (!canShow) return null;
-
+export function BitcoinCustomFeeFiat({ feeInBtc, fiatFeeValue }: BitcoinCustomFeeFiatProps) {
return (
- {feeData.fiatFeeValue}
+ {fiatFeeValue}
- {feeData.feeInBtc} BTC
+ {feeInBtc} BTC
);
diff --git a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-input.tsx b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-input.tsx
new file mode 100644
index 00000000000..83b561d112a
--- /dev/null
+++ b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee-input.tsx
@@ -0,0 +1,110 @@
+import { useState } from 'react';
+
+import { useField } from 'formik';
+import { Stack } from 'leather-styles/jsx';
+
+import { createMoney } from '@shared/models/money.model';
+
+import { useOnMount } from '@app/common/hooks/use-on-mount';
+import { satToBtc } from '@app/common/money/unit-conversion';
+import { InsufficientFundsError } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection';
+import { Input } from '@app/ui/components/input/input';
+
+import { ErrorLabel } from '../error-label';
+import { BitcoinCustomFeeFiat } from './bitcoin-custom-fee-fiat';
+import { useBitcoinCustomFee } from './hooks/use-bitcoin-custom-fee';
+
+interface Props {
+ onClick?(): void;
+ amount: number;
+ isSendingMax: boolean;
+ recipient: string;
+ hasInsufficientBalanceError: boolean;
+ errorMessage?: string;
+ setCustomFeeInitialValue?(value: string): void;
+ customFeeInitialValue: string;
+}
+
+const feeInputLabel = 'sats/vB';
+
+export function BitcoinCustomFeeInput({
+ onClick,
+ amount,
+ isSendingMax,
+ recipient,
+ hasInsufficientBalanceError,
+ setCustomFeeInitialValue,
+ customFeeInitialValue,
+}: Props) {
+ const [field] = useField('feeRate');
+
+ const [feeValue, setFeeValue] = useState(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) {
+ const value = e.target.value;
+ setCustomFeeInitialValue?.(e.target.value);
+ processFeeValue(value);
+ }
+
+ useOnMount(() => {
+ processFeeValue(customFeeInitialValue);
+ });
+ return (
+
+
+
+ {feeInputLabel}
+ {
+ field.onChange(e);
+ onChange?.(e);
+ }}
+ />
+
+ {hasError && {errorMessage}}
+
+
+ {!hasError && feeValue && (
+
+ )}
+
+ );
+}
diff --git a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
index 0913027cca8..440fba223e0 100644
--- a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
+++ b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
@@ -1,6 +1,6 @@
import { Dispatch, SetStateAction, useCallback, useRef } from 'react';
-import { Form, Formik, useField } from 'formik';
+import { Form, Formik } from 'formik';
import { Stack, styled } from 'leather-styles/jsx';
import * as yup from 'yup';
@@ -9,41 +9,12 @@ import { createMoney } from '@shared/models/money.model';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
import { PreviewButton } from '@app/components/preview-button';
-import { Input } from '@app/ui/components/input/input';
import { Link } from '@app/ui/components/link/link';
import { OnChooseFeeArgs } from '../bitcoin-fees-list/bitcoin-fees-list';
-import { BitcoinCustomFeeFiat } from './bitcoin-custom-fee-fiat';
+import { BitcoinCustomFeeInput } from './bitcoin-custom-fee-input';
import { useBitcoinCustomFee } from './hooks/use-bitcoin-custom-fee';
-const feeInputLabel = 'sats/vB';
-
-interface BitcoinCustomFeeInputProps {
- hasInsufficientBalanceError: boolean;
- onClick(): void;
- onChange?(e: React.ChangeEvent): void;
-}
-function BitcoinCustomFeeInput({
- hasInsufficientBalanceError,
- onClick,
- onChange,
-}: BitcoinCustomFeeInputProps) {
- const [field] = useField('feeRate');
- return (
-
- {feeInputLabel}
- {
- field.onChange(e);
- onChange?.(e);
- }}
- />
-
- );
-}
-
interface BitcoinCustomFeeProps {
amount: number;
customFeeInitialValue: string;
@@ -56,6 +27,7 @@ interface BitcoinCustomFeeProps {
setCustomFeeInitialValue: Dispatch>;
maxCustomFeeRate: number;
}
+
export function BitcoinCustomFee({
amount,
customFeeInitialValue,
@@ -123,20 +95,17 @@ export function BitcoinCustomFee({
{
- feeInputRef?.current?.focus();
+ feeInputRef.current?.focus();
await props.setValues({ ...props.values });
}}
- onChange={e => setCustomFeeInitialValue((e.target as HTMLInputElement).value)}
+ customFeeInitialValue={customFeeInitialValue}
+ setCustomFeeInitialValue={setCustomFeeInitialValue}
+ recipient={recipient}
+ hasInsufficientBalanceError={hasInsufficientBalanceError}
/>
-
-
-
diff --git a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts
index fd98d553812..786016ccdd6 100644
--- a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts
+++ b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts
@@ -21,10 +21,14 @@ function getFeeForList(
determineUtxosForFeeArgs: DetermineUtxosForSpendArgs,
isSendingMax?: boolean
) {
- const { fee } = isSendingMax
- ? determineUtxosForSpendAll(determineUtxosForFeeArgs)
- : determineUtxosForSpend(determineUtxosForFeeArgs);
- return fee;
+ try {
+ const { fee } = isSendingMax
+ ? determineUtxosForSpendAll(determineUtxosForFeeArgs)
+ : determineUtxosForSpend(determineUtxosForFeeArgs);
+ return fee;
+ } catch (error) {
+ return null;
+ }
}
interface UseBitcoinFeesListArgs {
@@ -75,36 +79,46 @@ export function useBitcoinFeesList({
feeRate: feeRates.hourFee.toNumber(),
};
+ const feesArr = [];
+
const highFeeValue = getFeeForList(determineUtxosForHighFeeArgs, isSendingMax);
const standardFeeValue = getFeeForList(determineUtxosForStandardFeeArgs, isSendingMax);
const lowFeeValue = getFeeForList(determineUtxosForLowFeeArgs, isSendingMax);
- return [
- {
+ if (highFeeValue) {
+ feesArr.push({
label: BtcFeeType.High,
value: highFeeValue,
btcValue: formatMoneyPadded(createMoney(highFeeValue, 'BTC')),
time: btcTxTimeMap.fastestFee,
fiatValue: getFiatFeeValue(highFeeValue),
feeRate: feeRates.fastestFee.toNumber(),
- },
- {
+ });
+ }
+
+ if (standardFeeValue) {
+ feesArr.push({
label: BtcFeeType.Standard,
value: standardFeeValue,
btcValue: formatMoneyPadded(createMoney(standardFeeValue, 'BTC')),
time: btcTxTimeMap.halfHourFee,
fiatValue: getFiatFeeValue(standardFeeValue),
feeRate: feeRates.halfHourFee.toNumber(),
- },
- {
+ });
+ }
+
+ if (lowFeeValue) {
+ feesArr.push({
label: BtcFeeType.Low,
value: lowFeeValue,
btcValue: formatMoneyPadded(createMoney(lowFeeValue, 'BTC')),
time: btcTxTimeMap.hourFee,
fiatValue: getFiatFeeValue(lowFeeValue),
feeRate: feeRates.hourFee.toNumber(),
- },
- ];
+ });
+ }
+
+ return feesArr;
}, [feeRates, utxos, isSendingMax, balance.amount, amount.amount, recipient, btcMarketData]);
return {
diff --git a/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx b/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx
index a957d4fe98b..83f8cca436e 100644
--- a/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx
+++ b/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx
@@ -1,6 +1,6 @@
import { useNavigate } from 'react-router-dom';
-import { Formik, useField } from 'formik';
+import { Formik } from 'formik';
import { Stack } from 'leather-styles/jsx';
import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model';
@@ -10,31 +10,19 @@ import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balanc
import { formatMoney } from '@app/common/money/format-money';
import { btcToSat } from '@app/common/money/unit-conversion';
import { getBitcoinTxValue } from '@app/common/transactions/bitcoin/utils';
-import { BitcoinCustomFeeFiat } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee-fiat';
+import { BitcoinCustomFeeInput } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee-input';
import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item';
import { LoadingSpinner } from '@app/components/loading-spinner';
import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
-import { Input } from '@app/ui/components/input/input';
import { Caption } from '@app/ui/components/typography/caption';
import { useBtcIncreaseFee } from '../hooks/use-btc-increase-fee';
import { IncreaseFeeActions } from './increase-fee-actions';
-const feeInputLabel = 'sats/vB';
-
-function BitcoinFeeIncreaseField() {
- const [field] = useField('feeRate');
- return (
-
-
- Fee rate
-
- );
-}
-
interface IncreaseBtcFeeFormProps {
btcTx: BitcoinTx;
}
+
export function IncreaseBtcFeeForm({ btcTx }: IncreaseBtcFeeFormProps) {
const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner();
const navigate = useNavigate();
@@ -63,13 +51,14 @@ export function IncreaseBtcFeeForm({ btcTx }: IncreaseBtcFeeFormProps) {
{btcTx && }
-
-
diff --git a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.spec.ts b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.spec.ts
index b2713c310b0..67978daf466 100644
--- a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.spec.ts
+++ b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.spec.ts
@@ -1,12 +1,12 @@
import { sumNumbers } from '@app/common/math/helpers';
-import { selectInscriptionTransferCoins } from './select-inscription-coins';
+import { selectTaprootInscriptionTransferCoins } from './select-inscription-coins';
-describe(selectInscriptionTransferCoins.name, () => {
+describe(selectTaprootInscriptionTransferCoins.name, () => {
test('inscription coin selection', () => {
const inscriptionInputAmount = 1000n;
- const result = selectInscriptionTransferCoins({
+ const result = selectTaprootInscriptionTransferCoins({
recipient: '',
inscriptionInput: {
value: Number(inscriptionInputAmount),
@@ -37,11 +37,11 @@ describe(selectInscriptionTransferCoins.name, () => {
Number(inscriptionInputAmount)
);
- expect(result.txFee).toEqual(6765);
+ expect(result.txFee).toEqual(5048);
});
test('when there are not enough utxo to cover fee', () => {
- const result = selectInscriptionTransferCoins({
+ const result = selectTaprootInscriptionTransferCoins({
recipient: '',
inscriptionInput: {
value: 1000,
diff --git a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts
index ac1e6185b4a..51ac5736e97 100644
--- a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts
+++ b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts
@@ -6,8 +6,6 @@ import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-s
import { createCounter } from '@app/common/utils/counter';
import { UtxoResponseItem, UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client';
-const idealInscriptionValue = 10_000;
-
interface SelectInscriptionCoinSuccess {
success: true;
inputs: UtxoResponseItem[];
@@ -28,7 +26,8 @@ interface SelectInscriptionTransferCoinsArgs {
recipient: string;
changeAddress: string;
}
-export function selectInscriptionTransferCoins(
+
+export function selectTaprootInscriptionTransferCoins(
args: SelectInscriptionTransferCoinsArgs
): SelectInscriptionCoinResult {
const { inscriptionInput, recipient, changeAddress, nativeSegwitUtxos, feeRate } = args;
@@ -51,9 +50,9 @@ export function selectInscriptionTransferCoins(
const indexCounter = createCounter();
function shouldContinueTryingWithMoreInputs() {
- const valueOfUtxos = sumNumbers(neededInputs.map(utxo => utxo.value));
+ const neededSumOfInputs = sumNumbers(neededInputs.map(utxo => utxo.value));
if (indexCounter.getValue() > nativeSegwitUtxos.length) return false;
- return idealInscriptionValue + txFee > inscriptionInput.value + valueOfUtxos.toNumber();
+ return txFee >= neededSumOfInputs.toNumber();
}
let utxos = nativeSegwitUtxos
diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts
index 78688b02f82..cff9efcdab6 100644
--- a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts
+++ b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts
@@ -6,7 +6,10 @@ import { BitcoinInputSigningConfig } from '@shared/crypto/bitcoin/signer-config'
import { logger } from '@shared/logger';
import { OrdinalSendFormValues } from '@shared/models/form.model';
-import { determineUtxosForSpend } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection';
+import {
+ InsufficientFundsError,
+ determineUtxosForSpend,
+} from '@app/common/transactions/bitcoin/coinselect/local-coin-selection';
import { createCounter } from '@app/common/utils/counter';
import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks';
import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client';
@@ -14,7 +17,7 @@ import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/
import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks';
-import { selectInscriptionTransferCoins } from '../coinselect/select-inscription-coins';
+import { selectTaprootInscriptionTransferCoins } from '../coinselect/select-inscription-coins';
export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivationPath) {
const createTaprootSigner = useCurrentAccountTaprootSigner();
@@ -37,7 +40,7 @@ export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivatio
if (!taprootSigner || !nativeSegwitSigner || !nativeSegwitUtxos || !values.feeRate) return;
- const result = selectInscriptionTransferCoins({
+ const result = selectTaprootInscriptionTransferCoins({
recipient: values.recipient,
inscriptionInput,
nativeSegwitUtxos,
@@ -51,7 +54,7 @@ export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivatio
if (!result.success) return null;
- const { inputs, outputs } = result;
+ const { inputs, outputs, txFee } = result;
try {
const tx = new btc.Transaction();
@@ -96,8 +99,11 @@ export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivatio
tx.toPSBT();
- return { psbt: tx.toPSBT(), signingConfig };
+ return { psbt: tx.toPSBT(), signingConfig, txFee };
} catch (e) {
+ if (e instanceof InsufficientFundsError) {
+ throw new InsufficientFundsError();
+ }
logger.error('Unable to sign transaction');
return null;
}
@@ -116,7 +122,7 @@ export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivatio
utxos: nativeSegwitUtxos,
};
- const { inputs, outputs } = determineUtxosForSpend(determineUtxosArgs);
+ const { inputs, outputs, fee } = determineUtxosForSpend(determineUtxosArgs);
try {
const tx = new btc.Transaction();
@@ -148,8 +154,11 @@ export function useGenerateUnsignedOrdinalTx(inscriptionInput: UtxoWithDerivatio
tx.addOutputAddress(values.recipient, BigInt(output.value), networkMode);
});
- return { psbt: tx.toPSBT(), signingConfig: undefined };
+ return { psbt: tx.toPSBT(), signingConfig: undefined, txFee: fee };
} catch (e) {
+ if (e instanceof InsufficientFundsError) {
+ throw new InsufficientFundsError();
+ }
logger.error('Unable to sign transaction');
return null;
}
diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts
index 3003240cb52..52435aa4d3f 100644
--- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts
+++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts
@@ -1,6 +1,7 @@
-import { useMemo } from 'react';
+import { useCallback, useMemo } from 'react';
import { BtcFeeType, btcTxTimeMap } from '@shared/models/fees/bitcoin-fees.model';
+import type { SupportedInscription } from '@shared/models/inscription.model';
import { createMoney } from '@shared/models/money.model';
import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
@@ -12,19 +13,44 @@ import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates
import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks';
import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
-import { selectInscriptionTransferCoins } from '../coinselect/select-inscription-coins';
+import { useGenerateUnsignedOrdinalTx } from './use-generate-ordinal-tx';
interface UseSendInscriptionFeesListArgs {
recipient: string;
utxo: UtxoWithDerivationPath;
+ inscription: SupportedInscription;
}
-export function useSendInscriptionFeesList({ recipient, utxo }: UseSendInscriptionFeesListArgs) {
+
+export function useSendInscriptionFeesList({
+ recipient,
+ utxo,
+ inscription,
+}: UseSendInscriptionFeesListArgs) {
const createNativeSegwitSigner = useCurrentAccountNativeSegwitSigner();
const { data: nativeSegwitUtxos } = useCurrentNativeSegwitUtxos();
const btcMarketData = useCryptoCurrencyMarketData('BTC');
const { data: feeRates, isLoading } = useAverageBitcoinFeeRates();
+ const { coverFeeFromAdditionalUtxos } = useGenerateUnsignedOrdinalTx(utxo);
+
+ const getTransactionFee = useCallback(
+ (feeRate: number) => {
+ try {
+ const tx = coverFeeFromAdditionalUtxos({
+ recipient,
+ feeRate,
+ inscription,
+ });
+
+ return tx?.txFee;
+ } catch (error) {
+ return null;
+ }
+ },
+ [coverFeeFromAdditionalUtxos, recipient, inscription]
+ );
+
const feesList: FeesListItem[] = useMemo(() => {
function getFiatFeeValue(fee: number) {
return `~ ${i18nFormatCurrency(
@@ -36,63 +62,47 @@ export function useSendInscriptionFeesList({ recipient, utxo }: UseSendInscripti
if (!feeRates || !nativeSegwitUtxos || !nativeSegwitSigner) return [];
- const highFeeResult = selectInscriptionTransferCoins({
- recipient,
- inscriptionInput: utxo,
- nativeSegwitUtxos,
- changeAddress: nativeSegwitSigner.payment.address!,
- feeRate: feeRates.fastestFee.toNumber(),
- });
-
- const standardFeeResult = selectInscriptionTransferCoins({
- recipient,
- inscriptionInput: utxo,
- nativeSegwitUtxos,
- changeAddress: nativeSegwitSigner.payment.address!,
- feeRate: feeRates.halfHourFee.toNumber(),
- });
-
- const lowFeeResult = selectInscriptionTransferCoins({
- recipient,
- inscriptionInput: utxo,
- nativeSegwitUtxos,
- changeAddress: nativeSegwitSigner.payment.address!,
- feeRate: feeRates.hourFee.toNumber(),
- });
-
- if (!highFeeResult.success || !standardFeeResult.success || !lowFeeResult.success) return [];
-
- const { txFee: highFeeValue } = highFeeResult;
- const { txFee: standardFeeValue } = standardFeeResult;
- const { txFee: lowFeeValue } = lowFeeResult;
-
- return [
- {
+ const highFeeValue = getTransactionFee(feeRates.fastestFee.toNumber());
+ const standardFeeValue = getTransactionFee(feeRates.halfHourFee.toNumber());
+ const lowFeeValue = getTransactionFee(feeRates.hourFee.toNumber());
+
+ const feesArr = [];
+
+ if (highFeeValue) {
+ feesArr.push({
label: BtcFeeType.High,
value: highFeeValue,
btcValue: formatMoneyPadded(createMoney(highFeeValue, 'BTC')),
time: btcTxTimeMap.fastestFee,
fiatValue: getFiatFeeValue(highFeeValue),
feeRate: feeRates.fastestFee.toNumber(),
- },
- {
+ });
+ }
+
+ if (standardFeeValue) {
+ feesArr.push({
label: BtcFeeType.Standard,
value: standardFeeValue,
btcValue: formatMoneyPadded(createMoney(standardFeeValue, 'BTC')),
time: btcTxTimeMap.halfHourFee,
fiatValue: getFiatFeeValue(standardFeeValue),
feeRate: feeRates.halfHourFee.toNumber(),
- },
- {
+ });
+ }
+
+ if (lowFeeValue) {
+ feesArr.push({
label: BtcFeeType.Low,
value: lowFeeValue,
btcValue: formatMoneyPadded(createMoney(lowFeeValue, 'BTC')),
time: btcTxTimeMap.hourFee,
fiatValue: getFiatFeeValue(lowFeeValue),
feeRate: feeRates.hourFee.toNumber(),
- },
- ];
- }, [createNativeSegwitSigner, feeRates, nativeSegwitUtxos, recipient, utxo, btcMarketData]);
+ });
+ }
+
+ return feesArr;
+ }, [feeRates, nativeSegwitUtxos, btcMarketData, createNativeSegwitSigner, getTransactionFee]);
return {
feesList,
diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx
index f60660bf7d2..9342d37bde6 100644
--- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx
+++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx
@@ -11,6 +11,7 @@ import { isError } from '@shared/utils';
import { FormErrorMessages } from '@app/common/error-messages';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { formFeeRowValue } from '@app/common/send/utils';
+import { InsufficientFundsError } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection';
import {
btcAddressNetworkValidator,
btcAddressValidator,
@@ -20,7 +21,7 @@ import { useSignBitcoinTx } from '@app/store/accounts/blockchain/bitcoin/bitcoin
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
import { useSendInscriptionState } from '../components/send-inscription-container';
-import { recipeintFieldName } from '../send-inscription-form';
+import { recipientFieldName } from '../send-inscription-form';
import { useGenerateUnsignedOrdinalTx } from './use-generate-ordinal-tx';
export function useSendInscriptionForm() {
@@ -66,6 +67,13 @@ export function useSendInscriptionForm() {
} catch (error) {
void analytics.track('ordinals_dot_com_unavailable', { error });
+ if (error instanceof InsufficientFundsError) {
+ setShowError(
+ 'Insufficient funds to cover fee. Deposit some BTC to your Native Segwit address.'
+ );
+ return;
+ }
+
let message = 'Unable to establish if utxo has multiple inscriptions';
if (isError(error)) {
message = error.message;
@@ -127,7 +135,7 @@ export function useSendInscriptionForm() {
},
validationSchema: yup.object({
- [recipeintFieldName]: yup
+ [recipientFieldName]: yup
.string()
.required(FormErrorMessages.AddressRequired)
.concat(btcAddressValidator())
diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx
index 9b90eb08bed..15b572ecbb1 100644
--- a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx
+++ b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx
@@ -22,7 +22,7 @@ export function SendInscriptionChooseFee() {
const navigate = useNavigate();
const { recipient, selectedFeeType, setSelectedFeeType, utxo, inscription } =
useSendInscriptionState();
- const { feesList, isLoading } = useSendInscriptionFeesList({ recipient, utxo });
+ const { feesList, isLoading } = useSendInscriptionFeesList({ recipient, utxo, inscription });
const recommendedFeeRate = feesList[1]?.feeRate.toString() || '';
const { reviewTransaction } = useSendInscriptionForm();
diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx
index 59d3e4f0afb..6e73330b073 100644
--- a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx
+++ b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx
@@ -18,7 +18,7 @@ import { useSendInscriptionState } from './components/send-inscription-container
import { useSendInscriptionForm } from './hooks/use-send-inscription-form';
import { SendInscriptionFormLoader } from './send-indcription-form-loader';
-export const recipeintFieldName = 'recipient';
+export const recipientFieldName = 'recipient';
export function SendInscriptionForm() {
const navigate = useNavigate();
@@ -30,7 +30,7 @@ export function SendInscriptionForm() {
} name="Ordinal inscription" />
diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-address-type-field.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-address-type-field.tsx
index 23da077496a..3dfbde755ea 100644
--- a/src/app/pages/send/send-crypto-asset-form/components/recipient-address-type-field.tsx
+++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-address-type-field.tsx
@@ -32,10 +32,12 @@ export function RecipientAddressTypeField({
return (
-
- {rightLabel}
-
- {topInputOverlay}
+ {rightLabel && (
+
+ {rightLabel}
+
+ )}
+ {topInputOverlay && {topInputOverlay}}