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
-
+