diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
index a5506723eb41..9dd91b337411 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
@@ -109,7 +109,7 @@ export function RedPacket(props: RedPacketProps) {
return t.description_claimed(
availability.claimed_amount
? {
- amount: formatBalance(availability.claimed_amount, token.decimals, 8),
+ amount: formatBalance(availability.claimed_amount, token.decimals, 2),
symbol: token.symbol,
}
: { amount: '-', symbol: '-' },
@@ -122,7 +122,7 @@ export function RedPacket(props: RedPacketProps) {
if (listOfStatus.includes(RedPacketStatus.expired) && canRefund)
return t.description_refund({
- balance: formatBalance(availability.balance, token.decimals),
+ balance: formatBalance(availability.balance, token.decimals, 2),
symbol: token.symbol ?? '-',
})
if (listOfStatus.includes(RedPacketStatus.refunded)) return t.description_refunded()
@@ -130,7 +130,7 @@ export function RedPacket(props: RedPacketProps) {
if (listOfStatus.includes(RedPacketStatus.empty)) return t.description_empty()
if (!payload.password) return t.description_broken()
return t.description_failover({
- total: formatBalance(payload.total, token.decimals),
+ total: formatBalance(payload.total, token.decimals, 2),
symbol: token.symbol ?? '-',
shares: payload.shares.toString() ?? '-',
})
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
index d3d9353d8e76..e7feaca80ae2 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
@@ -1,19 +1,28 @@
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useCallback, useEffect, useMemo, useRef } from 'react'
+import {
+ useAccount,
+ useChainId,
+ useBalance,
+ useNetworkType,
+ useWeb3,
+ useNativeToken,
+ useNativeTokenAddress,
+} from '@masknet/plugin-infra/web3'
import { chainResolver, explorerResolver, isNativeTokenAddress, useRedPacketConstants } from '@masknet/web3-shared-evm'
import { Grid, Link, Paper, Typography } from '@mui/material'
import { makeStyles } from '@masknet/theme'
import LaunchIcon from '@mui/icons-material/Launch'
import { FormattedBalance, useOpenShareTxDialog } from '@masknet/shared'
+import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
import { useI18N } from '../locales'
-import { RedPacketSettings, useCreateCallback } from './hooks/useCreateCallback'
-import { useAccount, useChainId, useNetworkType, useWeb3 } from '@masknet/plugin-infra/web3'
-import { NetworkPluginID, formatBalance } from '@masknet/web3-shared-base'
+import { RedPacketSettings, useCreateCallback, useCreateParams } from './hooks/useCreateCallback'
+import { useTransactionValue } from '@masknet/plugin-infra/web3-evm'
+import { NetworkPluginID, formatBalance, isSameAddress } from '@masknet/web3-shared-base'
import type { RedPacketJSONPayload, RedPacketRecord } from '../types'
import { RedPacketRPC } from '../messages'
import { PluginWalletStatusBar } from '../../../utils'
-import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
import { ChainBoundary } from '../../../web3/UI/ChainBoundary'
const useStyles = makeStyles()((theme) => ({
@@ -81,6 +90,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
const t = useI18N()
const { onBack, settings, onCreated, onClose } = props
const { classes } = useStyles()
+ const { value: balance = '0', loading: loadingBalance } = useBalance(NetworkPluginID.PLUGIN_EVM)
const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
useEffect(() => {
if (settings?.token?.chainId !== chainId) onClose()
@@ -93,11 +103,27 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM)
const account = useAccount(NetworkPluginID.PLUGIN_EVM)
const networkType = useNetworkType(NetworkPluginID.PLUGIN_EVM)
+ const nativeTokenAddress = useNativeTokenAddress(NetworkPluginID.PLUGIN_EVM)
const { address: publicKey, privateKey } = useMemo(
() => web3?.eth.accounts.create() ?? { address: '', privateKey: '' },
[web3],
)!
- const [{ loading: isCreating }, createCallback] = useCreateCallback(settings!, contract_version, publicKey)
+ const { value: nativeToken } = useNativeToken(NetworkPluginID.PLUGIN_EVM)
+
+ // #region amount minus estimate gas fee
+ const { value: createParams } = useCreateParams(settings!, contract_version, publicKey)
+ const isNativeToken = isSameAddress(settings?.token?.address, nativeTokenAddress)
+ const { transactionValue, estimateGasFee } = useTransactionValue(settings?.total, createParams?.gas)
+ const isWaitGasBeMinus = (!estimateGasFee || loadingBalance) && isNativeToken
+ const isBalanceInsufficient = new BigNumber(transactionValue).isLessThanOrEqualTo(0)
+ const total = isNativeToken ? (isBalanceInsufficient ? '0' : transactionValue) : (settings?.total as string)
+ const formatTotal = formatBalance(total, settings?.token?.decimals ?? 18, isNativeToken ? 3 : 0)
+ const [{ loading: isCreating }, createCallback] = useCreateCallback(
+ { ...settings!, total },
+ contract_version,
+ publicKey,
+ )
+ // #endregion
const openShareTxDialog = useOpenShareTxDialog()
const createRedpacket = useCallback(async () => {
const result = await createCallback()
@@ -216,26 +242,45 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
- {settings?.isRandom ? null : (
+ {!estimateGasFee ? null : (
<>
- {t.amount_per_share()}
+ {t.estimate_gas_fee()}
>
)}
+ {settings?.isRandom ? null : (
+ <>
+
+
+ {t.amount_per_share()}
+
+
+
+
+ {isBalanceInsufficient
+ ? '0'
+ : new BigNumber(formatTotal).div(settings?.shares ?? 1).toFixed(6)}{' '}
+ {settings?.token?.symbol}
+
+
+ >
+ )}
+
{t.total_amount()}
@@ -243,12 +288,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
-
+ {formatTotal} {settings?.token?.symbol}
@@ -261,11 +301,17 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
-
- {t.send_symbol({
- amount: formatBalance(settings?.total, settings?.token?.decimals ?? 0),
- symbol: settings?.token?.symbol ?? '-',
- })}
+
+ {!isBalanceInsufficient
+ ? t.send_symbol({
+ amount: formatTotal,
+ symbol: settings?.token?.symbol ?? '-',
+ })
+ : t.insufficient_balance()}
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
index dd57a2476f68..9fef50607064 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
@@ -1,5 +1,5 @@
import { useCallback } from 'react'
-import { useAsyncFn } from 'react-use'
+import { useAsync, useAsyncFn } from 'react-use'
import Web3Utils from 'web3-utils'
import { omit } from 'lodash-unified'
import { useAccount, useChainId, useWeb3Connection, useWeb3 } from '@masknet/plugin-infra/web3'
@@ -58,7 +58,11 @@ interface CreateParams {
gasError: Error | null
}
-export function useCreateParams(redPacketSettings: RedPacketSettings | undefined, version: number, publicKey: string) {
+export function useCreateParamsCallback(
+ redPacketSettings: RedPacketSettings | undefined,
+ version: number,
+ publicKey: string,
+) {
const account = useAccount(NetworkPluginID.PLUGIN_EVM)
const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
const { NATIVE_TOKEN_ADDRESS } = useTokenConstants(chainId)
@@ -111,11 +115,16 @@ export function useCreateParams(redPacketSettings: RedPacketSettings | undefined
return getCreateParams
}
+export function useCreateParams(redPacketSettings: RedPacketSettings, version: number, publicKey: string) {
+ const getCreateParams = useCreateParamsCallback(redPacketSettings, version, publicKey)
+ return useAsync(() => getCreateParams(), [redPacketSettings, version, publicKey])
+}
+
export function useCreateCallback(redPacketSettings: RedPacketSettings, version: number, publicKey: string) {
const account = useAccount(NetworkPluginID.PLUGIN_EVM)
const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
const redPacketContract = useRedPacketContract(chainId, version)
- const getCreateParams = useCreateParams(redPacketSettings, version, publicKey)
+ const getCreateParams = useCreateParamsCallback(redPacketSettings, version, publicKey)
const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM)
const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM)
@@ -162,5 +171,5 @@ export function useCreateCallback(redPacketSettings: RedPacketSettings, version:
}
}
return { hash, receipt }
- }, [account, connection, redPacketContract, redPacketSettings, chainId, getCreateParams])
+ }, [account, connection, redPacketContract, redPacketSettings, chainId])
}
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
index b052fd414538..1b8aa23efa1d 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
@@ -1,5 +1,12 @@
import { type Plugin, usePluginWrapper, PluginId } from '@masknet/plugin-infra/content-script'
-import { ChainId, SchemaType, chainResolver, networkResolver, NetworkType } from '@masknet/web3-shared-evm'
+import {
+ ChainId,
+ SchemaType,
+ chainResolver,
+ networkResolver,
+ NetworkType,
+ isNativeTokenAddress,
+} from '@masknet/web3-shared-evm'
import { base } from '../base'
import { RedPacketMetaKey, RedPacketNftMetaKey } from '../constants'
import {
@@ -8,6 +15,7 @@ import {
renderWithRedPacketMetadata,
renderWithRedPacketNftMetadata,
} from './helpers'
+import { useI18N } from '../locales'
import type { RedPacketJSONPayload, RedPacketNftJSONPayload } from '../types'
import RedPacketDialog from './RedPacketDialog'
import { RedPacketInPost } from './RedPacketInPost'
@@ -147,6 +155,7 @@ interface ERC20RedpacketBadgeProps {
function ERC20RedpacketBadge(props: ERC20RedpacketBadgeProps) {
const { payload } = props
+ const t = useI18N()
const { value: fetchedToken } = useFungibleToken(
NetworkPluginID.PLUGIN_EVM,
payload.token?.address ?? payload.token?.address,
@@ -156,9 +165,16 @@ function ERC20RedpacketBadge(props: ERC20RedpacketBadgeProps) {
const tokenDetailed = payload.token?.schema === SchemaType.Native ? nativeCurrency : payload.token ?? fetchedToken
return (
- A Lucky Drop with{' '}
- {formatBalance(payload.total, tokenDetailed?.decimals ?? 0)} $
- {tokenDetailed?.symbol ?? tokenDetailed?.name ?? 'Token'} from {payload.sender.name}
+
+ {t.badge({
+ balance: formatBalance(
+ payload.total,
+ tokenDetailed?.decimals ?? 0,
+ isNativeTokenAddress(payload.token?.address) ? 6 : 0,
+ ),
+ tokenName: tokenDetailed?.symbol ?? tokenDetailed?.name ?? 'Token',
+ sender: payload.sender.name,
+ })}
)
}
diff --git a/packages/mask/src/plugins/RedPacket/locales/en-US.json b/packages/mask/src/plugins/RedPacket/locales/en-US.json
index fb7fb85fe4c0..8f79904442b5 100644
--- a/packages/mask/src/plugins/RedPacket/locales/en-US.json
+++ b/packages/mask/src/plugins/RedPacket/locales/en-US.json
@@ -2,6 +2,7 @@
"promote": "🧧🧧🧧 Try sending Lucky Drop to your friends with tokens or NFTs to share the joy now! Install Mask.io to send your first Lucky Drop.",
"promote_short": "🧧🧧🧧 Try sending Lucky Drop to your friends with Mask.io.",
"nft_shift_select_tip": "You can also use {{text}} to select multiple NFTs.",
+ "badge": "A Lucky Drop with {{balance}} {{tokenName}} from {{sender}}",
"collections": "Collections",
"select_a_token": "Select a Token",
"search": "Search",
@@ -53,10 +54,12 @@
"select_existing": "History",
"create_new": "New",
"send_symbol": "Send {{amount}} {{symbol}}",
+ "insufficient_balance": "Insufficient Balance",
"back": "Back",
"hint": "You can withdraw the remaining balance 24 hours after the Lucky Drop is sent.",
"total_amount": "Total Amount",
"amount_per_share": "Amount per Share",
+ "estimate_gas_fee": "Estimate gas fee",
"shares": "Shares",
"average": "Average",
"random": "Random",
diff --git a/packages/plugin-infra/src/web3/EVM/index.ts b/packages/plugin-infra/src/web3/EVM/index.ts
index edb258a194ec..9076bf6a2163 100644
--- a/packages/plugin-infra/src/web3/EVM/index.ts
+++ b/packages/plugin-infra/src/web3/EVM/index.ts
@@ -8,6 +8,7 @@ export * from './useERC721ContractIsApproveForAll'
export * from './useERC721ContractSetApproveForAllCallback'
export * from './useERC721TokenTransferCallback'
export * from './useGasConfig'
+export * from './useTransactionValue'
export * from './useMulticall'
export * from './useNativeTokenTransferCallback'
export * from './useNativeTokenWrapperCallback'
diff --git a/packages/plugin-infra/src/web3/EVM/useTransactionValue.ts b/packages/plugin-infra/src/web3/EVM/useTransactionValue.ts
new file mode 100644
index 000000000000..a3e53b2f9ec1
--- /dev/null
+++ b/packages/plugin-infra/src/web3/EVM/useTransactionValue.ts
@@ -0,0 +1,27 @@
+import { useBalance } from '../useBalance'
+import BigNumber from 'bignumber.js'
+import { useGasConfig } from './useGasConfig'
+import { useChainId } from '../useChainId'
+import { NetworkPluginID } from '@masknet/web3-shared-base'
+
+export function useTransactionValue(originalValue: BigNumber.Value | undefined, gas: number | undefined) {
+ const { value: balance = '0' } = useBalance(NetworkPluginID.PLUGIN_EVM)
+ const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
+
+ // #region amount minus estimate gas fee
+ const { gasPrice } = useGasConfig(chainId)
+
+ const estimateGasFee = !gas
+ ? undefined
+ : gasPrice && gasPrice !== '0'
+ ? new BigNumber(gasPrice).multipliedBy(gas * 1.5).toFixed()
+ : undefined
+
+ const transactionValue = new BigNumber(balance).isLessThan(
+ new BigNumber(originalValue ?? '0').plus(new BigNumber(estimateGasFee ?? '0')),
+ )
+ ? new BigNumber(originalValue ?? '0').minus(estimateGasFee ?? '0').toFixed()
+ : (originalValue as string)
+
+ return { transactionValue, estimateGasFee }
+}
diff --git a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC20.ts b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC20.ts
index ab391b45b151..85cec4441ca7 100644
--- a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC20.ts
+++ b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC20.ts
@@ -14,6 +14,7 @@ export class ERC20Descriptor implements TransactionDescriptor {
})
for (const method of context.methods) {
const parameters = method.parameters
+
switch (method.name) {
case 'approve':
if (parameters?.spender === undefined || parameters?.value === undefined) break
diff --git a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC721.ts b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC721.ts
index bcbf4dea0356..0048b51af5a8 100644
--- a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC721.ts
+++ b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/ERC721.ts
@@ -1,5 +1,6 @@
import type { TransactionContext } from '@masknet/web3-shared-base'
import type { ChainId, TransactionParameter } from '@masknet/web3-shared-evm'
+import { Web3StateSettings } from '../../../settings'
import type { TransactionDescriptor } from '../types'
export class ERC721Descriptor implements TransactionDescriptor {
@@ -10,6 +11,20 @@ export class ERC721Descriptor implements TransactionDescriptor {
const parameters = method.parameters
switch (method.name) {
+ case 'approve':
+ if (parameters?.to === undefined || parameters?.tokenId === undefined) break
+
+ const connection = await Web3StateSettings.value.Connection?.getConnection?.({
+ chainId: context.chainId,
+ })
+ const contract = await connection?.getNonFungibleTokenContract(context.to)
+
+ return {
+ chainId: context.chainId,
+ title: `Unlock ${contract?.symbol ?? 'token'} contract`,
+ description: `Unlock ${contract?.symbol ?? 'token'} contract`,
+ successfulDescription: `${contract?.symbol ?? 'token'} is unlocked successfully.`,
+ }
case 'setApprovalForAll':
if (parameters?.operator === undefined || parameters?.approved === undefined) break
return {
diff --git a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts
index 3023490d64fe..72a305dbf3ab 100644
--- a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts
+++ b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts
@@ -3,6 +3,7 @@ import {
ChainId,
getNftRedPacketConstants,
getRedPacketConstants,
+ isNativeTokenAddress,
TransactionParameter,
} from '@masknet/web3-shared-evm'
import type { TransactionDescriptor } from '../types'
@@ -26,7 +27,11 @@ export class RedPacketDescriptor implements TransactionDescriptor {
})
const token = await connection?.getFungibleToken(parameters?._token_addr ?? '')
- const amount = formatBalance(parameters?._total_tokens, token?.decimals)
+ const amount = formatBalance(
+ parameters?._total_tokens,
+ token?.decimals,
+ isNativeTokenAddress(parameters?._token_addr) ? 6 : 0,
+ )
return {
chainId: context.chainId,