diff --git a/src/contexts/WalletConnectContext.tsx b/src/contexts/WalletConnectContext.tsx index a713b5ac1..6bb5a9eb8 100644 --- a/src/contexts/WalletConnectContext.tsx +++ b/src/contexts/WalletConnectContext.tsx @@ -18,11 +18,13 @@ import { DialogTitle, } from '@/components/ui/dialog'; import { LoadingButton } from '@/components/ui/loading-button'; +import { FeeAmountInput } from '@/components/ui/masked-input'; import { Switch } from '@/components/ui/switch'; import { useWallet } from '@/contexts/WalletContext'; import { useBiometric } from '@/hooks/useBiometric'; +import { useDefaultFee } from '@/hooks/useDefaultFee'; import { useErrors } from '@/hooks/useErrors'; -import { decodeHexMessage, fromMojos, isHex } from '@/lib/utils'; +import { decodeHexMessage, fromMojos, toMojos, isHex } from '@/lib/utils'; import { useWalletState } from '@/state'; import { Params, @@ -378,6 +380,112 @@ interface RequestDialogProps { interface CommandDialogProps { params: Partial>; + onFeeChange?: (feeMojos: string | null) => void; +} + +function EditableFee({ + suggestedFeeMojos, + onFeeChange, +}: { + suggestedFeeMojos: string | number | undefined; + onFeeChange?: (feeMojos: string | null) => void; +}) { + const walletState = useWalletState(); + const { fee: defaultFee } = useDefaultFee(); + const precision = walletState.sync.unit.precision; + + const suggestedDisplay = fromMojos( + suggestedFeeMojos || 0, + precision, + ).toString(); + const [feeValue, setFeeValue] = useState(suggestedDisplay); + const [isEditing, setIsEditing] = useState(false); + + const handleFeeChange = (values: { + floatValue: number | undefined; + value: string; + }) => { + setFeeValue(values.value); + if (onFeeChange) { + onFeeChange(toMojos(values.value || '0', precision)); + } + }; + + const applyDefaultFee = () => { + setFeeValue(defaultFee); + setIsEditing(true); + if (onFeeChange) { + onFeeChange(toMojos(defaultFee || '0', precision)); + } + }; + + if (!isEditing) { + return ( +
+
+ Fee{' '} + + (suggested by dApp) + +
+
+
+ {formatNumber({ + value: fromMojos(suggestedFeeMojos || 0, precision), + minimumFractionDigits: 0, + maximumFractionDigits: precision, + })}{' '} + {walletState.sync.unit.ticker} +
+ + {parseFloat(defaultFee) > 0 && ( + + )} +
+
+ ); + } + + return ( +
+
+ Fee +
+
+
+ +
+ +
+
+ ); } function SignCoinSpendsDialog({ @@ -479,7 +587,10 @@ function SignMessageByAddressDialog({ ); } -function TakeOfferDialog({ params }: CommandDialogProps<'chia_takeOffer'>) { +function TakeOfferDialog({ + params, + onFeeChange, +}: CommandDialogProps<'chia_takeOffer'>) { const [offer, setOffer] = useState(null); const { addError } = useErrors(); @@ -490,16 +601,26 @@ function TakeOfferDialog({ params }: CommandDialogProps<'chia_takeOffer'>) { .catch(addError); }, [params, addError]); - return offer ? ( - - ) : ( -
- Loading offer details... + return ( +
+ {offer ? ( + + ) : ( +
+ Loading offer details... +
+ )} +
+ +
); } -function CreateOfferDialog({ params }: CommandDialogProps<'chia_createOffer'>) { +function CreateOfferDialog({ + params, + onFeeChange, +}: CommandDialogProps<'chia_createOffer'>) { const walletState = useWalletState(); // Check if any requested assets are revocable const hasRevocableAssets = params.requestAssets?.some( @@ -563,23 +684,15 @@ function CreateOfferDialog({ params }: CommandDialogProps<'chia_createOffer'>) { ))}
-
-
Fee
-
- {formatNumber({ - value: fromMojos(params.fee || 0, walletState.sync.unit.precision), - minimumFractionDigits: 0, - maximumFractionDigits: walletState.sync.unit.precision, - })}{' '} - {walletState.sync.unit.ticker} -
-
+ ); } -function CancelOfferDialog({ params }: CommandDialogProps<'chia_cancelOffer'>) { - const walletState = useWalletState(); +function CancelOfferDialog({ + params, + onFeeChange, +}: CommandDialogProps<'chia_cancelOffer'>) { const [record, setRecord] = useState(null); const { addError } = useErrors(); @@ -595,15 +708,7 @@ function CancelOfferDialog({ params }: CommandDialogProps<'chia_cancelOffer'>) {
Offer ID
{params.id}
-
Fee
-
- {formatNumber({ - value: fromMojos(params.fee || 0, walletState.sync.unit.precision), - minimumFractionDigits: 0, - maximumFractionDigits: walletState.sync.unit.precision, - })}{' '} - {walletState.sync.unit.ticker} -
+ {record && (
@@ -614,7 +719,7 @@ function CancelOfferDialog({ params }: CommandDialogProps<'chia_cancelOffer'>) { ); } -function SendDialog({ params }: CommandDialogProps<'chia_send'>) { +function SendDialog({ params, onFeeChange }: CommandDialogProps<'chia_send'>) { const walletState = useWalletState(); return ( @@ -641,17 +746,7 @@ function SendDialog({ params }: CommandDialogProps<'chia_send'>) { {params.assetId ? 'CAT' : walletState.sync.unit.ticker}
-
-
Fee
-
- {formatNumber({ - value: fromMojos(params.fee || 0, walletState.sync.unit.precision), - minimumFractionDigits: 0, - maximumFractionDigits: walletState.sync.unit.precision, - })}{' '} - {walletState.sync.unit.ticker} -
-
+ {params.assetId && (
Asset Id
@@ -685,6 +780,14 @@ const COMMAND_COMPONENTS: { chia_signMessageByAddress: SignMessageByAddressDialog, }; +const COMMANDS_WITH_FEE = new Set([ + 'chia_send', + 'chia_createOffer', + 'chia_cancelOffer', + 'chia_takeOffer', + // 'chia_bulkMintNfts', // not implemented yet, but will require fee override support when it is +]); + const COMMAND_METADATA: Partial< Record< WalletConnectCommand, @@ -727,6 +830,7 @@ function RequestDialog({ signClient, }: RequestDialogProps) { const [isApproving, setIsApproving] = useState(false); + const [feeOverride, setFeeOverride] = useState(null); const { currentTheme } = useTheme(); const method = request.params.request.method as WalletConnectCommand; const params = request.params.request.params; @@ -744,6 +848,29 @@ function RequestDialog({ [params, commandInfo], ); + const hasFee = COMMANDS_WITH_FEE.has(method); + + const approveWithFee = useCallback(async () => { + if (feeOverride !== null && hasFee) { + const modifiedRequest = { + ...request, + params: { + ...request.params, + request: { + ...request.params.request, + params: { + ...request.params.request.params, + fee: feeOverride, + }, + }, + }, + }; + await approve(modifiedRequest); + } else { + await approve(request); + } + }, [request, approve, feeOverride, hasFee]); + if (!commandInfo.confirm) { return null; } @@ -779,7 +906,12 @@ function RequestDialog({
- {CommandComponent && } + {CommandComponent && ( + + )}
@@ -794,7 +926,7 @@ function RequestDialog({ onClick={async () => { setIsApproving(true); try { - await approve(request); + await approveWithFee(); } finally { setIsApproving(false); }