Skip to content

Commit 82f49e3

Browse files
Merge pull request #747 from 0xsequence/local-relayer
Receipt handling for local relayer. Chain switching for window.ethereum.
2 parents 4b7067a + 7b2e455 commit 82f49e3

File tree

2 files changed

+78
-15
lines changed

2 files changed

+78
-15
lines changed

packages/wallet/core/src/relayer/local.ts

+56-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { Constants, Payload } from '@0xsequence/wallet-primitives'
2-
import { AbiFunction, Address, Bytes, Hex } from 'ox'
2+
import { AbiFunction, Address, Bytes, Hex, TransactionReceipt } from 'ox'
33
import { FeeOption, FeeQuote, OperationStatus, Relayer } from './relayer.js'
44

5+
type GenericProviderTransactionReceipt = 'success' | 'failed' | 'unknown'
6+
57
export interface GenericProvider {
6-
sendTransaction(args: { to: string; data: string }): Promise<string>
8+
sendTransaction(args: { to: string; data: string }, chainId: bigint): Promise<string>
9+
getTransactionReceipt(txHash: string, chainId: bigint): Promise<GenericProviderTransactionReceipt>
710
}
811

912
export class LocalRelayer implements Relayer {
@@ -18,15 +21,33 @@ export class LocalRelayer implements Relayer {
1821
return undefined
1922
}
2023

24+
const trySwitchChain = async (chainId: bigint) => {
25+
try {
26+
await eth.request({
27+
method: 'wallet_switchEthereumChain',
28+
params: [
29+
{
30+
chainId: `0x${chainId.toString(16)}`,
31+
},
32+
],
33+
})
34+
} catch (error) {
35+
// Log and continue
36+
console.error('Error switching chain', error)
37+
}
38+
}
39+
2140
return new LocalRelayer({
22-
sendTransaction: async (args) => {
41+
sendTransaction: async (args, chainId) => {
2342
const accounts: string[] = await eth.request({ method: 'eth_requestAccounts' })
2443
const from = accounts[0]
2544
if (!from) {
2645
console.warn('No account selected, skipping local relayer')
2746
return undefined
2847
}
2948

49+
await trySwitchChain(chainId)
50+
3051
const tx = await eth.request({
3152
method: 'eth_sendTransaction',
3253
params: [
@@ -39,6 +60,20 @@ export class LocalRelayer implements Relayer {
3960
})
4061
return tx
4162
},
63+
getTransactionReceipt: async (txHash, chainId) => {
64+
await trySwitchChain(chainId)
65+
66+
const rpcReceipt = await eth.request({ method: 'eth_getTransactionReceipt', params: [txHash] })
67+
if (rpcReceipt) {
68+
const receipt = TransactionReceipt.fromRpc(rpcReceipt)
69+
if (receipt?.status === 'success') {
70+
return 'success'
71+
} else if (receipt?.status === 'reverted') {
72+
return 'failed'
73+
}
74+
}
75+
return 'unknown'
76+
},
4277
})
4378
}
4479

@@ -65,17 +100,26 @@ export class LocalRelayer implements Relayer {
65100
}
66101

67102
async relay(to: Address.Address, data: Hex.Hex, chainId: bigint, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> {
68-
const hash = Payload.hash(to, chainId, this.decodeCalls(data))
69-
70-
await this.provider.sendTransaction({
71-
to,
72-
data,
73-
})
103+
const txHash = await this.provider.sendTransaction(
104+
{
105+
to,
106+
data,
107+
},
108+
chainId,
109+
)
110+
Hex.assert(txHash)
74111

75-
return { opHash: Hex.fromBytes(hash) }
112+
return { opHash: txHash }
76113
}
77114

78-
status(opHash: Hex.Hex, chainId: bigint): Promise<OperationStatus> {
79-
throw new Error('Method not implemented.')
115+
async status(opHash: Hex.Hex, chainId: bigint): Promise<OperationStatus> {
116+
const receipt = await this.provider.getTransactionReceipt(opHash, chainId)
117+
if (receipt === 'unknown') {
118+
// Could be pending but we don't know
119+
return { status: 'unknown' }
120+
}
121+
return receipt === 'success'
122+
? { status: 'confirmed', transactionHash: opHash }
123+
: { status: 'failed', reason: 'failed' }
80124
}
81125
}

packages/wallet/core/src/relayer/pk-relayer.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Payload } from '@0xsequence/wallet-primitives'
2-
import { Address, Hex, Provider, Secp256k1, TransactionEnvelopeEip1559 } from 'ox'
2+
import { Address, Hex, Provider, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox'
33
import { LocalRelayer } from './local.js'
44
import { FeeOption, FeeQuote, OperationStatus, Relayer } from './relayer.js'
55

@@ -13,7 +13,12 @@ export class PkRelayer implements Relayer {
1313
) {
1414
const relayerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey }))
1515
this.relayer = new LocalRelayer({
16-
sendTransaction: async (args) => {
16+
sendTransaction: async (args, chainId) => {
17+
const providerChainId = BigInt(await this.provider.request({ method: 'eth_chainId' }))
18+
if (providerChainId !== chainId) {
19+
throw new Error('Provider chain id does not match relayer chain id')
20+
}
21+
1722
const oxArgs = { ...args, to: args.to as `0x${string}`, data: args.data as `0x${string}` }
1823
// Estimate gas with a safety buffer
1924
const estimatedGas = BigInt(await this.provider.request({ method: 'eth_estimateGas', params: [oxArgs] }))
@@ -40,7 +45,6 @@ export class PkRelayer implements Relayer {
4045
)
4146

4247
// Build the relay envelope
43-
const chainId = BigInt(await this.provider.request({ method: 'eth_chainId' }))
4448
const relayEnvelope = TransactionEnvelopeEip1559.from({
4549
chainId: Number(chainId),
4650
type: 'eip1559',
@@ -66,6 +70,21 @@ export class PkRelayer implements Relayer {
6670
})
6771
return tx
6872
},
73+
getTransactionReceipt: async (txHash: string, chainId: bigint) => {
74+
Hex.assert(txHash)
75+
76+
const providerChainId = BigInt(await this.provider.request({ method: 'eth_chainId' }))
77+
if (providerChainId !== chainId) {
78+
throw new Error('Provider chain id does not match relayer chain id')
79+
}
80+
81+
const rpcReceipt = await this.provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] })
82+
if (!rpcReceipt) {
83+
return 'unknown'
84+
}
85+
const receipt = TransactionReceipt.fromRpc(rpcReceipt)
86+
return receipt.status === 'success' ? 'success' : 'failed'
87+
},
6988
})
7089
}
7190

0 commit comments

Comments
 (0)