Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add hook for confirmed btc balance, inscription callouts #711

Merged
merged 12 commits into from
Dec 28, 2023
Merged
15 changes: 6 additions & 9 deletions src/app/components/confirmBtcTransactionComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SettingIcon from '@assets/img/dashboard/faders_horizontal.svg';
import AssetIcon from '@assets/img/transactions/Assets.svg';
import ActionButton from '@components/button';
import InfoContainer from '@components/infoContainer';
import RecipientComponent from '@components/recipientComponent';
import TransactionSettingAlert from '@components/transactionSetting';
import TransferFeeView from '@components/transferFeeView';
Expand Down Expand Up @@ -239,7 +238,7 @@
setSignedTx(data.signedTx);
setShowFeeSettings(false);
}
}, [data]);

Check warning on line 241 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'setCurrentFee'. Either include it or remove the dependency array. If 'setCurrentFee' changes too often, find the parent component that defines it and wrap that definition in useCallback

useEffect(() => {
if (ordinalData) {
Expand All @@ -247,7 +246,7 @@
setSignedTx(ordinalData.signedTx);
setShowFeeSettings(false);
}
}, [ordinalData]);

Check warning on line 249 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'setCurrentFee'. Either include it or remove the dependency array. If 'setCurrentFee' changes too often, find the parent component that defines it and wrap that definition in useCallback

useEffect(() => {
let sum: BigNumber = new BigNumber(0);
Expand All @@ -266,17 +265,13 @@
setSignedTx(signedNonOrdinalBtcSend.signedTx);
setShowFeeSettings(false);
}
}, [signedNonOrdinalBtcSend]);

Check warning on line 268 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'setCurrentFee'. Either include it or remove the dependency array. If 'setCurrentFee' changes too often, find the parent component that defines it and wrap that definition in useCallback

useEffect(() => {
if (
const isFeeHigh =
feeMultipliers &&
currentFee.isGreaterThan(new BigNumber(feeMultipliers.thresholdHighSatsFee))
) {
setShowFeeWarning(true);
} else if (showFeeWarning) {
setShowFeeWarning(false);
}
currentFee.isGreaterThan(new BigNumber(feeMultipliers.thresholdHighSatsFee));
setShowFeeWarning(!!isFeeHigh);
}, [currentFee, feeMultipliers]);

const onAdvancedSettingClick = () => {
Expand Down Expand Up @@ -330,7 +325,7 @@
setError(t('TX_ERRORS.INSUFFICIENT_BALANCE_FEES'));
} else setError(txError.toString());
}
}, [txError]);

Check warning on line 328 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has missing dependencies: 'recipients' and 't'. Either include them or remove the dependency array

useEffect(() => {
if (recipients && ordinalError) {
Expand All @@ -341,7 +336,7 @@
setError(t('TX_ERRORS.INSUFFICIENT_BALANCE_FEES'));
} else setError(ordinalError.toString());
}
}, [ordinalError]);

Check warning on line 339 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has missing dependencies: 'recipients', 't', and 'txError'. Either include them or remove the dependency array

useEffect(() => {
if (recipients && errorSigningNonOrdial) {
Expand All @@ -352,13 +347,15 @@
setError(t('TX_ERRORS.INSUFFICIENT_BALANCE_FEES'));
} else setError(errorSigningNonOrdial.toString());
}
}, [errorSigningNonOrdial]);

Check warning on line 350 in src/app/components/confirmBtcTransactionComponent/index.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has missing dependencies: 'recipients', 't', and 'txError'. Either include them or remove the dependency array

