diff --git a/app/constants/index.ts b/app/constants/index.ts index 9d7d1d6fe..5274633a6 100644 --- a/app/constants/index.ts +++ b/app/constants/index.ts @@ -72,6 +72,8 @@ export const LATEST_LEDGER_VERSION_MINOR = 14; export const EARLIEST_SUPPORTED_LEDGER_VERSION = '0.11.0'; +export const FEE_RATE = 400; + export const DEFAULT_POLLING_INTERVAL = 10_000; export const SEND_MANY_CONTACT_ID = 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.send-many-memo'; diff --git a/app/hooks/use-calculate-fee.ts b/app/hooks/use-calculate-fee.ts index 9b6f1027a..933c44126 100644 --- a/app/hooks/use-calculate-fee.ts +++ b/app/hooks/use-calculate-fee.ts @@ -1,18 +1,13 @@ import { useCallback } from 'react'; import BigNumber from 'bignumber.js'; -import { useFetchFeeRate } from './use-fetch-fee-rate'; - -const defaultFeeRate = 1; +import { useFeeRate } from './use-fee-rate'; /** * Returns a function calculating how much of a fee should be set * based on the number of bytes of the transaction. */ export function useCalculateFee() { - const { feeRate } = useFetchFeeRate(); - return useCallback( - (bytes: number) => new BigNumber(feeRate ?? defaultFeeRate).multipliedBy(bytes), - [feeRate] - ); + const { feeRate } = useFeeRate(); + return useCallback((bytes: number) => new BigNumber(feeRate).multipliedBy(bytes), [feeRate]); } diff --git a/app/hooks/use-fee-rate.ts b/app/hooks/use-fee-rate.ts new file mode 100644 index 000000000..c913b23ed --- /dev/null +++ b/app/hooks/use-fee-rate.ts @@ -0,0 +1,6 @@ +import { useMemo } from 'react'; +import { FEE_RATE } from '@constants/index'; + +export function useFeeRate() { + return useMemo(() => ({ feeRate: FEE_RATE }), []); +} diff --git a/app/hooks/use-fetch-fee-rate.ts b/app/hooks/use-fetch-fee-rate.ts deleted file mode 100644 index 9b0adb0bd..000000000 --- a/app/hooks/use-fetch-fee-rate.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useMemo } from 'react'; -import { useQuery, UseQueryOptions } from 'react-query'; - -import { ApiResource } from '@models'; -import { useApi } from './use-api'; - -const queryArgs: UseQueryOptions = { - refetchInterval: 300_000, -}; - -export function useFetchFeeRate() { - const api = useApi(); - - const { data: feeRate } = useQuery( - ApiResource.FeeRate, - () => api.getFeeRate().then(({ data }) => data), - queryArgs - ); - - return useMemo(() => ({ feeRate }), [feeRate]); -} diff --git a/app/modals/delegated-stacking/delegated-stacking-modal.tsx b/app/modals/delegated-stacking/delegated-stacking-modal.tsx index ca242e084..7e0ed697b 100644 --- a/app/modals/delegated-stacking/delegated-stacking-modal.tsx +++ b/app/modals/delegated-stacking/delegated-stacking-modal.tsx @@ -7,6 +7,7 @@ import BN from 'bn.js'; import { RootState } from '@store/index'; import routes from '@constants/routes.json'; +import { POOLED_STACKING_TX_SIZE_BYTES } from '@constants/index'; import { selectPoxInfo } from '@store/stacking'; import { safeAwait } from '@utils/safe-await'; @@ -22,6 +23,7 @@ import { useMempool } from '@hooks/use-mempool'; import { PostCoreNodeTransactionsError } from '@stacks/stacks-blockchain-api-types'; import { TxSigningModal } from '@modals/tx-signing-modal/tx-signing-modal'; +import { useCalculateFee } from '@hooks/use-calculate-fee'; interface StackingModalProps { delegateeStxAddress: string; @@ -40,6 +42,7 @@ export const DelegatedStackingModal: FC = props => { const { broadcastTx, isBroadcasting } = useBroadcastTx(); const { refetch } = useMempool(); + const calcFee = useCalculateFee(); const api = useApi(); @@ -49,16 +52,20 @@ export const DelegatedStackingModal: FC = props => { const [nodeResponseError, setNodeResponseError] = useState(null); + const delegationTxOptions = useMemo((): ContractCallOptions => { if (!poxInfo) throw new Error('`poxInfo` undefined'); console.log(amountToStack.toString()); - return stackingClient.getDelegateOptions({ - amountMicroStx: new BN(amountToStack.toString()), - contract: poxInfo.contract_id, - delegateTo: delegateeStxAddress, - untilBurnBlockHeight: burnHeight, - }); - }, [amountToStack, burnHeight, delegateeStxAddress, poxInfo, stackingClient]); + return { + ...stackingClient.getDelegateOptions({ + amountMicroStx: new BN(amountToStack.toString()), + contract: poxInfo.contract_id, + delegateTo: delegateeStxAddress, + untilBurnBlockHeight: burnHeight, + }), + fee: new BN(calcFee(POOLED_STACKING_TX_SIZE_BYTES).toString()), + }; + }, [amountToStack, burnHeight, calcFee, delegateeStxAddress, poxInfo, stackingClient]); const delegateStx = (signedTx: StacksTransaction) => broadcastTx({ diff --git a/app/modals/revoke-delegation/revoke-delegation-modal.tsx b/app/modals/revoke-delegation/revoke-delegation-modal.tsx index 179e980ac..5f7037c8d 100644 --- a/app/modals/revoke-delegation/revoke-delegation-modal.tsx +++ b/app/modals/revoke-delegation/revoke-delegation-modal.tsx @@ -2,10 +2,12 @@ import React, { FC, useState, useCallback, useMemo } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { ContractCallOptions, StacksTransaction } from '@stacks/transactions'; import { useHotkeys } from 'react-hotkeys-hook'; +import BN from 'bn.js'; import { selectPoxInfo } from '@store/stacking'; import { PostCoreNodeTransactionsError } from '@stacks/stacks-blockchain-api-types'; +import { REVOKE_DELEGATION_TX_SIZE_BYTES } from '@constants/index'; import { safeAwait } from '@utils/safe-await'; import { homeActions } from '@store/home'; import { useStackingClient } from '@hooks/use-stacking-client'; @@ -14,6 +16,7 @@ import { watchForNewTxToAppear } from '@api/watch-tx-to-appear-in-api'; import { useBroadcastTx } from '@hooks/use-broadcast-tx'; import { useMempool } from '@hooks/use-mempool'; import { TxSigningModal } from '@modals/tx-signing-modal/tx-signing-modal'; +import { useCalculateFee } from '@hooks/use-calculate-fee'; export const RevokeDelegationModal: FC = () => { const dispatch = useDispatch(); @@ -25,6 +28,7 @@ export const RevokeDelegationModal: FC = () => { const { stackingClient } = useStackingClient(); const { broadcastTx, isBroadcasting } = useBroadcastTx(); const { refetch: refetchMempool } = useMempool(); + const calcFee = useCalculateFee(); const poxInfo = useSelector(selectPoxInfo); const [nodeResponseError, setNodeResponseError] = @@ -32,8 +36,11 @@ export const RevokeDelegationModal: FC = () => { const revocationTxOptions = useMemo((): ContractCallOptions => { if (!poxInfo) throw new Error('`poxInfo` undefined'); - return stackingClient.getRevokeDelegateStxOptions(poxInfo.contract_id); - }, [poxInfo, stackingClient]); + return { + ...stackingClient.getRevokeDelegateStxOptions(poxInfo.contract_id), + fee: new BN(calcFee(REVOKE_DELEGATION_TX_SIZE_BYTES).toString()), + }; + }, [calcFee, poxInfo, stackingClient]); const revokeDelegation = useCallback( (signedTx: StacksTransaction) => diff --git a/app/modals/send-stx/send-stx-modal.tsx b/app/modals/send-stx/send-stx-modal.tsx index 656d1b034..0e60c702d 100644 --- a/app/modals/send-stx/send-stx-modal.tsx +++ b/app/modals/send-stx/send-stx-modal.tsx @@ -41,6 +41,7 @@ import { useBroadcastTx } from '@hooks/use-broadcast-tx'; import { TransactionError } from '@modals/components/transaction-error'; import { ModalHeader } from '@modals/components/modal-header'; import { HomeSelectors } from 'app/tests/features/home.selectors'; +import { useCalculateFee } from '@hooks/use-calculate-fee'; interface TxModalProps { balance: string; @@ -66,6 +67,7 @@ export const SendStxModal: FC = ({ address, isOpen }) => { const [total, setTotal] = useState(new BigNumber(0)); const { availableBalance: balance } = useBalance(); const { broadcastTx, isBroadcasting } = useBroadcastTx(); + const calcFee = useCalculateFee(); const [feeEstimateError, setFeeEstimateError] = useState(null); @@ -144,27 +146,23 @@ export const SendStxModal: FC = ({ address, isOpen }) => { }), noMemoRequired: yup.boolean().required(), }), - onSubmit: async () => { + onSubmit() { setLoading(true); setFeeEstimateError(null); - const [error, feeRate] = await safeAwait(new Api(stacksApi.baseUrl).getFeeRate()); - if (error) { - setFeeEstimateError('Error fetching estimate fees'); - } - if (feeRate) { - const fee = new BigNumber(feeRate.data).multipliedBy(STX_TRANSFER_TX_SIZE_BYTES); - const amount = stxToMicroStx(form.values.amount); - setFee(fee); - setTotal(amount.plus(fee.toString())); - setAmount(amount); - setStep(TxModalStep.PreviewTx); - } + const fee = calcFee(STX_TRANSFER_TX_SIZE_BYTES); + const amount = stxToMicroStx(form.values.amount); + setFee(fee); + setTotal(amount.plus(fee.toString())); + setAmount(amount); + setStep(TxModalStep.PreviewTx); + setTxDetails({ recipient: form.values.recipient, network: stacksNetwork, amount: new BN(stxToMicroStx(form.values.amount || 0).toString()), memo: form.values.memo, nonce: new BN(nonce), + fee: new BN(fee.toString()), }); setLoading(false); },