diff --git a/src/pages/Account/components/transaction/transaction.tsx b/src/pages/Account/components/transaction/transaction.tsx index 5ea36ea..5588e06 100644 --- a/src/pages/Account/components/transaction/transaction.tsx +++ b/src/pages/Account/components/transaction/transaction.tsx @@ -1,71 +1,381 @@ +import { UserOperationStruct } from '@account-abstraction/contracts'; import { + Box, Button, CardActions, CardContent, CircularProgress, + Container, + Paper, Stack, + TextField, Typography, } from '@mui/material'; -import React from 'react'; +import { ethers } from 'ethers'; +import { arrayify } from 'ethers/lib/utils.js'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useAccount, useConnect, useSignMessage } from 'wagmi'; +import { + useBackgroundDispatch, + useBackgroundSelector, +} from '../../../App/hooks'; +import { + getAccountInfo, + getActiveAccount, +} from '../../../Background/redux-slices/selectors/accountSelectors'; +import { selectCurrentOriginPermission } from '../../../Background/redux-slices/selectors/dappPermissionSelectors'; +import { getActiveNetwork } from '../../../Background/redux-slices/selectors/networkSelectors'; +import { + selectCurrentPendingSendTransactionRequest, + selectCurrentPendingSendTransactionUserOp, +} from '../../../Background/redux-slices/selectors/transactionsSelectors'; +import { createUnsignedUserOp } from '../../../Background/redux-slices/transactions'; import { EthersTransactionRequest } from '../../../Background/services/provider-bridge'; +import AccountInfo from '../../../Popup/components/account-info'; +import OriginInfo from '../../../Popup/components/origin-info'; +import useAccountApi from '../../useAccountApi'; import { TransactionComponentProps } from '../types'; +const SignTransactionConfirmation = ({ + activeNetwork, + activeAccount, + accountInfo, + originPermission, + transactions, + userOp, + onReject, + onSend, +}: { + activeNetwork: any; + activeAccount: any; + accountInfo: any; + originPermission: any; + transactions: EthersTransactionRequest[]; + userOp: UserOperationStruct; + onReject: any; + onSend: any; +}) => { + const [showAddPaymasterUI, setShowAddPaymasterUI] = useState(false); + const [addPaymasterLoader, setAddPaymasterLoader] = useState(false); + const [paymasterUrl, setPaymasterUrl] = useState(''); + + const addPaymaster = useCallback(async () => { + // let newPaymasterUrl: string = paymasterUrl; + // if (paymasterUrl[paymasterUrl.length - 1] !== '/') + // newPaymasterUrl = paymasterUrl + '/'; + + // const response = await fetch(`${newPaymasterUrl}rpc/eth_g`); + setAddPaymasterLoader(false); + setShowAddPaymasterUI(false); + }, []); + + const onSendClick = useCallback(() => { + onSend(); + }, [onSend]); + + return ( + + + + Send transaction request + + + {activeAccount && ( + + )} + + + + Paymaster Info + + {!showAddPaymasterUI && ( + + + {userOp.paymasterAndData === '0x' + ? 'No paymaster has been used' + : ';'} + + + + )} + {showAddPaymasterUI && ( + + setPaymasterUrl(e.target.value)} + sx={{ width: '100%' }} + label="Paymaster URL" + variant="standard" + /> + + + + + + )} + + {transactions.length > 1 ? ' Transactions data' : 'Transaction data'} + + + {transactions.map((transaction: EthersTransactionRequest) => ( + + + To:{' '} + +
{transaction.to}
+
+
+ + Data:{' '} + +
+                    {transaction.data?.toString()}
+                  
+
+
+ + Value:{' '} + +
+                    {transaction.value
+                      ? ethers.utils.formatEther(transaction.value)
+                      : 0}{' '}
+                    {activeNetwork.baseAsset.symbol}
+                  
+
+
+
+ ))} +
+
+ {!showAddPaymasterUI && ( + + + + + + + )} +
+ ); +}; + const Transaction = ({ transaction, onComplete, onReject, }: TransactionComponentProps) => { - const [loader, setLoader] = React.useState(false); + const [stage, setStage] = useState<'show-transaction' | 'awaiting-signature'>( + 'show-transaction' + ); - return ( + const { connect, connectors, isLoading, error, pendingConnector } = + useConnect(); + + const backgroundDispatch = useBackgroundDispatch(); + const activeAccount = useBackgroundSelector(getActiveAccount); + const activeNetwork = useBackgroundSelector(getActiveNetwork); + const accountInfo = useBackgroundSelector((state) => + getAccountInfo(state, activeAccount) + ); + + const sendTransactionRequest = useBackgroundSelector( + selectCurrentPendingSendTransactionRequest + ); + + const pendingUserOp = useBackgroundSelector( + selectCurrentPendingSendTransactionUserOp + ); + + useEffect(() => { + if (activeAccount) { + backgroundDispatch(createUnsignedUserOp(activeAccount)); + } + }, [activeAccount, backgroundDispatch]); + + const originPermission = useBackgroundSelector((state) => + selectCurrentOriginPermission(state, { + origin: sendTransactionRequest?.origin || '', + address: activeAccount || '', + }) + ); + + const { result, loading, callAccountApi } = useAccountApi(); + + const { isConnected } = useAccount(); + + const { data: signedMessage, signMessage } = useSignMessage({ + onSuccess(data, variables) { + // Verify signature when sign message succeeds + console.log(data, variables); + }, + }); + + useEffect(() => { + if (signedMessage) { + onComplete(transaction, { + signedMessage, + }); + } + }, [signedMessage, onComplete, transaction]); + + useEffect(() => { + if (result) { + signMessage({ message: arrayify(result) }); + } + }, [result, loading, signMessage]); + + useEffect(() => { + if (!isConnected) { + connect({ connector: connectors[0] }); + } + }, [isConnected, connect, connectors, callAccountApi]); + + const onSend = useCallback(() => { + setStage('awaiting-signature'); + callAccountApi('getUserOpHashToSign', [pendingUserOp]); + }, [callAccountApi, pendingUserOp]); + + if (!pendingUserOp) + return ( + + + + ); + + if ( + stage === 'show-transaction' && + pendingUserOp && + sendTransactionRequest.transactionRequest + ) + return ( + + ); + + return !isConnected ? ( <> - Dummy Account Component + Connect 2FA Device - You can show as many steps as you want in this dummy component. You - need to call the function onComplete passed as a props to this - component.
-
- The function takes a modifiedTransactions & context as a parameter, - the context will be passed to your AccountApi when creating a new - account. While modifiedTransactions will be agreed upon by the user. + All your transactions must be signed by your mobile wallet and this + chrome extension to prevent fraudulant transactions.
- This Component is defined in exported in{' '} -
- - trampoline/src/pages/Account/components/transaction/index.ts
- + {connectors.map((connector) => ( + + ))} + + {error && {error.message}} + ) : ( + + + Awaiting Signature + + + Check your phone, a signature request has been sent for the transaction. +
+
+ +
); };