@0xsequence/relayer 0.43.33 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/0xsequence-relayer.cjs.dev.js +91 -218
- package/dist/0xsequence-relayer.cjs.prod.js +91 -218
- package/dist/0xsequence-relayer.esm.js +92 -216
- package/dist/declarations/src/index.d.ts +11 -10
- package/dist/declarations/src/local-relayer.d.ts +8 -8
- package/dist/declarations/src/provider-relayer.d.ts +15 -14
- package/dist/declarations/src/rpc-relayer/index.d.ts +18 -13
- package/package.json +8 -8
- package/src/index.ts +18 -14
- package/src/local-relayer.ts +24 -38
- package/src/provider-relayer.ts +23 -32
- package/src/rpc-relayer/index.ts +70 -80
- package/dist/declarations/src/base-relayer.d.ts +0 -35
- package/src/base-relayer.ts +0 -136
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
|
-
import { Transaction, SignedTransactions, TransactionResponse } from '@0xsequence/transactions';
|
|
3
|
-
import { BaseRelayer, BaseRelayerOptions } from '../base-relayer';
|
|
4
2
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from '..';
|
|
5
|
-
import { WalletContext } from '@0xsequence/network';
|
|
6
|
-
import { WalletConfig } from '@0xsequence/config';
|
|
7
3
|
import * as proto from './relayer.gen';
|
|
4
|
+
import { commons } from '@0xsequence/core';
|
|
8
5
|
export { proto };
|
|
9
|
-
export interface RpcRelayerOptions
|
|
6
|
+
export interface RpcRelayerOptions {
|
|
7
|
+
provider: ethers.providers.Provider | {
|
|
8
|
+
url: string;
|
|
9
|
+
};
|
|
10
10
|
url: string;
|
|
11
11
|
}
|
|
12
12
|
export declare function isRpcRelayerOptions(obj: any): obj is RpcRelayerOptions;
|
|
13
|
-
export declare class RpcRelayer
|
|
13
|
+
export declare class RpcRelayer implements Relayer {
|
|
14
14
|
private readonly service;
|
|
15
|
+
readonly provider: ethers.providers.Provider;
|
|
15
16
|
constructor(options: RpcRelayerOptions);
|
|
16
|
-
waitReceipt(metaTxnId: string |
|
|
17
|
-
simulate(wallet: string, ...transactions: Transaction[]): Promise<SimulateResult[]>;
|
|
18
|
-
getFeeOptions(
|
|
17
|
+
waitReceipt(metaTxnId: string | commons.transaction.SignedTransactionBundle, delay?: number, maxFails?: number, isCancelled?: () => boolean): Promise<proto.GetMetaTxnReceiptReturn>;
|
|
18
|
+
simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]>;
|
|
19
|
+
getFeeOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<{
|
|
19
20
|
options: FeeOption[];
|
|
20
21
|
quote?: FeeQuote;
|
|
21
22
|
}>;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
getFeeOptionsRaw(entrypoint: string, data: ethers.utils.BytesLike): Promise<{
|
|
24
|
+
options: FeeOption[];
|
|
25
|
+
quote?: FeeQuote;
|
|
26
|
+
}>;
|
|
27
|
+
gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]>;
|
|
28
|
+
getNonce(address: string, space?: ethers.BigNumberish): Promise<ethers.BigNumberish>;
|
|
29
|
+
relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse<RelayerTxReceipt>>;
|
|
30
|
+
wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay?: number, maxFails?: number): Promise<commons.transaction.TransactionResponse<RelayerTxReceipt>>;
|
|
26
31
|
}
|
|
27
32
|
export type RelayerTxReceipt = {
|
|
28
33
|
blockHash: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/relayer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "relayer sub-package for Sequence",
|
|
5
5
|
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/relayer",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
"author": "Horizon Blockchain Games",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@0xsequence/abi": "
|
|
13
|
-
"@0xsequence/
|
|
14
|
-
"@0xsequence/
|
|
15
|
-
"@0xsequence/transactions": "^0.43.33",
|
|
16
|
-
"@0xsequence/utils": "^0.43.33"
|
|
12
|
+
"@0xsequence/abi": "1.0.0",
|
|
13
|
+
"@0xsequence/utils": "1.0.0",
|
|
14
|
+
"@0xsequence/core": "1.0.0"
|
|
17
15
|
},
|
|
18
16
|
"peerDependencies": {
|
|
19
17
|
"ethers": ">=5.5 < 6"
|
|
20
18
|
},
|
|
21
19
|
"devDependencies": {
|
|
22
|
-
"@0xsequence/wallet-contracts": "1.10.0",
|
|
23
|
-
"ethers": "^5.7.2"
|
|
20
|
+
"@0xsequence/wallet-contracts": "^1.10.0",
|
|
21
|
+
"ethers": "^5.7.2",
|
|
22
|
+
"@0xsequence/signhub": "1.0.0",
|
|
23
|
+
"@0xsequence/tests": "1.0.0"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"src",
|
package/src/index.ts
CHANGED
|
@@ -1,48 +1,52 @@
|
|
|
1
1
|
import { ethers, providers } from 'ethers'
|
|
2
|
-
import { SignedTransactions, Transaction, TransactionResponse } from '@0xsequence/transactions'
|
|
3
|
-
import { WalletContext } from '@0xsequence/network'
|
|
4
|
-
import { WalletConfig } from '@0xsequence/config'
|
|
5
2
|
import { proto } from './rpc-relayer'
|
|
6
3
|
|
|
4
|
+
import { commons } from '@0xsequence/core'
|
|
5
|
+
|
|
7
6
|
export interface Relayer {
|
|
8
7
|
// simulate returns the execution results for a list of transactions.
|
|
9
|
-
simulate(wallet: string, ...transactions: Transaction[]): Promise<SimulateResult[]>
|
|
8
|
+
simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]>
|
|
10
9
|
|
|
11
10
|
// getFeeOptions returns the fee options that the relayer will accept as payment.
|
|
12
11
|
// If a quote is returned, it may be passed back to the relayer for dispatch.
|
|
13
12
|
getFeeOptions(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
...transactions: Transaction[]
|
|
13
|
+
address: string,
|
|
14
|
+
...transactions: commons.transaction.Transaction[]
|
|
17
15
|
): Promise<{ options: FeeOption[], quote?: FeeQuote }>
|
|
18
16
|
|
|
17
|
+
// getFeeOptionsRaw returns the fee options that the relayer will accept as payment.
|
|
18
|
+
// If a quote is returned, it may be passed back to the relayer for dispatch.
|
|
19
|
+
// It doesn't make any assumptions about the transaction format.
|
|
20
|
+
getFeeOptionsRaw(
|
|
21
|
+
entrypoint: string,
|
|
22
|
+
data: ethers.utils.BytesLike
|
|
23
|
+
): Promise<{ options: FeeOption[]; quote?: FeeQuote }>
|
|
24
|
+
|
|
19
25
|
// gasRefundOptions returns the transactions which can be included to refund a
|
|
20
26
|
// relayer for submitting your transaction to a network.
|
|
21
27
|
gasRefundOptions(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
...transactions: Transaction[]
|
|
28
|
+
address: string,
|
|
29
|
+
...transactions: commons.transaction.Transaction[]
|
|
25
30
|
): Promise<FeeOption[]>
|
|
26
31
|
|
|
27
32
|
// getNonce returns the transaction count/nonce for a wallet, encoded with nonce space.
|
|
28
33
|
// If space is undefined, the relayer can choose a nonce space to encode the result with.
|
|
29
34
|
// Otherwise, the relayer must return a nonce encoded for the given nonce space.
|
|
30
|
-
getNonce(
|
|
35
|
+
getNonce(address: string, space?: ethers.BigNumberish, blockTag?: providers.BlockTag): Promise<ethers.BigNumberish>
|
|
31
36
|
|
|
32
37
|
// relayer will submit the transaction(s) to the network and return the transaction response.
|
|
33
38
|
// The quote should be the one returned from getFeeOptions, if any.
|
|
34
39
|
// waitForReceipt must default to true.
|
|
35
|
-
relay(signedTxs:
|
|
40
|
+
relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse>
|
|
36
41
|
|
|
37
42
|
// wait for transaction confirmation
|
|
38
43
|
// timeout is the maximum time to wait for the transaction response
|
|
39
44
|
// delay is the polling interval, i.e. the time to wait between requests
|
|
40
45
|
// maxFails is the maximum number of hard failures to tolerate before giving up
|
|
41
|
-
wait(metaTxnId: string |
|
|
46
|
+
wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay?: number, maxFails?: number): Promise<commons.transaction.TransactionResponse>
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export * from './local-relayer'
|
|
45
|
-
export * from './base-relayer'
|
|
46
50
|
export * from './provider-relayer'
|
|
47
51
|
export * from './rpc-relayer'
|
|
48
52
|
export { proto as RpcRelayerProto } from './rpc-relayer'
|
package/src/local-relayer.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { Signer as AbstractSigner,
|
|
2
|
-
import { walletContracts } from '@0xsequence/abi'
|
|
3
|
-
import { SignedTransactions, Transaction, sequenceTxAbiEncode, TransactionResponse } from '@0xsequence/transactions'
|
|
4
|
-
import { WalletContext } from '@0xsequence/network'
|
|
5
|
-
import { WalletConfig } from '@0xsequence/config'
|
|
1
|
+
import { Signer as AbstractSigner, providers, BytesLike } from 'ethers'
|
|
6
2
|
import { logger } from '@0xsequence/utils'
|
|
7
3
|
import { FeeOption, FeeQuote, Relayer } from '.'
|
|
8
4
|
import { ProviderRelayer, ProviderRelayerOptions } from './provider-relayer'
|
|
5
|
+
import { commons } from '@0xsequence/core'
|
|
9
6
|
|
|
10
7
|
export type LocalRelayerOptions = Omit<ProviderRelayerOptions, "provider"> & {
|
|
11
8
|
signer: AbstractSigner
|
|
@@ -25,30 +22,22 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
25
22
|
if (!this.signer.provider) throw new Error("Signer must have a provider")
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
async deployWallet(config: WalletConfig, context: WalletContext): Promise<TransactionResponse> {
|
|
29
|
-
// NOTE: on hardhat some tests fail on HookCallerMock when not passing gasLimit directly as below,
|
|
30
|
-
// and using eth_gasEstimate. Perhaps review HookCallerMock.sol and fix it to avoid what looks
|
|
31
|
-
// like an infinite loop?
|
|
32
|
-
const walletDeployTxn = this.prepareWalletDeploy(config, context)
|
|
33
|
-
|
|
34
|
-
// NOTE: for hardhat to pass, we have to set the gasLimit directly, as its unable to estimate
|
|
35
|
-
return this.signer.sendTransaction({ ...walletDeployTxn, gasLimit: ethers.constants.Two.pow(17) } )
|
|
36
|
-
}
|
|
37
|
-
|
|
38
25
|
async getFeeOptions(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
..._transactions: Transaction[]
|
|
26
|
+
_address: string,
|
|
27
|
+
..._transactions: commons.transaction.Transaction[]
|
|
42
28
|
): Promise<{ options: FeeOption[] }> {
|
|
43
29
|
return { options: [] }
|
|
44
30
|
}
|
|
45
31
|
|
|
32
|
+
async getFeeOptionsRaw(_entrypoint: string, _data: BytesLike): Promise<{ options: FeeOption[] }> {
|
|
33
|
+
return { options: [] }
|
|
34
|
+
}
|
|
35
|
+
|
|
46
36
|
async gasRefundOptions(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...transactions: Transaction[]
|
|
37
|
+
address: string,
|
|
38
|
+
...transactions:commons.transaction.Transaction[]
|
|
50
39
|
): Promise<FeeOption[]> {
|
|
51
|
-
const { options } = await this.getFeeOptions(
|
|
40
|
+
const { options } = await this.getFeeOptions(address, ...transactions)
|
|
52
41
|
return options
|
|
53
42
|
}
|
|
54
43
|
|
|
@@ -56,33 +45,30 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
56
45
|
this.txnOptions = transactionRequest
|
|
57
46
|
}
|
|
58
47
|
|
|
59
|
-
async relay(
|
|
48
|
+
async relay(
|
|
49
|
+
signedTxs: commons.transaction.IntendedTransactionBundle,
|
|
50
|
+
quote?: FeeQuote, waitForReceipt: boolean = true
|
|
51
|
+
): Promise<commons.transaction.TransactionResponse<providers.TransactionReceipt>> {
|
|
60
52
|
if (quote !== undefined) {
|
|
61
53
|
logger.warn(`LocalRelayer doesn't accept fee quotes`)
|
|
62
54
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new Error('LocalRelayer requires the context.guestModule address')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const { to, execute } = await this.prependWalletDeploy(signedTxs)
|
|
69
|
-
|
|
70
|
-
const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi)
|
|
71
|
-
const data = walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [
|
|
72
|
-
sequenceTxAbiEncode(execute.transactions),
|
|
73
|
-
execute.nonce,
|
|
74
|
-
execute.signature
|
|
75
|
-
])
|
|
55
|
+
|
|
56
|
+
const data = commons.transaction.encodeBundleExecData(signedTxs)
|
|
76
57
|
|
|
77
58
|
// TODO: think about computing gas limit individually, summing together and passing across
|
|
78
59
|
// NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation
|
|
79
60
|
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum.add(tx.gasLimit), ethers.BigNumber.from(0))
|
|
80
61
|
// txRequest.gasLimit = gasLimit
|
|
81
62
|
|
|
82
|
-
const responsePromise = this.signer.sendTransaction({
|
|
63
|
+
const responsePromise = this.signer.sendTransaction({
|
|
64
|
+
to: signedTxs.entrypoint,
|
|
65
|
+
data,
|
|
66
|
+
...this.txnOptions,
|
|
67
|
+
gasLimit: 9000000
|
|
68
|
+
})
|
|
83
69
|
|
|
84
70
|
if (waitForReceipt) {
|
|
85
|
-
const response: TransactionResponse = await responsePromise
|
|
71
|
+
const response: commons.transaction.TransactionResponse = await responsePromise
|
|
86
72
|
response.receipt = await response.wait()
|
|
87
73
|
return response
|
|
88
74
|
} else {
|
package/src/provider-relayer.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { ethers, providers } from 'ethers'
|
|
2
2
|
import { walletContracts } from '@0xsequence/abi'
|
|
3
|
-
import { computeMetaTxnHash, encodeNonce, SignedTransactions, Transaction, TransactionResponse } from '@0xsequence/transactions'
|
|
4
|
-
import { WalletContext } from '@0xsequence/network'
|
|
5
|
-
import { WalletConfig, addressOf } from '@0xsequence/config'
|
|
6
|
-
import { BaseRelayer, BaseRelayerOptions } from './base-relayer'
|
|
7
3
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from '.'
|
|
8
|
-
import { logger, Optionals
|
|
4
|
+
import { logger, Optionals } from '@0xsequence/utils'
|
|
5
|
+
import { commons } from '@0xsequence/core'
|
|
9
6
|
|
|
10
7
|
const DEFAULT_GAS_LIMIT = ethers.BigNumber.from(800000)
|
|
11
8
|
|
|
12
|
-
export interface ProviderRelayerOptions
|
|
9
|
+
export interface ProviderRelayerOptions {
|
|
13
10
|
provider: providers.Provider,
|
|
14
11
|
waitPollRate?: number,
|
|
15
12
|
deltaBlocksLog?: number,
|
|
16
13
|
fromBlockLog?: number
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
export const ProviderRelayerDefaults: Required<Optionals<
|
|
16
|
+
export const ProviderRelayerDefaults: Required<Optionals<ProviderRelayerOptions>> = {
|
|
20
17
|
waitPollRate: 1000,
|
|
21
18
|
deltaBlocksLog: 12,
|
|
22
19
|
fromBlockLog: -1024
|
|
@@ -26,36 +23,35 @@ export function isProviderRelayerOptions(obj: any): obj is ProviderRelayerOption
|
|
|
26
23
|
return obj.provider !== undefined && providers.Provider.isProvider(obj.provider)
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
export abstract class ProviderRelayer
|
|
26
|
+
export abstract class ProviderRelayer implements Relayer {
|
|
30
27
|
public provider: providers.Provider
|
|
31
28
|
public waitPollRate: number
|
|
32
29
|
public deltaBlocksLog: number
|
|
33
30
|
public fromBlockLog: number
|
|
34
31
|
|
|
35
32
|
constructor(options: ProviderRelayerOptions) {
|
|
36
|
-
super(options)
|
|
37
33
|
const opts = { ...ProviderRelayerDefaults, ...options }
|
|
38
34
|
this.provider = opts.provider
|
|
39
|
-
this.waitPollRate = opts.waitPollRate
|
|
40
|
-
this.deltaBlocksLog = opts.deltaBlocksLog
|
|
41
|
-
this.fromBlockLog = opts.fromBlockLog
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
abstract getFeeOptions(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
address: string,
|
|
39
|
+
...transactions: commons.transaction.Transaction[]
|
|
40
|
+
): Promise<{ options: FeeOption[], quote?: FeeQuote }>
|
|
41
|
+
|
|
42
|
+
abstract getFeeOptionsRaw(
|
|
43
|
+
entrypoint: string,
|
|
44
|
+
data: ethers.utils.BytesLike
|
|
48
45
|
): Promise<{ options: FeeOption[], quote?: FeeQuote }>
|
|
49
46
|
|
|
50
47
|
abstract gasRefundOptions(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
...transactions: Transaction[]
|
|
48
|
+
address: string,
|
|
49
|
+
...transactions: commons.transaction.Transaction[]
|
|
54
50
|
): Promise<FeeOption[]>
|
|
55
51
|
|
|
56
|
-
abstract relay(signedTxs:
|
|
52
|
+
abstract relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse>
|
|
57
53
|
|
|
58
|
-
async simulate(wallet: string, ...transactions: Transaction[]): Promise<SimulateResult[]> {
|
|
54
|
+
async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]> {
|
|
59
55
|
return (await Promise.all(transactions.map(async tx => {
|
|
60
56
|
// Respect gasLimit request of the transaction (as long as its not 0)
|
|
61
57
|
if (tx.gasLimit && !ethers.BigNumber.from(tx.gasLimit || 0).eq(ethers.constants.Zero)) {
|
|
@@ -68,7 +64,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer {
|
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
// Fee can't be estimated for self-called if wallet hasn't been deployed
|
|
71
|
-
if (tx.to === wallet &&
|
|
67
|
+
if (tx.to === wallet && await this.provider.getCode(wallet).then((code) => ethers.utils.arrayify(code).length === 0)) {
|
|
72
68
|
return DEFAULT_GAS_LIMIT
|
|
73
69
|
}
|
|
74
70
|
|
|
@@ -93,8 +89,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer {
|
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
async getNonce(
|
|
96
|
-
|
|
97
|
-
context: WalletContext,
|
|
92
|
+
address: string,
|
|
98
93
|
space?: ethers.BigNumberish,
|
|
99
94
|
blockTag?: providers.BlockTag
|
|
100
95
|
): Promise<ethers.BigNumberish> {
|
|
@@ -102,9 +97,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer {
|
|
|
102
97
|
throw new Error('provider is not set')
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if ((await this.provider.getCode(addr)) === '0x') {
|
|
100
|
+
if ((await this.provider.getCode(address)) === '0x') {
|
|
108
101
|
return 0
|
|
109
102
|
}
|
|
110
103
|
|
|
@@ -112,21 +105,19 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer {
|
|
|
112
105
|
space = 0
|
|
113
106
|
}
|
|
114
107
|
|
|
115
|
-
const module = new ethers.Contract(
|
|
108
|
+
const module = new ethers.Contract(address, walletContracts.mainModule.abi, this.provider)
|
|
116
109
|
const nonce = await module.readNonce(space, { blockTag: blockTag })
|
|
117
|
-
return encodeNonce(space, nonce)
|
|
110
|
+
return commons.transaction.encodeNonce(space, nonce)
|
|
118
111
|
}
|
|
119
112
|
|
|
120
113
|
async wait(
|
|
121
|
-
metaTxnId: string |
|
|
114
|
+
metaTxnId: string | commons.transaction.SignedTransactionBundle,
|
|
122
115
|
timeout?: number,
|
|
123
116
|
delay: number = this.waitPollRate,
|
|
124
117
|
maxFails: number = 5
|
|
125
118
|
): Promise<providers.TransactionResponse & { receipt: providers.TransactionReceipt }> {
|
|
126
119
|
if (typeof metaTxnId !== 'string') {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
metaTxnId = computeMetaTxnHash(addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions)
|
|
120
|
+
metaTxnId = commons.transaction.intendedTransactionID(metaTxnId)
|
|
130
121
|
}
|
|
131
122
|
|
|
132
123
|
let timedOut = false
|
package/src/rpc-relayer/index.ts
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import { ethers } from 'ethers'
|
|
2
|
-
import { walletContracts } from '@0xsequence/abi'
|
|
3
|
-
import {
|
|
4
|
-
Transaction,
|
|
5
|
-
readSequenceNonce,
|
|
6
|
-
appendNonce,
|
|
7
|
-
MetaTransactionsType,
|
|
8
|
-
sequenceTxAbiEncode,
|
|
9
|
-
SignedTransactions,
|
|
10
|
-
computeMetaTxnHash,
|
|
11
|
-
decodeNonce,
|
|
12
|
-
TransactionResponse
|
|
13
|
-
} from '@0xsequence/transactions'
|
|
14
|
-
import { BaseRelayer, BaseRelayerOptions } from '../base-relayer'
|
|
15
2
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from '..'
|
|
16
|
-
import { WalletContext } from '@0xsequence/network'
|
|
17
|
-
import { WalletConfig, addressOf, buildStubSignature } from '@0xsequence/config'
|
|
18
|
-
import { logger } from '@0xsequence/utils'
|
|
19
3
|
import * as proto from './relayer.gen'
|
|
4
|
+
import { commons } from '@0xsequence/core'
|
|
5
|
+
import { getDefaultConnectionInfo, logger } from '@0xsequence/utils'
|
|
20
6
|
|
|
21
7
|
export { proto }
|
|
22
8
|
|
|
@@ -29,34 +15,41 @@ const FINAL_STATUSES = [
|
|
|
29
15
|
|
|
30
16
|
const FAILED_STATUSES = [proto.ETHTxnStatus.DROPPED, proto.ETHTxnStatus.PARTIALLY_FAILED, proto.ETHTxnStatus.FAILED]
|
|
31
17
|
|
|
32
|
-
export interface RpcRelayerOptions
|
|
18
|
+
export interface RpcRelayerOptions {
|
|
19
|
+
provider: ethers.providers.Provider | { url: string }
|
|
33
20
|
url: string
|
|
34
21
|
}
|
|
35
22
|
|
|
36
23
|
export function isRpcRelayerOptions(obj: any): obj is RpcRelayerOptions {
|
|
37
|
-
return
|
|
24
|
+
return (
|
|
25
|
+
obj.url !== undefined &&
|
|
26
|
+
typeof obj.url === 'string' &&
|
|
27
|
+
obj.provider !== undefined &&
|
|
28
|
+
ethers.providers.Provider.isProvider(obj.provider)
|
|
29
|
+
)
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
const fetch = typeof global === 'object' ? global.fetch : window.fetch
|
|
41
33
|
|
|
42
|
-
export class RpcRelayer
|
|
34
|
+
export class RpcRelayer implements Relayer {
|
|
43
35
|
private readonly service: proto.Relayer
|
|
36
|
+
public readonly provider: ethers.providers.Provider
|
|
44
37
|
|
|
45
38
|
constructor(options: RpcRelayerOptions) {
|
|
46
|
-
super(options)
|
|
47
39
|
this.service = new proto.Relayer(options.url, fetch)
|
|
40
|
+
this.provider = ethers.providers.Provider.isProvider(options.provider)
|
|
41
|
+
? options.provider
|
|
42
|
+
: new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(options.provider.url))
|
|
48
43
|
}
|
|
49
44
|
|
|
50
45
|
async waitReceipt(
|
|
51
|
-
metaTxnId: string |
|
|
46
|
+
metaTxnId: string | commons.transaction.SignedTransactionBundle,
|
|
52
47
|
delay: number = 1000,
|
|
53
48
|
maxFails: number = 5,
|
|
54
49
|
isCancelled?: () => boolean
|
|
55
50
|
): Promise<proto.GetMetaTxnReceiptReturn> {
|
|
56
51
|
if (typeof metaTxnId !== 'string') {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
metaTxnId = computeMetaTxnHash(addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions)
|
|
52
|
+
metaTxnId = commons.transaction.intendedTransactionID(metaTxnId)
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnId}`)
|
|
@@ -91,16 +84,18 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
91
84
|
throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`)
|
|
92
85
|
}
|
|
93
86
|
|
|
94
|
-
async simulate(wallet: string, ...transactions: Transaction[]): Promise<SimulateResult[]> {
|
|
87
|
+
async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]> {
|
|
95
88
|
const coder = ethers.utils.defaultAbiCoder
|
|
96
|
-
const encoded = coder.encode(
|
|
89
|
+
const encoded = coder.encode(
|
|
90
|
+
[commons.transaction.MetaTransactionsType],
|
|
91
|
+
[commons.transaction.sequenceTxAbiEncode(transactions)]
|
|
92
|
+
)
|
|
97
93
|
return (await this.service.simulate({ wallet, transactions: encoded })).results
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
async getFeeOptions(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
...transactions: Transaction[]
|
|
97
|
+
address: string,
|
|
98
|
+
...transactions: commons.transaction.Transaction[]
|
|
104
99
|
): Promise<{ options: FeeOption[]; quote?: FeeQuote }> {
|
|
105
100
|
// NOTE/TODO: for a given `service` the feeTokens will not change between execution, so we should memoize this value
|
|
106
101
|
// for a short-period of time, perhaps for 1 day or in memory. Perhaps one day we can make this happen automatically
|
|
@@ -111,35 +106,23 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
111
106
|
const symbols = feeTokens.tokens.map(token => token.symbol).join(', ')
|
|
112
107
|
logger.info(`[rpc-relayer/getFeeOptions] relayer fees are required, accepted tokens are ${symbols}`)
|
|
113
108
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
let nonce = readSequenceNonce(...transactions)
|
|
117
|
-
if (nonce === undefined) {
|
|
118
|
-
nonce = await this.getNonce(config, context)
|
|
119
|
-
}
|
|
109
|
+
const nonce = await this.getNonce(address)
|
|
120
110
|
|
|
121
111
|
if (!this.provider) {
|
|
122
112
|
logger.warn(`[rpc-relayer/getFeeOptions] provider not set, needed for stub signature`)
|
|
123
113
|
throw new Error('provider is not set')
|
|
124
114
|
}
|
|
125
115
|
|
|
126
|
-
const {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
116
|
+
const { options, quote } = await this.service.feeOptions({
|
|
117
|
+
wallet: address,
|
|
118
|
+
to: address,
|
|
119
|
+
data: commons.transaction.encodeBundleExecData({
|
|
120
|
+
entrypoint: address,
|
|
121
|
+
transactions,
|
|
122
|
+
nonce
|
|
123
|
+
})
|
|
132
124
|
})
|
|
133
125
|
|
|
134
|
-
const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi)
|
|
135
|
-
const data = walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [
|
|
136
|
-
sequenceTxAbiEncode(execute.transactions),
|
|
137
|
-
execute.nonce,
|
|
138
|
-
execute.signature
|
|
139
|
-
])
|
|
140
|
-
|
|
141
|
-
const { options, quote } = await this.service.feeOptions({ wallet, to, data })
|
|
142
|
-
|
|
143
126
|
logger.info(`[rpc-relayer/getFeeOptions] got refund options ${JSON.stringify(options)}`)
|
|
144
127
|
return { options, quote: { _tag: 'FeeQuote', _quote: quote } }
|
|
145
128
|
} else {
|
|
@@ -148,27 +131,36 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
148
131
|
}
|
|
149
132
|
}
|
|
150
133
|
|
|
151
|
-
async
|
|
152
|
-
const { options } = await this.
|
|
134
|
+
async getFeeOptionsRaw(entrypoint: string, data: ethers.utils.BytesLike): Promise<{ options: FeeOption[]; quote?: FeeQuote }> {
|
|
135
|
+
const { options, quote } = await this.service.feeOptions({
|
|
136
|
+
wallet: entrypoint,
|
|
137
|
+
to: entrypoint,
|
|
138
|
+
data: ethers.utils.hexlify(data)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return { options, quote: { _tag: 'FeeQuote', _quote: quote } }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]> {
|
|
145
|
+
const { options } = await this.getFeeOptions(address, ...transactions)
|
|
153
146
|
return options
|
|
154
147
|
}
|
|
155
148
|
|
|
156
|
-
async getNonce(
|
|
157
|
-
|
|
158
|
-
logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${addr} space: ${space}`)
|
|
149
|
+
async getNonce(address: string, space?: ethers.BigNumberish): Promise<ethers.BigNumberish> {
|
|
150
|
+
logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`)
|
|
159
151
|
const encodedNonce = space !== undefined ? ethers.BigNumber.from(space).toHexString() : undefined
|
|
160
|
-
const resp = await this.service.getMetaTxnNonce({ walletContractAddress:
|
|
152
|
+
const resp = await this.service.getMetaTxnNonce({ walletContractAddress: address, space: encodedNonce })
|
|
161
153
|
const nonce = ethers.BigNumber.from(resp.nonce)
|
|
162
|
-
const [decodedSpace, decodedNonce] = decodeNonce(nonce)
|
|
163
|
-
logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${
|
|
154
|
+
const [decodedSpace, decodedNonce] = commons.transaction.decodeNonce(nonce)
|
|
155
|
+
logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`)
|
|
164
156
|
return nonce
|
|
165
157
|
}
|
|
166
158
|
|
|
167
159
|
async relay(
|
|
168
|
-
signedTxs:
|
|
160
|
+
signedTxs: commons.transaction.IntendedTransactionBundle,
|
|
169
161
|
quote?: FeeQuote,
|
|
170
162
|
waitForReceipt: boolean = true
|
|
171
|
-
): Promise<TransactionResponse<RelayerTxReceipt>> {
|
|
163
|
+
): Promise<commons.transaction.TransactionResponse<RelayerTxReceipt>> {
|
|
172
164
|
logger.info(
|
|
173
165
|
`[rpc-relayer/relay] relaying signed meta-transactions ${JSON.stringify(signedTxs)} with quote ${JSON.stringify(quote)}`
|
|
174
166
|
)
|
|
@@ -187,27 +179,25 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
187
179
|
throw new Error('provider is not set')
|
|
188
180
|
}
|
|
189
181
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const metaTxn = await this.service.sendMetaTxn({ call: { walletAddress, contract, input }, quote: typecheckedQuote })
|
|
182
|
+
const data = commons.transaction.encodeBundleExecData(signedTxs)
|
|
183
|
+
const metaTxn = await this.service.sendMetaTxn({
|
|
184
|
+
call: {
|
|
185
|
+
walletAddress: signedTxs.intent.wallet,
|
|
186
|
+
contract: signedTxs.entrypoint,
|
|
187
|
+
input: data
|
|
188
|
+
},
|
|
189
|
+
quote: typecheckedQuote
|
|
190
|
+
})
|
|
201
191
|
|
|
202
192
|
logger.info(`[rpc-relayer/relay] got relay result ${JSON.stringify(metaTxn)}`)
|
|
203
193
|
|
|
204
194
|
if (waitForReceipt) {
|
|
205
|
-
return this.wait(
|
|
195
|
+
return this.wait(signedTxs.intent.id)
|
|
206
196
|
} else {
|
|
207
197
|
const response = {
|
|
208
|
-
hash:
|
|
198
|
+
hash: signedTxs.intent.id,
|
|
209
199
|
confirmations: 0,
|
|
210
|
-
from:
|
|
200
|
+
from: signedTxs.intent.wallet,
|
|
211
201
|
wait: (_confirmations?: number): Promise<ethers.providers.TransactionReceipt> => Promise.reject(new Error('impossible'))
|
|
212
202
|
}
|
|
213
203
|
|
|
@@ -216,7 +206,7 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
216
206
|
throw new Error('cannot wait for receipt, relayer has no provider set')
|
|
217
207
|
}
|
|
218
208
|
|
|
219
|
-
const waitResponse = await this.wait(
|
|
209
|
+
const waitResponse = await this.wait(signedTxs.intent.id)
|
|
220
210
|
const transactionHash = waitResponse.receipt?.transactionHash
|
|
221
211
|
|
|
222
212
|
if (!transactionHash) {
|
|
@@ -230,16 +220,16 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
230
220
|
|
|
231
221
|
response.wait = wait
|
|
232
222
|
|
|
233
|
-
return response as TransactionResponse
|
|
223
|
+
return response as commons.transaction.TransactionResponse
|
|
234
224
|
}
|
|
235
225
|
}
|
|
236
226
|
|
|
237
227
|
async wait(
|
|
238
|
-
metaTxnId: string |
|
|
228
|
+
metaTxnId: string | commons.transaction.SignedTransactionBundle,
|
|
239
229
|
timeout?: number,
|
|
240
230
|
delay: number = 1000,
|
|
241
231
|
maxFails: number = 5
|
|
242
|
-
): Promise<TransactionResponse<RelayerTxReceipt>> {
|
|
232
|
+
): Promise<commons.transaction.TransactionResponse<RelayerTxReceipt>> {
|
|
243
233
|
let timedOut = false
|
|
244
234
|
|
|
245
235
|
const { receipt } = await (timeout !== undefined
|
|
@@ -264,12 +254,12 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
|
|
|
264
254
|
blockHash: txReceipt.blockHash,
|
|
265
255
|
blockNumber: ethers.BigNumber.from(txReceipt.blockNumber).toNumber(),
|
|
266
256
|
confirmations: 1,
|
|
267
|
-
from: typeof metaTxnId === 'string' ? undefined :
|
|
257
|
+
from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet,
|
|
268
258
|
hash: txReceipt.transactionHash,
|
|
269
259
|
raw: receipt.txnReceipt,
|
|
270
260
|
receipt: txReceipt, // extended type which is Sequence-specific. Contains the decoded metaTxReceipt
|
|
271
261
|
wait: async (confirmations?: number) => this.provider!.waitForTransaction(txReceipt.transactionHash, confirmations)
|
|
272
|
-
} as TransactionResponse
|
|
262
|
+
} as commons.transaction.TransactionResponse
|
|
273
263
|
}
|
|
274
264
|
}
|
|
275
265
|
|