Skip to content

Commit

Permalink
feat: mf-6440 show token balance and add max button (#11822)
Browse files Browse the repository at this point in the history
* fix: show network full name

* fix: transaction fee

* fix: logic

* feat: mf-6440 show token balance and add max button

* fix: run codegen

---------

Co-authored-by: swkatmask <swkatmask@users.noreply.github.com>
  • Loading branch information
swkatmask and swkatmask authored Oct 1, 2024
1 parent b6b0e2d commit e129774
Show file tree
Hide file tree
Showing 21 changed files with 485 additions and 376 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ interface Props extends TypographyProps {
chainId: ChainId
gasPrice: BigNumber.Value | undefined
gasLimit: BigNumber.Value | undefined
/** If gasFee is provided, we can use it directly */
gasFee?: BigNumber.Value
}
export function GasCost({ chainId, gasPrice, gasLimit, ...rest }: Props) {
export function GasCost({ chainId, gasPrice, gasLimit, gasFee: txGasFee, ...rest }: Props) {
const { data: nativeToken } = useNativeToken()
const { gasFee, gasCost } = useGasCost(gasPrice || '0', gasLimit || '1')
const { gasFee, gasCost } = useGasCost(gasPrice || '0', gasLimit || '1', txGasFee)
const tokenCost = `${formatWeiToEther(gasFee)} ${nativeToken?.symbol ?? '--'}`
return (
<Typography {...rest}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ interface Options {

const GasManagerContext = createContext<Options>(null!)
export function GasManager({ children }: PropsWithChildren) {
const { quote, chainId, fromToken, toToken } = useTrade()
const gasLimit = quote?.estimateGasFee ?? '1'
const { quote, mode, bridgeQuote, chainId, fromToken, toToken } = useTrade()
const gasLimit = mode === 'swap' ? quote?.estimateGasFee : bridgeQuote?.routerList[0]?.estimateGasFee
const { gasConfig, setGasConfig, gasOptions, isLoadingGasOptions } = useGasConfig(chainId)
const { data: price } = useNativeTokenPrice(NetworkPluginID.PLUGIN_EVM, { chainId })

Expand All @@ -41,7 +41,7 @@ export function GasManager({ children }: PropsWithChildren) {

const gasFee = useMemo(() => {
const price = gasConfig.gasPrice ?? (gasConfig as EIP1559GasConfig).maxFeePerGas ?? '1'
return multipliedBy(gasLimit, price)
return multipliedBy(gasLimit ?? '1', price)
}, [gasLimit, gasConfig.gasPrice])

const gasCost = useMemo(() => {
Expand Down
14 changes: 13 additions & 1 deletion packages/plugins/Trader/src/SiteAdaptor/trader/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Web3Helper } from '@masknet/web3-helpers'
import { leftShift, TokenType } from '@masknet/web3-shared-base'
import { formatCompact, leftShift, TokenType } from '@masknet/web3-shared-base'
import type { Token } from '../../types/trader.js'
import { SchemaType } from '@masknet/web3-shared-evm'
import type { RouterListItem } from '@masknet/web3-providers/types'
import { type BigNumber } from 'bignumber.js'

const MINIMUM_AMOUNT_RE = /((?:Minimum|Maximum) amount is\s+)(\d+)/
export function fixBridgeMessage(message: string, token?: Web3Helper.FungibleTokenAll) {
Expand Down Expand Up @@ -37,3 +38,14 @@ export function getBridgeLeftSideToken(bridge: RouterListItem) {
export function getBridgeRightSideToken(bridge: RouterListItem) {
return bridge.toDexRouterList[0]?.subRouterList[0]?.toToken
}

export function formatTokenBalance(raw: BigNumber.Value, decimals = 0) {
const balance = leftShift(raw, decimals).toNumber()
return formatCompact(balance, {
minimumSignificantDigits: 2,
maximumSignificantDigits:
balance < 100 ? 4
: balance < 1000 ? 2
: 2,
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { type BigNumber } from 'bignumber.js'
import { useMemo } from 'react'
import { useTrade } from '../contexts/TradeProvider.js'

export function useGasCost(gasPrice: BigNumber.Value, gas: BigNumber.Value) {
export function useGasCost(gasPrice: BigNumber.Value, gas: BigNumber.Value, txGasFee?: BigNumber.Value) {
const { chainId } = useTrade()
const gasFee = useMemo(() => multipliedBy(gas, gasPrice ?? '1'), [gasPrice, gas])
const gasFee = useMemo(() => txGasFee ?? multipliedBy(gas, gasPrice ?? '1'), [gasPrice, gas, txGasFee])
const { data: price } = useNativeTokenPrice(NetworkPluginID.PLUGIN_EVM, { chainId })
const gasCost = useMemo(() => {
if (!price) return ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ export const BridgeConfirm = memo(function BridgeConfirm() {
: null

const Web3 = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId: fromChainId })
const gas = gasConfig.gas ?? transaction?.gasLimit ?? gasLimit
const [{ loading: isSending }, sendBridge] = useAsyncFn(async () => {
if (!transaction?.data) return
return Web3.sendTransaction({
Expand All @@ -245,13 +246,13 @@ export const BridgeConfirm = memo(function BridgeConfirm() {
from: account,
value: transaction.value,
gasPrice: gasConfig.gasPrice ?? transaction.gasPrice,
gas: transaction.gasLimit,
gas,
maxPriorityFeePerGas:
'maxPriorityFeePerGas' in gasConfig && gasConfig.maxFeePerGas ?
gasConfig.maxFeePerGas
: transaction.maxPriorityFeePerGas,
})
}, [transaction, account, gasConfig, Web3])
}, [transaction, account, gasConfig, Web3, gas])

const [isBridgable, errorMessage] = useBridgable()

Expand Down Expand Up @@ -303,7 +304,7 @@ export const BridgeConfirm = memo(function BridgeConfirm() {
className={cx(classes.fromToken, classes.value)}>
-{formatBalance(fromTokenAmount, fromToken?.decimals || 0)} {fromToken?.symbol}
</ProgressiveText>
<Typography className={classes.network}>{fromNetwork?.name}</Typography>
<Typography className={classes.network}>{fromNetwork?.fullName}</Typography>
</div>
</div>
</div>
Expand All @@ -322,7 +323,7 @@ export const BridgeConfirm = memo(function BridgeConfirm() {
<ProgressiveText loading={!toToken} className={cx(classes.toToken, classes.value)}>
+{formatBalance(toTokenAmount, toToken?.decimals || 0)} {toToken?.symbol}
</ProgressiveText>
<Typography className={classes.network}>{toNetwork?.name}</Typography>
<Typography className={classes.network}>{toNetwork?.fullName}</Typography>
</div>
</div>
</div>
Expand All @@ -342,7 +343,7 @@ export const BridgeConfirm = memo(function BridgeConfirm() {
size={16}
/>
<Trans>
{fromNetwork?.name || '--'} to {toNetwork?.name || '--'}
{fromNetwork?.fullName || '--'} to {toNetwork?.fullName || '--'}
</Trans>
</Typography>
</div>
Expand Down Expand Up @@ -503,7 +504,7 @@ than estimated, and any unused funds will remain in the original address.`}>
dexContractAddress: spender,
to: transaction.to,
estimatedTime: bridge?.estimateTime ? +bridge.estimateTime : 0,
gasLimit: gasLimit || gasConfig.gas || '1',
gasLimit: gas!,
gasPrice: gasConfig.gasPrice || '0',
leftSideToken: getBridgeLeftSideToken(bridge),
rightSideToken: getBridgeRightSideToken(bridge),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ export const Confirm = memo(function Confirm() {

const [isSwappable, errorMessage] = useSwappable()
const Web3 = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId })
const gas = gasConfig.gas ?? transaction?.gas ?? gasLimit
const [{ loading: isSending }, sendSwap] = useAsyncFn(async () => {
if (!transaction?.data) return
return Web3.sendTransaction({
Expand All @@ -245,14 +246,14 @@ export const Confirm = memo(function Confirm() {
from: account,
value: transaction.value,
gasPrice: gasConfig.gasPrice ?? transaction.gasPrice,
gas: transaction.gas,
gas,
maxPriorityFeePerGas:
'maxPriorityFeePerGas' in gasConfig && gasConfig.maxFeePerGas ?
gasConfig.maxFeePerGas
: transaction.maxPriorityFeePerGas,
_disableSuccessSnackbar: true,
})
}, [transaction, account, gasConfig, Web3])
}, [transaction, account, gasConfig, Web3, gas])

const [{ isLoadingApproveInfo, isLoadingSpender, isLoadingAllowance, spender }, approveMutation] = useApprove()

Expand Down Expand Up @@ -484,7 +485,7 @@ export const Confirm = memo(function Confirm() {
dexContractAddress: spender,
to: transaction.to,
estimatedTime: estimatedSeconds ?? 10,
gasLimit: gasLimit || gasConfig.gas || '1',
gasLimit: gas!,
gasPrice: gasConfig.gasPrice || '0',
})
if (leaveRef.current) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ export function HistoryView() {
</div>
{toNetwork ?
<Typography className={classes.network}>
{`${network?.name ?? '--'} to ${toNetwork.name ?? '--'}`}
{`${network?.fullName ?? '--'} to ${toNetwork.fullName ?? '--'}`}
</Typography>
: <Typography className={classes.network}>{network?.name ?? '--'}</Typography>}
: <Typography className={classes.network}>{network?.fullName ?? '--'}</Typography>
}
</div>
<div className={classes.result}>
<Typography className={classes.received}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Box, Typography, type BoxProps } from '@mui/material'
import { useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import { bridges, DEFAULT_SLIPPAGE, RoutePaths } from '../../../constants.js'
import { useTrade } from '../../contexts/index.js'
import { useGasManagement, useTrade } from '../../contexts/index.js'
import { useLiquidityResources } from '../../hooks/useLiquidityResources.js'

const useStyles = makeStyles()((theme) => ({
Expand Down Expand Up @@ -84,6 +84,8 @@ export function Quote({ quote, ...props }: QuoteProps) {
const { data: liquidityList = EMPTY_LIST } = useLiquidityResources(chainId)
const dexIdsCount = liquidityList.filter((x) => !disabledDexIds.includes(x.id)).length

const { gasCost } = useGasManagement()

const rateNode = (
<>
1 {baseToken.tokenSymbol}{rate ? formatCompact(rate.toNumber(), { maximumFractionDigits: 6 }) : '--'}{' '}
Expand Down Expand Up @@ -122,7 +124,7 @@ export function Quote({ quote, ...props }: QuoteProps) {
</div>
<div className={classes.infoRow}>
<Typography className={classes.rowName}>Est Network fee</Typography>
<Typography className={classes.rowValue}>$2.46</Typography>
<Typography className={classes.rowValue}>${gasCost}</Typography>
</div>
<div className={classes.infoRow}>
<Typography className={classes.rowName}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PluginWalletStatusBar, SelectFungibleTokenModal } from '@masknet/shared
import { NetworkPluginID } from '@masknet/shared-base'
import { ActionButton, makeStyles } from '@masknet/theme'
import type { Web3Helper } from '@masknet/web3-helpers'
import { useNetworks } from '@masknet/web3-hooks-base'
import { useFungibleTokenBalance, useNetworks } from '@masknet/web3-hooks-base'
import {
formatBalance,
isGreaterThan,
Expand All @@ -13,20 +13,22 @@ import {
leftShift,
minus,
multipliedBy,
trimZero,
} from '@masknet/web3-shared-base'
import type { ChainId } from '@masknet/web3-shared-evm'
import { Box, Typography } from '@mui/material'
import { isNativeTokenAddress, type ChainId } from '@masknet/web3-shared-evm'
import { Box, Button, Typography } from '@mui/material'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import urlcat from 'urlcat'
import { CoinIcon } from '../../../components/CoinIcon.js'
import { Warning } from '../../../components/Warning.js'
import { RoutePaths } from '../../../constants.js'
import { useTrade } from '../../contexts/index.js'
import { useGasManagement, useTrade } from '../../contexts/index.js'
import { formatTokenBalance } from '../../helpers.js'
import { useBridgable } from '../../hooks/useBridgable.js'
import { useSupportedChains } from '../../hooks/useSupportedChains.js'
import { useSwappable } from '../../hooks/useSwappable.js'
import { Quote } from './Quote.js'
import { CoinIcon } from '../../../components/CoinIcon.js'

const useStyles = makeStyles()((theme) => ({
view: {
Expand Down Expand Up @@ -94,6 +96,20 @@ const useStyles = makeStyles()((theme) => ({
color: theme.palette.maskColor.second,
lineHeight: '18px',
},
tokenStatus: {
position: 'absolute',
right: 0,
top: 0,
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
},
balance: {
fontSize: 14,
fontWeight: 700,
lineHeight: '18px',
color: theme.palette.maskColor.main,
},
tokenInput: {
height: '100%',
width: '100%',
Expand All @@ -115,6 +131,15 @@ const useStyles = makeStyles()((theme) => ({
right: 0,
bottom: 0,
},
maxButton: {
padding: '0 6px',
fontSize: 12,
lineHeight: '16px',
color: theme.palette.maskColor.bottom,
backgroundColor: theme.palette.maskColor.main,
borderRadius: 4,
minWidth: 0,
},
diff: {
marginLeft: theme.spacing(0.5),
},
Expand Down Expand Up @@ -158,6 +183,13 @@ export function TradeView() {
const fromNetwork = networks.find((x) => x.chainId === fromChainId)
const toNetwork = networks.find((x) => x.chainId === toChainId)
const chainQuery = useSupportedChains()
const { data: fromTokenBalance } = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, fromToken?.address, {
chainId: fromChainId,
})
const { gasFee } = useGasManagement()
const { data: toTokenBalance } = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, toToken?.address, {
chainId: toChainId,
})

const pickToken = async (
currentToken: Web3Helper.FungibleTokenAll | null | undefined,
Expand Down Expand Up @@ -199,7 +231,7 @@ export function TradeView() {
const [isBridgable, bridgeErrorMessage] = useBridgable()
const errorMessage = isSwap ? swapErrorMessage : bridgeErrorMessage

const isTradable = isSwap ? !isSwappable : !isBridgable
const isTradable = isSwap ? isSwappable : isBridgable
const isLoading = isSwap ? isQuoteLoading : isBridgeQuoteLoading
const swapButtonLabel = isOverSlippage ? t`Swap anyway` : t`Swap`
const bridgeButtonLabel = isOverSlippage ? t`Bridge anyway` : t`Bridge`
Expand Down Expand Up @@ -239,7 +271,7 @@ export function TradeView() {
{fromToken?.symbol ?? '--'}
</Typography>
<Typography component="span" className={classes.chain}>
{fromNetwork?.name ? t`on ${fromNetwork.name}` : '--'}
{fromNetwork?.fullName ? t`on ${fromNetwork.fullName}` : '--'}
</Typography>
</Box>
<Icons.ArrowDrop size={16} />
Expand All @@ -248,6 +280,26 @@ export function TradeView() {
</Box>
<Box flexGrow={1}>
<Box height="100%" position="relative">
{fromTokenBalance ?
<Box className={classes.tokenStatus}>
<Icons.Wallet size={16} />
<Typography className={classes.balance}>
{formatTokenBalance(fromTokenBalance, fromToken?.decimals)}
</Typography>
<Button
type="button"
className={classes.maxButton}
onClick={() => {
if (!fromToken?.address) return
const isNative = isNativeTokenAddress(fromToken.address)
const balance =
isNative ? minus(fromTokenBalance, gasFee) : fromTokenBalance
setInputAmount(trimZero(leftShift(balance, fromToken.decimals).toFixed(12)))
}}>
<Trans>MAX</Trans>
</Button>
</Box>
: null}
<input
className={classes.tokenInput}
autoFocus
Expand Down Expand Up @@ -299,7 +351,7 @@ export function TradeView() {
{toToken?.symbol ?? '--'}
</Typography>
<Typography component="span" className={classes.chain}>
{toNetwork?.name ? t`on ${toNetwork.name}` : '--'}
{toNetwork?.fullName ? t`on ${toNetwork.fullName}` : '--'}
</Typography>
</Box>
<Icons.ArrowDrop size={16} />
Expand All @@ -308,6 +360,14 @@ export function TradeView() {
</Box>
<Box flexGrow={1}>
<Box height="100%" position="relative">
{toTokenBalance ?
<Box className={classes.tokenStatus}>
<Icons.Wallet size={16} />
<Typography className={classes.balance}>
{formatTokenBalance(toTokenBalance, toToken?.decimals)}
</Typography>
</Box>
: null}
<input
className={classes.tokenInput}
disabled
Expand Down Expand Up @@ -348,7 +408,7 @@ export function TradeView() {
loading={isLoading}
fullWidth
color={isOverSlippage ? 'error' : undefined}
disabled={isTradable}
disabled={!isTradable}
onClick={() => {
navigate(
urlcat(isSwap ? RoutePaths.Confirm : RoutePaths.BridgeConfirm, {
Expand Down
Loading

0 comments on commit e129774

Please sign in to comment.