Skip to content

Commit

Permalink
added ethers impl for ERC5792 and ERC7677 on laboratory (#2310)
Browse files Browse the repository at this point in the history
  • Loading branch information
KannuSingh authored May 29, 2024
1 parent 427914c commit 220f752
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 0 deletions.
93 changes: 93 additions & 0 deletions apps/laboratory/src/components/Ethers/EthersGetCallsStatusTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Button, Stack, Text, Input } from '@chakra-ui/react'
import { useState } from 'react'
import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react'
import { EthereumProvider } from '@walletconnect/ethereum-provider'
import { useChakraToast } from '../Toast'
import { BrowserProvider } from 'ethers'
import { type GetCallsStatusParams } from '../../types/EIP5792'
import { EIP_5792_RPC_METHODS } from '../../utils/EIP5792Utils'

export function EthersGetCallsStatusTest() {
const [isLoading, setLoading] = useState(false)
const [batchCallId, setBatchCallId] = useState('')

const { address, chainId, isConnected } = useWeb3ModalAccount()
const { walletProvider } = useWeb3ModalProvider()
const toast = useChakraToast()

async function onGetCallsStatus() {
try {
setLoading(true)
if (!walletProvider || !address) {
throw Error('user is disconnected')
}
if (!chainId) {
throw Error('chain not selected')
}
if (!batchCallId) {
throw Error('call id not valid')
}
const provider = new BrowserProvider(walletProvider, chainId)
const batchCallsStatus = await provider.send(EIP_5792_RPC_METHODS.WALLET_GET_CALLS_STATUS, [
batchCallId as GetCallsStatusParams
])
toast({
title: 'Success',
description: JSON.stringify(batchCallsStatus),
type: 'success'
})
} catch {
toast({
title: 'Error',
description: 'Failed to get call status',
type: 'error'
})
} finally {
setLoading(false)
}
}
function isGetCallsStatusSupported(): boolean {
if (walletProvider instanceof EthereumProvider) {
return Boolean(
walletProvider?.signer?.session?.namespaces?.['eip155']?.methods?.includes(
EIP_5792_RPC_METHODS.WALLET_GET_CALLS_STATUS
)
)
}

return false
}

if (!isConnected || !address || !walletProvider) {
return (
<Text fontSize="md" color="yellow">
Wallet not connected
</Text>
)
}
if (!isGetCallsStatusSupported()) {
return (
<Text fontSize="md" color="yellow">
Wallet does not support wallet_getCallsStatus rpc method
</Text>
)
}

return (
<Stack direction={['column', 'column', 'row']}>
<Input
placeholder="0xf34ffa..."
onChange={e => setBatchCallId(e.target.value)}
value={batchCallId}
isDisabled={isLoading}
/>
<Button
data-test-id="get-calls-status-button"
onClick={onGetCallsStatus}
isDisabled={isLoading}
>
Get Calls Status
</Button>
</Stack>
)
}
127 changes: 127 additions & 0 deletions apps/laboratory/src/components/Ethers/EthersSendCallsTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Button, Stack, Text, Spacer } from '@chakra-ui/react'
import { useState } from 'react'
import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react'
import { EthereumProvider } from '@walletconnect/ethereum-provider'
import { useChakraToast } from '../Toast'
import { parseGwei, type Address } from 'viem'
import { vitalikEthAddress } from '../../utils/DataUtil'
import { BrowserProvider } from 'ethers'
import {
EIP_5792_RPC_METHODS,
WALLET_CAPABILITIES,
getCapabilitySupportedChainInfo
} from '../../utils/EIP5792Utils'

