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 14 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,12 @@
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 { useTransactionFee } from "#/hooks/useTransactionFee"

function StakeDetails({
currency,
Expand All @@ -20,6 +23,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 transactionFee = useTransactionFee(amount)

return (
<List spacing={3} mt={10}>
Expand All @@ -33,11 +37,13 @@ function StakeDetails({
currency: "usd",
}}
/>
<TransactionDetailsAmountItem
label="Protocol fee (0.01%)"
<FeesDetailsAmountItem
label="Fees"
sublabel="How are fees calculated?"
tooltip={<FeesTooltip />}
from={{
currency,
amount: details?.protocolFee,
amount: transactionFee.total,
}}
to={{
currency: "usd",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
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 { useTransactionDetails } from "#/hooks"
import { FeesTooltip } from "#/components/TransactionModal/FeesTooltip"
import { useTransactionDetails, useTransactionFee } from "#/hooks"
import { CurrencyType } from "#/types"

function UnstakeDetails({ currency }: { currency: CurrencyType }) {
const value = useTokenAmountFormValue()
const details = useTransactionDetails(value ?? 0n)
const transactionFee = useTransactionFee(value)

return (
<List spacing={3} mt={10}>
Expand All @@ -21,11 +24,13 @@ function UnstakeDetails({ currency }: { currency: CurrencyType }) {
currency: "usd",
}}
/>
<TransactionDetailsAmountItem
label="Protocol fee (0.01%)"
<FeesDetailsAmountItem
label="Fees"
sublabel="How are fees calculated?"
tooltip={<FeesTooltip />}
from={{
currency,
amount: details?.protocolFee,
amount: transactionFee.total,
}}
to={{
currency: "usd",
Expand Down
43 changes: 43 additions & 0 deletions dapp/src/components/TransactionModal/FeesTooltip/FeesTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react"
import { Info } from "#/assets/icons"
import { Icon, Tooltip, List } from "@chakra-ui/react"
import { DepositFee } from "#/types"
import { useTransactionFee } from "#/hooks/useTransactionFee"
import { FeesTooltipItem } from "./FeesTooltipItem"

const mapFeeKeyToLabel = (feeId: keyof DepositFee) => {
switch (feeId) {
case "acre":
return "Acre protocol fees"
case "tbtc":
return "tBTC bridge fees"
case "total":
return "Bitcoin network fees"
default:
return ""
}
}

export function FeesTooltip() {
const estimatedDepositFee = useTransactionFee()

return (
<Tooltip
placement="right"
label={
<List spacing={0.5} minW={60}>
{Object.entries(estimatedDepositFee).map(([feeKey, feeValue]) => (
<FeesTooltipItem
key={feeKey}
label={mapFeeKeyToLabel(feeKey as keyof DepositFee)}
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
15 changes: 11 additions & 4 deletions dapp/src/components/shared/TransactionDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react"
import { ListItem, ListItemProps } from "@chakra-ui/react"
import { ListItem, ListItemProps, VStack } from "@chakra-ui/react"
import { TextMd } from "../Typography"

export type TransactionDetailsItemProps = {
Expand All @@ -21,9 +21,16 @@ function TransactionDetailsItem({
alignItems="center"
{...listItemProps}
>
<TextMd fontWeight="semibold" color="grey.700">
{label}
</TextMd>
<VStack alignItems="start" gap={0}>
<TextMd
display="flex"
alignItems="center"
fontWeight="semibold"
color="grey.700"
>
{label}
</TextMd>
</VStack>
{value ? <TextMd color="grey.700">{value}</TextMd> : children}
</ListItem>
)
Expand Down
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"
1 change: 1 addition & 0 deletions dapp/src/hooks/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./useAppDispatch"
export * from "./useAppSelector"
export * from "./useEstimatedBTCBalance"
export * from "./useSharesBalance"
export * from "./useEstimatedDepositFee"
export * from "./useMinDepositAmount"
6 changes: 6 additions & 0 deletions dapp/src/hooks/store/useEstimatedDepositFee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { selectEstimatedDepositFee } from "#/store/btc"
import { useAppSelector } from "./useAppSelector"

export function useEstimatedDepositFee() {
return useAppSelector(selectEstimatedDepositFee)
}
35 changes: 35 additions & 0 deletions dapp/src/hooks/useTransactionFee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useAcreContext } from "#/acre-react/hooks"
import { setEstimatedDepositFee } from "#/store/btc"
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)

dispatch(setEstimatedDepositFee(fee))
setDepositFee(fee)
}
logPromiseFailure(getEstimatedDepositFee())
}
}, [acre, dispatch, amount])

return depositFee
}
4 changes: 4 additions & 0 deletions dapp/src/store/btc/btcSelector.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DepositFee } from "#/types"
import { RootState } from ".."

export const selectEstimatedBtcBalance = (state: RootState): bigint =>
Expand All @@ -6,6 +7,9 @@ export const selectEstimatedBtcBalance = (state: RootState): bigint =>
export const selectSharesBalance = (state: RootState): bigint =>
state.btc.sharesBalance

export const selectEstimatedDepositFee = (state: RootState): DepositFee =>
state.btc.estimateDepositFee

export const selectBtcUsdPrice = (state: RootState): number =>
state.btc.usdPrice

Expand Down
18 changes: 16 additions & 2 deletions dapp/src/store/btc/btcSlice.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { DepositFee } from "#/types"
import { fetchBTCPriceUSD } from "./btcThunk"

type BtcState = {
estimatedBtcBalance: bigint
sharesBalance: bigint
estimateDepositFee: DepositFee
isLoadingPriceUSD: boolean
usdPrice: number
minDepositAmount: bigint
Expand All @@ -12,6 +14,11 @@ type BtcState = {
const initialState: BtcState = {
estimatedBtcBalance: 0n,
sharesBalance: 0n,
estimateDepositFee: {
tbtc: 0n,
acre: 0n,
total: 0n,
},
isLoadingPriceUSD: false,
usdPrice: 0,
minDepositAmount: 0n,
Expand All @@ -28,6 +35,9 @@ export const btcSlice = createSlice({
setEstimatedBtcBalance(state, action: PayloadAction<bigint>) {
state.estimatedBtcBalance = action.payload
},
setEstimatedDepositFee(state, action: PayloadAction<DepositFee>) {
state.estimateDepositFee = action.payload
},
setMinDepositAmount(state, action: PayloadAction<bigint>) {
state.minDepositAmount = action.payload
},
Expand All @@ -49,5 +59,9 @@ export const btcSlice = createSlice({
},
})

export const { setSharesBalance, setEstimatedBtcBalance, setMinDepositAmount } =
btcSlice.actions
export const {
setSharesBalance,
setEstimatedBtcBalance,
setEstimatedDepositFee,
setMinDepositAmount,
} = btcSlice.actions
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