return (
<>
<OuterContainer isGalleryOpen={isGalleryOpen}>
{showFeeWarning && (
<InfoContainer type="Warning" bodyText={t('CONFIRM_TRANSACTION.HIGH_FEE_WARNING_TEXT')} />
<CalloutContainer>
<Callout bodyText={t('CONFIRM_TRANSACTION.HIGH_FEE_WARNING_TEXT')} variant="warning" />
</CalloutContainer>
)}
{/* TODO tim: refactor this not to use children. it should be just another prop */}
{children}
Expand Down
36 changes: 36 additions & 0 deletions src/app/hooks/queries/useConfirmedBtcBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import useBtcClient from '@hooks/useBtcClient';
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import useWalletSelector from '../useWalletSelector';

const useConfirmBtcBalance = () => {
const { btcAddress } = useWalletSelector();
const btcClient = useBtcClient();

const fetchBtcAddressData = async () => btcClient.getAddressData(btcAddress);

const { data, isLoading, isError, error } = useQuery({
queryKey: ['btc-address-data'],
queryFn: fetchBtcAddressData,
});

const confirmedBalance = useMemo(() => {
if (!isLoading && !isError && data) {
const chainStats = data.chain_stats;
const mempoolStats = data.mempool_stats;

if (chainStats && mempoolStats) {
return chainStats.funded_txo_sum - chainStats.spent_txo_sum - mempoolStats.spent_txo_sum;
}
}
return undefined;
}, [data, isLoading, isError]);

return {
confirmedBalance,
isLoading,
error,
};
};

export default useConfirmBtcBalance;
47 changes: 40 additions & 7 deletions src/app/screens/createInscription/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
currencySymbolMap,
fetchBtcFeeRate,
getNonOrdinalUtxo,
InscriptionErrorCode,
useInscriptionExecute,
useInscriptionFees,
UTXO,
Expand All @@ -25,8 +26,9 @@ import { ExternalSatsMethods, MESSAGE_SOURCE } from '@common/types/message-types
import AccountHeaderComponent from '@components/accountHeader';
import ConfirmScreen from '@components/confirmScreen';
import useWalletSelector from '@hooks/useWalletSelector';
import { getShortTruncatedAddress } from '@utils/helper';
import { getShortTruncatedAddress, isLedgerAccount } from '@utils/helper';

import useConfirmedBtcBalance from '@hooks/queries/useConfirmedBtcBalance';
import useBtcClient from '@hooks/useBtcClient';
import useSeedVault from '@hooks/useSeedVault';
import Callout from '@ui-library/callout';
Expand Down Expand Up @@ -230,6 +232,7 @@ const ButtonImage = styled.img((props) => ({
}));

const DEFAULT_FEE_RATE = 8;
const MAX_REPEATS = 24;

function CreateInscription() {
const { t } = useTranslation('translation', { keyPrefix: 'INSCRIPTION_REQUEST' });
Expand Down Expand Up @@ -261,10 +264,11 @@ function CreateInscription() {
} = payload as CreateInscriptionPayload | CreateRepeatInscriptionsPayload;

const { repeat } = payload as CreateRepeatInscriptionsPayload;
const showOver24RepeatsError = !Number.isNaN(repeat) && repeat > 24;
const showOver24RepeatsError = !Number.isNaN(repeat) && repeat > MAX_REPEATS;

const [utxos, setUtxos] = useState<UTXO[] | undefined>();
const [showFeeSettings, setShowFeeSettings] = useState(false);
const [showConfirmedBalanceError, setShowConfirmedBalanceError] = useState(false);
const [feeRate, setFeeRate] = useState(suggestedMinerFeeRate ?? DEFAULT_FEE_RATE);
const [feeRates, setFeeRates] = useState<BtcFeeResponse>();
const { getSeed } = useSeedVault();
Expand Down Expand Up @@ -381,8 +385,11 @@ function CreateInscription() {
inscriptionValue,
} = commitValueBreakdown ?? {};

const { confirmedBalance, isLoading: confirmedBalanceLoading } = useConfirmedBtcBalance();

const chainFee = (revealChainFee ?? 0) + (commitChainFee ?? 0);
const totalFee = (revealServiceFee ?? 0) + (externalServiceFee ?? 0) + chainFee;

const showTotalFee = totalFee !== chainFee;

const toFiat = (value: number | string = 0) =>
Expand All @@ -392,6 +399,20 @@ function CreateInscription() {
.plus(new BigNumber(totalInscriptionValue ?? 0))
.toString();

const errorCode = feeErrorCode || executeErrorCode;

const isLoading = utxos === undefined || inscriptionFeesLoading;

useEffect(() => {
const showConfirmError =
!isLoading &&
!confirmedBalanceLoading &&
errorCode !== InscriptionErrorCode.INSUFFICIENT_FUNDS &&
confirmedBalance !== undefined &&
Number(bundlePlusFees) > confirmedBalance;
setShowConfirmedBalanceError(!!showConfirmError);
}, [confirmedBalance, errorCode, bundlePlusFees, isLoading, confirmedBalanceLoading]);

if (complete && revealTransactionId) {
const onClose = () => {
const response = {
Expand Down Expand Up @@ -420,9 +441,12 @@ function CreateInscription() {
return <CompleteScreen txId={revealTransactionId} onClose={onClose} network={network} />;
}

const errorCode = feeErrorCode || executeErrorCode;

const isLoading = utxos === undefined || inscriptionFeesLoading;
const disableConfirmButton =
!!errorCode ||
isExecuting ||
showOver24RepeatsError ||
showConfirmedBalanceError ||
isLedgerAccount(selectedAccount);

return (
<ConfirmScreen
Expand All @@ -431,7 +455,7 @@ function CreateInscription() {
cancelText={t('CANCEL_BUTTON')}
confirmText={!errorCode ? t('CONFIRM_BUTTON') : t(`ERRORS.SHORT.${errorCode}`)}
loading={isExecuting || isLoading}
disabled={!!errorCode || isExecuting || showOver24RepeatsError}
disabled={disableConfirmButton}
isError={!!errorCode || showOver24RepeatsError}
>
<OuterContainer>
Expand All @@ -440,7 +464,16 @@ function CreateInscription() {
<Title>{t('TITLE')}</Title>
<SubTitle>{t('SUBTITLE', { name: appName ?? '' })}</SubTitle>
{showOver24RepeatsError && (
<StyledCallout variant="danger" bodyText={t('ERRORS.TOO_MANY_REPEATS')} />
<StyledCallout
variant="danger"
bodyText={t('ERRORS.TOO_MANY_REPEATS', { maxRepeats: MAX_REPEATS })}
/>
)}
{showConfirmedBalanceError && (
<StyledCallout variant="danger" bodyText={t('ERRORS.UNCONFIRMED_UTXO')} />
)}
{isLedgerAccount(selectedAccount) && (
<StyledCallout variant="danger" bodyText={t('ERRORS.LEDGER_INSCRIPTION')} />
)}
<CardContainer bottomPadding>
<CardRow>
Expand Down
4 changes: 2 additions & 2 deletions src/app/screens/sendStx/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
import { useMutation } from '@tanstack/react-query';
import { replaceCommaByDot } from '@utils/helper';
import BigNumber from 'bignumber.js';
import { useEffect,useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation,useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import TopRow from '../../components/topRow';

function SendStxScreen() {
Expand Down
2 changes: 1 addition & 1 deletion src/app/screens/signatureRequest/clarityMessageView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { buf2hex } from '@secretkeylabs/xverse-core';
import { ClarityType, ClarityValue, cvToString,principalToString } from '@stacks/transactions';
import { ClarityType, ClarityValue, cvToString, principalToString } from '@stacks/transactions';
import styled from 'styled-components';

const Container = styled.div<{ isRoot: boolean }>((props) => ({
Expand Down
2 changes: 1 addition & 1 deletion src/app/screens/signatureRequest/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import useSignatureRequest, {
import useWalletReducer from '@hooks/useWalletReducer';
import useWalletSelector from '@hooks/useWalletSelector';
import Transport from '@ledgerhq/hw-transport-webusb';
import { bip0322Hash, hashMessage, signStxMessage, buf2hex } from '@secretkeylabs/xverse-core';
import { bip0322Hash, buf2hex, hashMessage, signStxMessage } from '@secretkeylabs/xverse-core';
import { SignaturePayload, StructuredDataSignaturePayload } from '@stacks/connect';
import { getNetworkType, getTruncatedAddress, isHardwareAccount } from '@utils/helper';
import { handleBip322LedgerMessageSigning, signatureVrsToRsv } from '@utils/ledger';
Expand Down
2 changes: 1 addition & 1 deletion src/app/screens/swap/useSwap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import useStxPendingTxData from '@hooks/queries/useStxPendingTxData';
import useWalletSelector from '@hooks/useWalletSelector';
import { SwapConfirmationInput } from '@screens/swap/swapConfirmation/useConfirmSwap';
import {
buf2hex,
FungibleToken,
getNewNonce,
getNonce,
microstacksToStx,
setNonce,
buf2hex
} from '@secretkeylabs/xverse-core';
import { AnchorMode, makeUnsignedContractCall, PostConditionMode } from '@stacks/transactions';
import { AlexSDK, Currency } from 'alex-sdk';
Expand Down
7 changes: 5 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@
"CONITNUE": "Continue",
"BACK": "Back",
"HIGH_FEE_WARNING_TEXT": "The estimated transaction fee for this transaction is very high.",
"UNCONFIRMED_BALANCE_WARNING": "You are spending unconfirmed outputs in this transaction. This may lower the effective fee rate causing delays in transaction confirmation",
"LEDGER": {
"CONNECT": {
"TITLE": "Connect your hardware wallet",
Expand Down Expand Up @@ -973,7 +974,7 @@
},
"LONG": {
"INVALID_JSON_CONTENT": "The content is not valid JSON. Please verify the content of your inscription.",
"INSUFFICIENT_FUNDS": "You have insufficient confirmed funds to process this transaction. This may be due to unconfirmed outputs in your balance. Please add more funds or try with a lower fee rate",
"INSUFFICIENT_FUNDS": "Your balance is insufficient to process this transaction. Please add more funds to your wallet or try with a lower fee rate",
"INVALID_FEE_RATE": "The fee applied to this transaction is too low. To apply a higher fee, select edit fees at the bottom of this screen.",
"INVALID_SERVICE_FEE_CONFIG": "The calling application sent an invalid payload. Please contact the application developer.",
"INVALID_CONTENT": "The ordinal content you are trying to inscribe is not valid. Please verify the content of your inscription.",
Expand All @@ -982,7 +983,9 @@
"FAILED_TO_FINALIZE": "The inscription transaction failed to finalize. Please try again or contact support.",
"SERVER_ERROR": "An unknown server error occurred. Please try again or contact support."
},
"TOO_MANY_REPEATS": "You can only create up to 24 inscriptions in a single request"
"TOO_MANY_REPEATS": "You can only create up to {{maxRepeats}} inscriptions in a single request",
"UNCONFIRMED_UTXO": "Some of your balance consists of unconfirmed outputs. You don't have enough confirmed funds to proceed with this transaction",
"LEDGER_INSCRIPTION": "This inscription service is not compatible with Ledger accounts. Please switch to a standard account to inscribe."
}
},
"ERROR_SCREEN": {
Expand Down
Loading