diff --git a/apps/laboratory/src/components/AddTransactionModal.tsx b/apps/laboratory/src/components/AddTransactionModal.tsx new file mode 100644 index 0000000000..4314e44697 --- /dev/null +++ b/apps/laboratory/src/components/AddTransactionModal.tsx @@ -0,0 +1,93 @@ +import { + Box, + Button, + Input, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay +} from '@chakra-ui/react' +import { useState } from 'react' +import { ethers } from 'ethers' +import { useChakraToast } from './Toast' + +type IAddTransactionModalProps = { + isOpen: boolean + onClose: () => void + onSubmit: (params: { eth: string; to: string }) => void +} +export function AddTransactionModal({ isOpen, onClose, onSubmit }: IAddTransactionModalProps) { + const toast = useChakraToast() + const [eth, setEth] = useState(0) + const [to, setTo] = useState('') + function onAddTransaction() { + if (!ethers.isAddress(to)) { + toast({ + title: 'Error', + description: 'Invalid address', + type: 'error' + }) + + return + } + if (!eth || isNaN(eth)) { + toast({ + title: 'Error', + description: 'Invalid ETH amount', + type: 'error' + }) + + return + } + onSubmit({ eth: eth.toString(), to }) + reset() + onClose() + } + + function reset() { + setEth(0) + setTo('') + } + + return ( + <> + + + + Add a transaction + + + Transactions will be batched and sent together to your wallet for approval + + + setEth(event.target.valueAsNumber)} + /> + + + + setTo(event.target.value)} + /> + + + + + + + + + + ) +} diff --git a/apps/laboratory/src/components/Ethers/Ethers5Tests.tsx b/apps/laboratory/src/components/Ethers/Ethers5Tests.tsx index e5c150d5d4..9568175990 100644 --- a/apps/laboratory/src/components/Ethers/Ethers5Tests.tsx +++ b/apps/laboratory/src/components/Ethers/Ethers5Tests.tsx @@ -11,6 +11,7 @@ import { EthersSendCallsWithPaymasterServiceTest } from './EthersSendCallsWithPa export function Ethers5Tests() { const [ready, setReady] = React.useState(false) + const [callsHash, setCallsHash] = React.useState('') const { isConnected } = useAppKitAccount() React.useEffect(() => { @@ -59,13 +60,13 @@ export function Ethers5Tests() { Send Calls (Atomic Batch) - + Get Calls Status - + diff --git a/apps/laboratory/src/components/Ethers/EthersGetCallsStatusTest.tsx b/apps/laboratory/src/components/Ethers/EthersGetCallsStatusTest.tsx index 56a317ace7..9f0e3d59f0 100644 --- a/apps/laboratory/src/components/Ethers/EthersGetCallsStatusTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersGetCallsStatusTest.tsx @@ -1,5 +1,5 @@ import { Button, Stack, Text, Input } from '@chakra-ui/react' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { useAppKitAccount, useAppKitNetwork, @@ -13,15 +13,19 @@ import { W3mFrameProvider } from '@reown/appkit-wallet' import { type GetCallsStatusParams } from '../../types/EIP5792' import { EIP_5792_RPC_METHODS } from '../../utils/EIP5792Utils' -export function EthersGetCallsStatusTest() { +export function EthersGetCallsStatusTest({ callsHash }: { callsHash: string }) { const [isLoading, setLoading] = useState(false) - const [batchCallId, setBatchCallId] = useState('') + const [batchCallId, setBatchCallId] = useState(callsHash) const { chainId } = useAppKitNetwork() const { address, isConnected } = useAppKitAccount() const { walletProvider } = useAppKitProvider('eip155') const toast = useChakraToast() + useEffect(() => { + setBatchCallId(callsHash) + }, [callsHash]) + async function onGetCallsStatus() { try { setLoading(true) diff --git a/apps/laboratory/src/components/Ethers/EthersSendCallsTest.tsx b/apps/laboratory/src/components/Ethers/EthersSendCallsTest.tsx index df93a7c27a..b928527e02 100644 --- a/apps/laboratory/src/components/Ethers/EthersSendCallsTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSendCallsTest.tsx @@ -1,9 +1,19 @@ -import { Button, Stack, Text, Spacer, Heading } from '@chakra-ui/react' +import { + Box, + Link, + Stat, + StatLabel, + StatNumber, + StatHelpText, + Card, + CardBody +} from '@chakra-ui/react' import { useState, useEffect } from 'react' +import { Button, Stack, Text, Spacer, Heading } from '@chakra-ui/react' import { useAppKitAccount, useAppKitNetwork, useAppKitProvider } from '@reown/appkit/react' import { UniversalProvider } from '@walletconnect/universal-provider' import { useChakraToast } from '../Toast' -import type { Address } from 'viem' +import { parseGwei, type Address } from 'viem' import { vitalikEthAddress } from '../../utils/DataUtil' import { BrowserProvider, type Eip1193Provider } from 'ethers' import { @@ -11,17 +21,39 @@ import { WALLET_CAPABILITIES, getCapabilitySupportedChainInfo } from '../../utils/EIP5792Utils' +import { AddIcon, DeleteIcon } from '@chakra-ui/icons' +import { AddTransactionModal } from '../AddTransactionModal' import { W3mFrameProvider } from '@reown/appkit-wallet' type Provider = W3mFrameProvider | Awaited> -export function EthersSendCallsTest() { +export function EthersSendCallsTest({ onCallsHash }: { onCallsHash: (hash: string) => void }) { const [loading, setLoading] = useState(false) const { chainId } = useAppKitNetwork() const { address, isConnected } = useAppKitAccount() const { walletProvider } = useAppKitProvider('eip155') + const [transactionsToBatch, setTransactionsToBatch] = useState<{ value: string; to: string }[]>( + [] + ) const toast = useChakraToast() + const [isOpen, setIsOpen] = useState(false) + function onSubmit(args: { to: string; eth: string }) { + setLastCallsBatchId(null) + setTransactionsToBatch(prev => [ + ...prev, + { + to: args.to as `0x${string}`, + data: '0x' as `0x${string}`, + value: `0x${parseGwei(args.eth).toString(16)}` + } + ]) + } + + function onClose() { + setIsOpen(false) + } + const [atomicBatchSupportedChains, setAtomicBatchSupportedChains] = useState< Awaited> >([]) @@ -47,6 +79,14 @@ export function EthersSendCallsTest() { chainInfo => chainInfo.chainId === Number(chainId) ) + function onAddTransactionButtonClick() { + setIsOpen(true) + } + + function onRemoveTransaction(index: number) { + setTransactionsToBatch(prev => prev.filter((_, i) => i !== index)) + } + async function onSendCalls() { try { setLoading(true) @@ -73,7 +113,7 @@ export function EthersSendCallsTest() { version: '1.0', chainId: `0x${BigInt(chainId).toString(16)}`, from: address, - calls + calls: transactionsToBatch.length ? transactionsToBatch : calls } const batchCallHash = await provider.send(EIP_5792_RPC_METHODS.WALLET_SEND_CALLS, [ sendCallsParams @@ -85,6 +125,8 @@ export function EthersSendCallsTest() { description: batchCallHash, type: 'success' }) + setTransactionsToBatch([]) + onCallsHash(batchCallHash) } catch { toast({ title: 'Error', @@ -133,23 +175,62 @@ export function EthersSendCallsTest() { } return currentChainsInfo ? ( - - - + <> + + + + ( + {transactionsToBatch.length ? ( + transactionsToBatch.map((tx, index) => ( + <> + + + + + ({index + 1}) Sending + onRemoveTransaction(index)} + /> + + {parseInt(tx.value, 16) / 1000000000} ETH + to {tx.to} + + + {' '} + + + )) + ) : ( + + )} + ) + + + + + + + {transactionsToBatch.length ? ( + + ) : null} + + + + {lastCallsBatchId && ( <> Last batch call ID: {lastCallsBatchId} )} - + ) : ( Switch to {atomicBatchSupportedChainsNames} to test atomic batch feature diff --git a/apps/laboratory/src/components/Ethers/EthersTests.tsx b/apps/laboratory/src/components/Ethers/EthersTests.tsx index f2de2244d2..d29f88c746 100644 --- a/apps/laboratory/src/components/Ethers/EthersTests.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTests.tsx @@ -11,8 +11,20 @@ import { EthersSendCallsWithPaymasterServiceTest } from './EthersSendCallsWithPa export function EthersTests() { const [ready, setReady] = React.useState(false) + + const [callsHash, setCallsHash] = React.useState('') const { isConnected } = useAppKitAccount() + const onCallsHash = React.useCallback((hash: string) => { + setCallsHash(hash) + }, []) + + React.useEffect(() => { + if (!isConnected) { + setCallsHash('') + } + }, [isConnected]) + React.useEffect(() => { setReady(true) }, []) @@ -59,13 +71,13 @@ export function EthersTests() { Send Calls (Atomic Batch) - + Get Calls Status - +