export function EthersSendCallsTest() {
const [loading, setLoading] = useState(false)

const { address, chainId, isConnected } = useWeb3ModalAccount()
const { walletProvider } = useWeb3ModalProvider()
const toast = useChakraToast()

const atomicBatchSupportedChains =
address && walletProvider instanceof EthereumProvider
? getCapabilitySupportedChainInfo(WALLET_CAPABILITIES.ATOMIC_BATCH, walletProvider, address)
: []

const atomicBatchSupportedChainNames = atomicBatchSupportedChains
.map(ci => ci.chainName)
.join(', ')
const currentChainsInfo = atomicBatchSupportedChains.find(
chainInfo => chainInfo.chainId === Number(chainId)
)

async function onSendCalls() {
try {
setLoading(true)
if (!walletProvider || !address) {
throw Error('user is disconnected')
}
if (!chainId) {
throw Error('chain not selected')
}
const provider = new BrowserProvider(walletProvider, chainId)
const amountToSend = parseGwei('0.001').toString(16)
const calls = [
{
to: vitalikEthAddress as `0x${string}`,
data: '0x' as `0x${string}`,
value: `0x${amountToSend}`
},
{
to: vitalikEthAddress as Address,
value: '0x00',
data: '0xdeadbeef'
}
]
const sendCallsParams = {
version: '1.0',
chainId: `0x${BigInt(chainId).toString(16)}`,
from: address,
calls
}
const batchCallHash = await provider.send(EIP_5792_RPC_METHODS.WALLET_SEND_CALLS, [
sendCallsParams
])
toast({
title: 'Success',
description: batchCallHash,
type: 'success'
})
} catch {
toast({
title: 'Error',
description: 'Failed to send calls',
type: 'error'
})
} finally {
setLoading(false)
}
}
function isSendCallsSupported(): boolean {
if (walletProvider instanceof EthereumProvider) {
return Boolean(
walletProvider?.signer?.session?.namespaces?.['eip155']?.methods?.includes(
EIP_5792_RPC_METHODS.WALLET_SEND_CALLS
)
)
}

return false
}

if (!isConnected || !walletProvider || !address) {
return (
<Text fontSize="md" color="yellow">
Wallet not connected
</Text>
)
}
if (!isSendCallsSupported()) {
return (
<Text fontSize="md" color="yellow">
Wallet does not support wallet_sendCalls rpc
</Text>
)
}
if (atomicBatchSupportedChains.length === 0) {
return (
<Text fontSize="md" color="yellow">
Account does not support atomic batch feature
</Text>
)
}

return currentChainsInfo ? (
<Stack direction={['column', 'column', 'row']}>
<Button data-test-id="send-calls-button" onClick={onSendCalls} isDisabled={loading}>
Send Batch Calls to Vitalik
</Button>
<Spacer />
</Stack>
) : (
<Text fontSize="md" color="yellow">
Switch to {atomicBatchSupportedChainNames} to test atomic batch feature
</Text>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Button, Stack, Text, Input, Tooltip } from '@chakra-ui/react'
import { useState } from 'react'
import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react'
import { EthereumProvider } from '@walletconnect/ethereum-provider'
import { useChakraToast } from '../Toast'
import { parseGwei } from 'viem'
import { vitalikEthAddress } from '../../utils/DataUtil'
import { BrowserProvider } from 'ethers'
import {
EIP_5792_RPC_METHODS,
WALLET_CAPABILITIES,
getCapabilitySupportedChainInfo
} from '../../utils/EIP5792Utils'

export function EthersSendCallsWithPaymasterServiceTest() {
const [paymasterServiceUrl, setPaymasterServiceUrl] = useState<string>('')
const [isLoading, setLoading] = useState(false)

const { address, chainId, isConnected } = useWeb3ModalAccount()
const { walletProvider } = useWeb3ModalProvider()
const toast = useChakraToast()

const paymasterServiceSupportedChains =
address && walletProvider instanceof EthereumProvider
? getCapabilitySupportedChainInfo(
WALLET_CAPABILITIES.PAYMASTER_SERVICE,
walletProvider,
address
)
: []
const paymasterServiceSupportedChainNames = paymasterServiceSupportedChains
.map(ci => ci.chainName)
.join(', ')
const currentChainsInfo = paymasterServiceSupportedChains.find(
chainInfo => chainInfo.chainId === Number(chainId)
)
async function onSendCalls() {
try {
setLoading(true)
if (!walletProvider || !address) {
throw Error('user is disconnected')
}
if (!chainId) {
throw Error('chain not selected')
}

if (!paymasterServiceUrl) {
throw Error('paymasterServiceUrl not set')
}
const provider = new BrowserProvider(walletProvider, chainId)
const amountToSend = parseGwei('0.001').toString(16)
const calls = [
{
to: vitalikEthAddress,
value: `0x${amountToSend}`
},
{
to: vitalikEthAddress,
data: '0xdeadbeef'
}
]
const sendCallsParams = {
version: '1.0',
chainId: `0x${BigInt(chainId).toString(16)}`,
from: address,
calls,
capabilities: {
paymasterService: {
url: paymasterServiceUrl
}
}
}
const batchCallHash = await provider.send(EIP_5792_RPC_METHODS.WALLET_SEND_CALLS, [
sendCallsParams
])
toast({
title: 'SendCalls Success',
description: batchCallHash,
type: 'success'
})
} catch {
toast({
title: 'Error',
description: 'Failed to send calls',
type: 'error'
})
} finally {
setLoading(false)
}
}

function isSendCallsSupported(): boolean {
if (walletProvider instanceof EthereumProvider) {
return Boolean(
walletProvider?.signer?.session?.namespaces?.['eip155']?.methods?.includes(
EIP_5792_RPC_METHODS.WALLET_SEND_CALLS
)
)
}

return false
}

if (!isConnected || !walletProvider || !address) {
return (
<Text fontSize="md" color="yellow">
Wallet not connected
</Text>
)
}
if (!isSendCallsSupported()) {
return (
<Text fontSize="md" color="yellow">
Wallet does not support wallet_sendCalls rpc
</Text>
)
}
if (paymasterServiceSupportedChains.length === 0) {
return (
<Text fontSize="md" color="yellow">
Account does not support paymaster service feature
</Text>
)
}

return currentChainsInfo ? (
<Stack direction={['column', 'column', 'column']}>
<Tooltip label="Paymaster Service URL should be of ERC-7677 paymaster service proxy">
<Input
placeholder="http://api.pimlico.io/v2/sepolia/rpc?apikey=..."
onChange={e => setPaymasterServiceUrl(e.target.value)}
value={paymasterServiceUrl}
isDisabled={isLoading}
whiteSpace="nowrap"
textOverflow="ellipsis"
/>
</Tooltip>
<Button
width={'fit-content'}
data-test-id="send-calls-paymaster-service-button"
onClick={onSendCalls}
isDisabled={isLoading || !paymasterServiceUrl}
>
SendCalls w/ Paymaster Service
</Button>
</Stack>
) : (
<Text fontSize="md" color="yellow">
Switch to {paymasterServiceSupportedChainNames} to test paymaster service feature
</Text>
)
}
Loading

0 comments on commit 220f752

Please sign in to comment.