diff --git a/src/background/connections/dAppConnection/models.ts b/src/background/connections/dAppConnection/models.ts index e5e555f1..aadc98a3 100644 --- a/src/background/connections/dAppConnection/models.ts +++ b/src/background/connections/dAppConnection/models.ts @@ -80,6 +80,7 @@ interface JsonRpcRequestPayloadBase { readonly method: Method; readonly site?: DomainMetadata; readonly tabId?: number; + readonly context?: Record; } interface JsonRpcRequestPayloadWithParams< diff --git a/src/contexts/BridgeProvider.test.tsx b/src/contexts/BridgeProvider.test.tsx index c4d46305..bb4adce8 100644 --- a/src/contexts/BridgeProvider.test.tsx +++ b/src/contexts/BridgeProvider.test.tsx @@ -195,19 +195,20 @@ describe('contexts/BridgeProvider', () => { onStatusChange(WrapStatus.WAITING_FOR_DEPOSIT_CONFIRMATION); signAndSendEVM(fakeDepositTx); - expect(requestFn).toHaveBeenCalledWith({ - method: RpcMethod.BITCOIN_SEND_TRANSACTION, - params: [ - { ...fakeDepositTx }, - { - customApprovalScreenTitle: 'Confirm Bridge', - contextInformation: { - title: 'This operation requires {{total}} approvals.', - notice: 'You will be prompted {{remaining}} more time(s).', - }, + expect(requestFn).toHaveBeenCalledWith( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [{ ...fakeDepositTx }], + }, + { + customApprovalScreenTitle: 'Confirm Bridge', + alert: { + type: 'info', + title: 'This operation requires {{total}} approvals.', + notice: 'You will be prompted {{remaining}} more time(s).', }, - ], - }); + } + ); // Mock the transfer TX being prompted and signed const fakeTransferTx = { @@ -219,15 +220,15 @@ describe('contexts/BridgeProvider', () => { onStatusChange(WrapStatus.WAITING_FOR_CONFIRMATION); signAndSendEVM(fakeTransferTx); - expect(requestFn).toHaveBeenCalledWith({ - method: RpcMethod.BITCOIN_SEND_TRANSACTION, - params: [ - { ...fakeTransferTx }, - { - customApprovalScreenTitle: 'Confirm Bridge', - }, - ], - }); + expect(requestFn).toHaveBeenCalledWith( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [{ ...fakeTransferTx }], + }, + { + customApprovalScreenTitle: 'Confirm Bridge', + } + ); }); }); diff --git a/src/contexts/BridgeProvider.tsx b/src/contexts/BridgeProvider.tsx index 464ee9e2..5679a9cd 100644 --- a/src/contexts/BridgeProvider.tsx +++ b/src/contexts/BridgeProvider.tsx @@ -228,39 +228,44 @@ function InnerBridgeProvider({ children }: { children: any }) { signAndSendEVM: (txData) => { const tx = txData as ContractTransaction; - return request({ - method: RpcMethod.BITCOIN_SEND_TRANSACTION, - params: [ - { - ...tx, - // erase gasPrice if maxFeePerGas can be used - gasPrice: tx.maxFeePerGas - ? undefined - : tx.gasPrice ?? undefined, - type: tx.maxFeePerGas ? undefined : 0, // use type: 0 if it's not an EIP-1559 transaction - }, - { - customApprovalScreenTitle: t('Confirm Bridge'), - contextInformation: - requiredSignatures > currentSignature - ? { - title: t( - 'This operation requires {{total}} approvals.', - { - total: requiredSignatures, - } - ), - notice: t( - 'You will be prompted {{remaining}} more time(s).', - { - remaining: requiredSignatures - currentSignature, - } - ), - } - : undefined, - }, - ], - }); + return request( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [ + { + ...tx, + chainId: + typeof tx.chainId === 'number' || + typeof tx.chainId === 'bigint' + ? `0x${tx.chainId.toString(16)}` + : tx.chainId, + // erase gasPrice if maxFeePerGas can be used + gasPrice: tx.maxFeePerGas + ? undefined + : tx.gasPrice ?? undefined, + type: tx.maxFeePerGas ? undefined : 0, // use type: 0 if it's not an EIP-1559 transaction + }, + ], + }, + { + customApprovalScreenTitle: t('Confirm Bridge'), + alert: + requiredSignatures > currentSignature + ? { + type: 'info', + title: t('This operation requires {{total}} approvals.', { + total: requiredSignatures, + }), + notice: t( + 'You will be prompted {{remaining}} more time(s).', + { + remaining: requiredSignatures - currentSignature, + } + ), + } + : undefined, + } + ); }, }); diff --git a/src/contexts/UnifiedBridgeProvider.test.tsx b/src/contexts/UnifiedBridgeProvider.test.tsx index abfe1b21..1e263791 100644 --- a/src/contexts/UnifiedBridgeProvider.test.tsx +++ b/src/contexts/UnifiedBridgeProvider.test.tsx @@ -260,28 +260,29 @@ describe('contexts/UnifiedBridgeProvider', () => { sign: expect.any(Function), }); - expect(requestFn).toHaveBeenCalledWith({ - method: RpcMethod.ETH_SEND_TRANSACTION, - params: [ - { ...allowanceTx }, - { - customApprovalScreenTitle: 'Confirm Bridge', - contextInformation: { - title: 'This operation requires {{total}} approvals.', - notice: 'You will be prompted {{remaining}} more time(s).', - }, - }, - ], - }); - expect(requestFn).toHaveBeenCalledWith({ - method: RpcMethod.ETH_SEND_TRANSACTION, - params: [ - { ...transferTx }, - { - customApprovalScreenTitle: 'Confirm Bridge', + expect(requestFn).toHaveBeenCalledWith( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [{ ...allowanceTx }], + }, + { + customApprovalScreenTitle: 'Confirm Bridge', + alert: { + type: 'info', + title: 'This operation requires {{total}} approvals.', + notice: 'You will be prompted {{remaining}} more time(s).', }, - ], - }); + } + ); + expect(requestFn).toHaveBeenCalledWith( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [{ ...transferTx }], + }, + { + customApprovalScreenTitle: 'Confirm Bridge', + } + ); expect(requestFn).toHaveBeenCalledWith({ method: ExtensionRequest.UNIFIED_BRIDGE_TRACK_TRANSFER, params: [transfer], diff --git a/src/contexts/UnifiedBridgeProvider.tsx b/src/contexts/UnifiedBridgeProvider.tsx index e1967a53..58b66594 100644 --- a/src/contexts/UnifiedBridgeProvider.tsx +++ b/src/contexts/UnifiedBridgeProvider.tsx @@ -405,36 +405,36 @@ export function UnifiedBridgeProvider({ assert(to, UnifiedBridgeError.InvalidTxPayload); assert(data, UnifiedBridgeError.InvalidTxPayload); - return request({ - method: RpcMethod.ETH_SEND_TRANSACTION, - params: [ - { - from, - to, - data, - }, - { - customApprovalScreenTitle: t('Confirm Bridge'), - contextInformation: - requiredSignatures > currentSignature - ? { - title: t( - 'This operation requires {{total}} approvals.', - { - total: requiredSignatures, - } - ), - notice: t( - 'You will be prompted {{remaining}} more time(s).', - { - remaining: requiredSignatures - currentSignature, - } - ), - } - : undefined, - }, - ], - }); + return request( + { + method: RpcMethod.ETH_SEND_TRANSACTION, + params: [ + { + from, + to, + data, + }, + ], + }, + { + customApprovalScreenTitle: t('Confirm Bridge'), + alert: + requiredSignatures > currentSignature + ? { + type: 'info', + title: t('This operation requires {{total}} approvals.', { + total: requiredSignatures, + }), + notice: t( + 'You will be prompted {{remaining}} more time(s).', + { + remaining: requiredSignatures - currentSignature, + } + ), + } + : undefined, + } + ); }, }); diff --git a/src/pages/ApproveAction/GenericApprovalScreen.tsx b/src/pages/ApproveAction/GenericApprovalScreen.tsx index aaa4f306..cd3891d9 100644 --- a/src/pages/ApproveAction/GenericApprovalScreen.tsx +++ b/src/pages/ApproveAction/GenericApprovalScreen.tsx @@ -6,6 +6,9 @@ import { LoadingOverlay } from '../../components/common/LoadingOverlay'; import { useTranslation } from 'react-i18next'; import { AlertType, DisplayData } from '@avalabs/vm-module-types'; import { + Alert, + AlertContent, + AlertTitle, Box, Button, Scrollbars, @@ -33,6 +36,21 @@ import { MaliciousTxAlert } from '@src/components/common/MaliciousTxAlert'; import { SpendLimitInfo } from '../SignTransaction/components/SpendLimitInfo/SpendLimitInfo'; import { NetworkDetails } from '../SignTransaction/components/ApprovalTxDetails'; +type WithContextAlert = { + alert: { type: 'info'; title: string; notice: string }; +}; + +function hasContextInfo( + context?: Record +): context is WithContextAlert { + return ( + typeof context === 'object' && + context !== null && + 'alert' in context && + Boolean(context.alert) + ); +} + export function GenericApprovalScreen() { const { t } = useTranslation(); const requestId = useGetRequestId(); @@ -138,6 +156,20 @@ export function GenericApprovalScreen() { )} + {hasContextInfo(context) && ( + + + {context.alert.title} + {context.alert.notice && ( + {context.alert.notice} + )} + + + )} + diff --git a/src/pages/ApproveAction/hooks/useFeeCustomizer.tsx b/src/pages/ApproveAction/hooks/useFeeCustomizer.tsx index c7e902ea..70040d2e 100644 --- a/src/pages/ApproveAction/hooks/useFeeCustomizer.tsx +++ b/src/pages/ApproveAction/hooks/useFeeCustomizer.tsx @@ -103,7 +103,7 @@ export const useFeeCustomizer = ({ case RpcMethod.BITCOIN_SEND_TRANSACTION: { return { feeRate: BigInt(data.data.feeRate), - limit: Math.ceil(data.data.fee / data.data.feeRate), + limit: Math.ceil(data.data.fee / data.data.feeRate) || 0, }; } diff --git a/src/utils/calculateGasAndFees.ts b/src/utils/calculateGasAndFees.ts index 7ab51295..74a6e742 100644 --- a/src/utils/calculateGasAndFees.ts +++ b/src/utils/calculateGasAndFees.ts @@ -45,14 +45,14 @@ export function calculateGasAndFees({ return { maxFeePerGas: maxFeePerGas, gasLimit: gasLimit || 0, - fee: fee.toDisplay({ asNumber: true }).toLocaleString(), + fee: fee.toDisplay(), bnFee, feeUSD: price - ? price.mul(fee).toDisplay({ fixedDp: 4, asNumber: true }) + ? price.mul(fee).toDisplay({ fixedDp: 2, asNumber: true }) : null, tipUSD: price && tip - ? price.mul(tip).toDisplay({ fixedDp: 4, asNumber: true }) + ? price.mul(tip).toDisplay({ fixedDp: 2, asNumber: true }) : null, }; }