diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx index 9f6d8ebf3..6fc73f611 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx @@ -1,9 +1,23 @@ import React from "react" import { List } from "@chakra-ui/react" import TransactionDetailsAmountItem from "#/components/shared/TransactionDetails/AmountItem" +import FeesDetailsAmountItem from "#/components/shared/FeesDetails/FeesItem" import { useTokenAmountFormValue } from "#/components/shared/TokenAmountForm/TokenAmountFormBase" +import { FeesTooltip } from "#/components/TransactionModal/FeesTooltip" import { useTransactionDetails } from "#/hooks" -import { CurrencyType } from "#/types" +import { CurrencyType, DepositFee } from "#/types" +import { useTransactionFee } from "#/hooks/useTransactionFee" + +const mapDepositFeeToLabel = (feeId: keyof DepositFee) => { + switch (feeId) { + case "acre": + return "Acre protocol fees" + case "tbtc": + return "tBTC bridge fees" + default: + return "" + } +} function StakeDetails({ currency, @@ -20,6 +34,7 @@ function StakeDetails({ // Let's not calculate the details of the transaction when the value is not valid. const amount = !isMaximumValueExceeded && isMinimumValueFulfilled ? value : 0n const details = useTransactionDetails(amount) + const { total, ...restFees } = useTransactionFee(amount) return ( @@ -33,11 +48,17 @@ function StakeDetails({ currency: "usd", }} /> - + } from={{ currency, - amount: details?.protocolFee, + amount: total, }} to={{ currency: "usd", diff --git a/dapp/src/components/TransactionModal/ActiveUnstakingStep/UnstakeFormModal/UnstakeDetails.tsx b/dapp/src/components/TransactionModal/ActiveUnstakingStep/UnstakeFormModal/UnstakeDetails.tsx index 850f88561..844fe9c43 100644 --- a/dapp/src/components/TransactionModal/ActiveUnstakingStep/UnstakeFormModal/UnstakeDetails.tsx +++ b/dapp/src/components/TransactionModal/ActiveUnstakingStep/UnstakeFormModal/UnstakeDetails.tsx @@ -21,16 +21,19 @@ function UnstakeDetails({ currency }: { currency: CurrencyType }) { currency: "usd", }} /> - } from={{ currency, - amount: details?.protocolFee, + amount: transactionFee.total, }} to={{ currency: "usd", }} - /> + /> */} = { + fees: F + mapFeeToLabel: (feeName: keyof F) => string +} + +export function FeesTooltip({ fees, mapFeeToLabel }: Props) { + return ( + + {Object.entries(fees).map(([feeKey, feeValue]) => ( + + ))} + + } + > + + + ) +} diff --git a/dapp/src/components/TransactionModal/FeesTooltip/FeesTooltipItem.tsx b/dapp/src/components/TransactionModal/FeesTooltip/FeesTooltipItem.tsx new file mode 100644 index 000000000..3250ffc80 --- /dev/null +++ b/dapp/src/components/TransactionModal/FeesTooltip/FeesTooltipItem.tsx @@ -0,0 +1,27 @@ +import React from "react" +import { ListItem } from "@chakra-ui/react" +import { + CurrencyBalance, + CurrencyBalanceProps, +} from "#/components/shared/CurrencyBalance" +import { TextSm } from "#/components/shared/Typography" + +export type FeesItemType = CurrencyBalanceProps & { + label: string +} + +export function FeesTooltipItem({ label, amount, ...props }: FeesItemType) { + return ( + + {label} + + + ) +} diff --git a/dapp/src/components/TransactionModal/FeesTooltip/index.ts b/dapp/src/components/TransactionModal/FeesTooltip/index.ts new file mode 100644 index 000000000..9d08672e3 --- /dev/null +++ b/dapp/src/components/TransactionModal/FeesTooltip/index.ts @@ -0,0 +1 @@ +export * from "./FeesTooltip" diff --git a/dapp/src/components/shared/FeesDetails/FeesItem.tsx b/dapp/src/components/shared/FeesDetails/FeesItem.tsx new file mode 100644 index 000000000..6697e7d6d --- /dev/null +++ b/dapp/src/components/shared/FeesDetails/FeesItem.tsx @@ -0,0 +1,43 @@ +import React, { ComponentProps } from "react" +import { Flex } from "@chakra-ui/react" +import FeesDetailsItem, { FeesDetailsItemProps } from "." +import { CurrencyBalanceWithConversion } from "../CurrencyBalanceWithConversion" + +type FeesDetailsItemAmountItemProps = ComponentProps< + typeof CurrencyBalanceWithConversion +> & + Pick + +function FeesDetailsAmountItem({ + label, + sublabel, + tooltip, + from, + to, +}: FeesDetailsItemAmountItemProps) { + return ( + + + + + + ) +} + +export default FeesDetailsAmountItem diff --git a/dapp/src/components/shared/FeesDetails/index.tsx b/dapp/src/components/shared/FeesDetails/index.tsx new file mode 100644 index 000000000..c9e0a6028 --- /dev/null +++ b/dapp/src/components/shared/FeesDetails/index.tsx @@ -0,0 +1,45 @@ +import React from "react" +import { ListItem, ListItemProps, VStack } from "@chakra-ui/react" +import { TextMd, TextSm } from "../Typography" + +export type FeesDetailsItemProps = { + label: string + sublabel: string + value?: string + tooltip: React.ReactElement + children?: React.ReactNode +} & ListItemProps + +function FeesDetailsItem({ + label, + sublabel, + tooltip, + value, + children, + ...listItemProps +}: FeesDetailsItemProps) { + return ( + + + + {label} + {tooltip} + + {sublabel && {sublabel}} + + {value ? {value} : children} + + ) +} + +export default FeesDetailsItem diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 5651557f2..1c483932d 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -23,3 +23,4 @@ export * from "./useTimeout" export * from "./useCountdown" export * from "./useActivities" export * from "./useSize" +export * from "./useTransactionFee" diff --git a/dapp/src/hooks/useTransactionFee.ts b/dapp/src/hooks/useTransactionFee.ts new file mode 100644 index 000000000..b650d7fbe --- /dev/null +++ b/dapp/src/hooks/useTransactionFee.ts @@ -0,0 +1,33 @@ +import { useAcreContext } from "#/acre-react/hooks" +import { logPromiseFailure } from "#/utils" +import { useEffect, useState } from "react" +import { DepositFee } from "#/types" +import { useAppDispatch } from "./store" + +const initialDepositFee = { + tbtc: 0n, + acre: 0n, + total: 0n, +} + +export function useTransactionFee(amount?: bigint) { + const [depositFee, setDepositFee] = useState(initialDepositFee) + const { acre } = useAcreContext() + const dispatch = useAppDispatch() + + useEffect(() => { + if (!amount) { + setDepositFee(initialDepositFee) + } else { + const getEstimatedDepositFee = async () => { + if (!acre) return + const fee = await acre.staking.estimateDepositFee(amount) + + setDepositFee(fee) + } + logPromiseFailure(getEstimatedDepositFee()) + } + }, [acre, dispatch, amount]) + + return depositFee +} diff --git a/dapp/src/types/fee.ts b/dapp/src/types/fee.ts new file mode 100644 index 000000000..e35b33004 --- /dev/null +++ b/dapp/src/types/fee.ts @@ -0,0 +1,5 @@ +export type DepositFee = { + tbtc: bigint + acre: bigint + total: bigint +} diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index 16d93da3b..b3b047b18 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -13,3 +13,4 @@ export * from "./coingecko" export * from "./time" export * from "./size" export * from "./toast" +export * from "./fee"