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

Fees popover in transaction modal #317

Merged
merged 21 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
<List spacing={3} mt={10}>
Expand All @@ -33,11 +48,17 @@ function StakeDetails({
currency: "usd",
}}
/>
<TransactionDetailsAmountItem
label="Protocol fee (0.01%)"
<FeesDetailsAmountItem
label="Fees"
sublabel="How are fees calculated?"
// TODO: Add `Bitcoin Network fee` (funding transaction fee selected by
// the user) and figure out how to estimate this fee.
tooltip={
<FeesTooltip fees={restFees} mapFeeToLabel={mapDepositFeeToLabel} />
}
from={{
currency,
amount: details?.protocolFee,
amount: total,
}}
to={{
currency: "usd",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@ function UnstakeDetails({ currency }: { currency: CurrencyType }) {
currency: "usd",
}}
/>
<TransactionDetailsAmountItem
label="Protocol fee (0.01%)"
{/* TODO: Uncomment when unstaking fees are ready. */}
{/* <FeesDetailsAmountItem
label="Fees"
sublabel="How are fees calculated?"
tooltip={<FeesTooltip fees={{}} />}
from={{
currency,
amount: details?.protocolFee,
amount: transactionFee.total,
}}
to={{
currency: "usd",
}}
/>
/> */}
<TransactionDetailsAmountItem
label="Approximately unstaked tokens"
from={{
Expand Down
33 changes: 33 additions & 0 deletions dapp/src/components/TransactionModal/FeesTooltip/FeesTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react"
import { Info } from "#/assets/icons"
import { Icon, Tooltip, List } from "@chakra-ui/react"
import { FeesTooltipItem } from "./FeesTooltipItem"

type Fee = { [key: string]: bigint }

type Props<F extends Fee> = {
fees: F
mapFeeToLabel: (feeName: keyof F) => string
}

export function FeesTooltip<F extends Fee>({ fees, mapFeeToLabel }: Props<F>) {
return (
<Tooltip
placement="right"
label={
<List spacing={0.5} minW={60}>
{Object.entries(fees).map(([feeKey, feeValue]) => (
<FeesTooltipItem
key={feeKey}
label={mapFeeToLabel(feeKey)}
amount={feeValue}
currency="bitcoin"
/>
))}
</List>
}
>
<Icon as={Info} ml={2} boxSize={4} cursor="pointer" color="grey.400" />
</Tooltip>
)
}
Original file line number Diff line number Diff line change
@@ -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 (
<ListItem display="flex" justifyContent="space-between">
<TextSm color="white">{label}</TextSm>
<CurrencyBalance
size="sm"
amount={amount}
color="gold.300"
balanceFontWeight="semibold"
symbolFontWeight="semibold"
{...props}
/>
</ListItem>
)
}
1 change: 1 addition & 0 deletions dapp/src/components/TransactionModal/FeesTooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./FeesTooltip"
43 changes: 43 additions & 0 deletions dapp/src/components/shared/FeesDetails/FeesItem.tsx
Original file line number Diff line number Diff line change
@@ -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<FeesDetailsItemProps, "label" | "sublabel" | "tooltip">

function FeesDetailsAmountItem({
label,
sublabel,
tooltip,
from,
to,
}: FeesDetailsItemAmountItemProps) {
return (
<FeesDetailsItem
label={label}
sublabel={sublabel}
tooltip={tooltip}
alignItems="start"
>
<Flex flexDirection="column" alignItems="end">
<CurrencyBalanceWithConversion
from={{
size: "md",
...from,
}}
to={{
size: "sm",
fontWeight: "medium",
color: "grey.500",
...to,
}}
/>
</Flex>
</FeesDetailsItem>
)
}

export default FeesDetailsAmountItem
45 changes: 45 additions & 0 deletions dapp/src/components/shared/FeesDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<ListItem
display="flex"
justifyContent="space-between"
alignItems="center"
{...listItemProps}
>
<VStack alignItems="start" gap={0}>
<TextMd
display="flex"
alignItems="center"
fontWeight="semibold"
color="grey.700"
>
{label}
{tooltip}
</TextMd>
{sublabel && <TextSm color="grey.400">{sublabel}</TextSm>}
</VStack>
{value ? <TextMd color="grey.700">{value}</TextMd> : children}
</ListItem>
)
}

export default FeesDetailsItem
1 change: 1 addition & 0 deletions dapp/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export * from "./useTimeout"
export * from "./useCountdown"
export * from "./useActivities"
export * from "./useSize"
export * from "./useTransactionFee"
33 changes: 33 additions & 0 deletions dapp/src/hooks/useTransactionFee.ts
Original file line number Diff line number Diff line change
@@ -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<DepositFee>(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
}
5 changes: 5 additions & 0 deletions dapp/src/types/fee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type DepositFee = {
tbtc: bigint
acre: bigint
total: bigint
}
1 change: 1 addition & 0 deletions dapp/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from "./coingecko"
export * from "./time"
export * from "./size"
export * from "./toast"
export * from "./fee"
Loading