@chainlink/ccip-sdk 0.90.2 → 0.91.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/README.md +35 -26
- package/dist/aptos/exec.d.ts +4 -5
- package/dist/aptos/exec.d.ts.map +1 -1
- package/dist/aptos/exec.js +5 -14
- package/dist/aptos/exec.js.map +1 -1
- package/dist/aptos/hasher.d.ts +18 -0
- package/dist/aptos/hasher.d.ts.map +1 -1
- package/dist/aptos/hasher.js +18 -0
- package/dist/aptos/hasher.js.map +1 -1
- package/dist/aptos/index.d.ts +127 -28
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +199 -70
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.d.ts +18 -0
- package/dist/aptos/logs.d.ts.map +1 -1
- package/dist/aptos/logs.js +21 -3
- package/dist/aptos/logs.js.map +1 -1
- package/dist/aptos/send.d.ts +22 -5
- package/dist/aptos/send.d.ts.map +1 -1
- package/dist/aptos/send.js +23 -15
- package/dist/aptos/send.js.map +1 -1
- package/dist/aptos/token.d.ts +6 -0
- package/dist/aptos/token.d.ts.map +1 -1
- package/dist/aptos/token.js +6 -0
- package/dist/aptos/token.js.map +1 -1
- package/dist/aptos/types.d.ts +16 -1
- package/dist/aptos/types.d.ts.map +1 -1
- package/dist/aptos/types.js +13 -0
- package/dist/aptos/types.js.map +1 -1
- package/dist/aptos/utils.d.ts +1 -1
- package/dist/aptos/utils.js +1 -1
- package/dist/chain.d.ts +185 -99
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +38 -15
- package/dist/chain.js.map +1 -1
- package/dist/commits.d.ts +4 -10
- package/dist/commits.d.ts.map +1 -1
- package/dist/commits.js +2 -1
- package/dist/commits.js.map +1 -1
- package/dist/evm/const.d.ts +5 -0
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +5 -0
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/errors.d.ts +5 -0
- package/dist/evm/errors.d.ts.map +1 -1
- package/dist/evm/errors.js +6 -1
- package/dist/evm/errors.js.map +1 -1
- package/dist/evm/hasher.d.ts +16 -2
- package/dist/evm/hasher.d.ts.map +1 -1
- package/dist/evm/hasher.js +17 -3
- package/dist/evm/hasher.js.map +1 -1
- package/dist/evm/index.d.ts +176 -31
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +312 -154
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/logs.d.ts +20 -0
- package/dist/evm/logs.d.ts.map +1 -0
- package/dist/evm/logs.js +194 -0
- package/dist/evm/logs.js.map +1 -0
- package/dist/evm/messages.d.ts +11 -2
- package/dist/evm/messages.d.ts.map +1 -1
- package/dist/evm/messages.js +4 -2
- package/dist/evm/messages.js.map +1 -1
- package/dist/evm/offchain.d.ts +7 -2
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +12 -7
- package/dist/evm/offchain.js.map +1 -1
- package/dist/execution.d.ts +19 -62
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +28 -31
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +35 -5
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +10 -5
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +6 -8
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +7 -9
- package/dist/gas.js.map +1 -1
- package/dist/hasher/common.d.ts +3 -2
- package/dist/hasher/common.d.ts.map +1 -1
- package/dist/hasher/common.js +2 -2
- package/dist/hasher/common.js.map +1 -1
- package/dist/hasher/hasher.d.ts +8 -2
- package/dist/hasher/hasher.d.ts.map +1 -1
- package/dist/hasher/hasher.js +8 -3
- package/dist/hasher/hasher.js.map +1 -1
- package/dist/hasher/merklemulti.d.ts +11 -9
- package/dist/hasher/merklemulti.d.ts.map +1 -1
- package/dist/hasher/merklemulti.js +17 -16
- package/dist/hasher/merklemulti.js.map +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +39 -25
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +42 -35
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts +1 -1
- package/dist/solana/cleanup.d.ts +14 -10
- package/dist/solana/cleanup.d.ts.map +1 -1
- package/dist/solana/cleanup.js +35 -33
- package/dist/solana/cleanup.js.map +1 -1
- package/dist/solana/exec.d.ts +19 -11
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +86 -163
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/hasher.d.ts +7 -2
- package/dist/solana/hasher.d.ts.map +1 -1
- package/dist/solana/hasher.js +7 -2
- package/dist/solana/hasher.js.map +1 -1
- package/dist/solana/index.d.ts +202 -84
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +367 -252
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.d.ts +8 -18
- package/dist/solana/offchain.d.ts.map +1 -1
- package/dist/solana/offchain.js +29 -83
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/patchBorsh.d.ts +5 -1
- package/dist/solana/patchBorsh.d.ts.map +1 -1
- package/dist/solana/patchBorsh.js +57 -46
- package/dist/solana/patchBorsh.js.map +1 -1
- package/dist/solana/send.d.ts +28 -10
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +44 -77
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/types.d.ts +22 -1
- package/dist/solana/types.d.ts.map +1 -1
- package/dist/solana/types.js +12 -1
- package/dist/solana/types.js.map +1 -1
- package/dist/solana/utils.d.ts +58 -4
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +110 -7
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/hasher.d.ts +18 -0
- package/dist/sui/hasher.d.ts.map +1 -1
- package/dist/sui/hasher.js +18 -0
- package/dist/sui/hasher.js.map +1 -1
- package/dist/sui/index.d.ts +99 -12
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +108 -19
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/types.d.ts +6 -0
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js +5 -0
- package/dist/sui/types.js.map +1 -1
- package/dist/supported-chains.d.ts +2 -1
- package/dist/supported-chains.d.ts.map +1 -1
- package/dist/supported-chains.js.map +1 -1
- package/dist/types.d.ts +127 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +67 -46
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +143 -21
- package/dist/utils.js.map +1 -1
- package/package.json +13 -9
- package/src/aptos/exec.ts +7 -18
- package/src/aptos/hasher.ts +18 -0
- package/src/aptos/index.ts +288 -110
- package/src/aptos/logs.ts +21 -3
- package/src/aptos/send.ts +25 -22
- package/src/aptos/token.ts +6 -0
- package/src/aptos/types.ts +26 -2
- package/src/aptos/utils.ts +1 -1
- package/src/chain.ts +243 -108
- package/src/commits.ts +6 -7
- package/src/evm/const.ts +5 -0
- package/src/evm/errors.ts +6 -1
- package/src/evm/hasher.ts +20 -4
- package/src/evm/index.ts +416 -214
- package/src/evm/logs.ts +255 -0
- package/src/evm/messages.ts +11 -5
- package/src/evm/offchain.ts +13 -4
- package/src/execution.ts +40 -32
- package/src/extra-args.ts +38 -6
- package/src/gas.ts +7 -9
- package/src/hasher/common.ts +3 -2
- package/src/hasher/hasher.ts +12 -4
- package/src/hasher/merklemulti.ts +17 -16
- package/src/index.ts +29 -23
- package/src/requests.ts +64 -46
- package/src/selectors.ts +1 -1
- package/src/solana/cleanup.ts +49 -34
- package/src/solana/exec.ts +128 -272
- package/src/solana/hasher.ts +13 -4
- package/src/solana/index.ts +483 -356
- package/src/solana/offchain.ts +32 -102
- package/src/solana/patchBorsh.ts +65 -50
- package/src/solana/send.ts +52 -111
- package/src/solana/types.ts +44 -3
- package/src/solana/utils.ts +143 -19
- package/src/sui/hasher.ts +18 -0
- package/src/sui/index.ts +143 -31
- package/src/sui/types.ts +6 -0
- package/src/supported-chains.ts +2 -1
- package/src/types.ts +130 -18
- package/src/utils.ts +168 -26
- package/tsconfig.json +2 -1
package/src/evm/index.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import util from 'util'
|
|
2
|
-
|
|
3
1
|
import { parseAbi } from 'abitype'
|
|
4
2
|
import {
|
|
5
3
|
type BytesLike,
|
|
4
|
+
type Interface,
|
|
6
5
|
type JsonRpcApiProvider,
|
|
7
6
|
type Log,
|
|
8
|
-
type Provider,
|
|
9
7
|
type Signer,
|
|
10
8
|
type TransactionReceipt,
|
|
11
|
-
|
|
12
|
-
BaseWallet,
|
|
9
|
+
type TransactionRequest,
|
|
13
10
|
Contract,
|
|
14
11
|
JsonRpcProvider,
|
|
15
12
|
Result,
|
|
16
|
-
SigningKey,
|
|
17
13
|
WebSocketProvider,
|
|
18
14
|
ZeroAddress,
|
|
19
15
|
concat,
|
|
@@ -24,19 +20,15 @@ import {
|
|
|
24
20
|
hexlify,
|
|
25
21
|
isBytesLike,
|
|
26
22
|
isHexString,
|
|
23
|
+
toBeHex,
|
|
27
24
|
toBigInt,
|
|
28
25
|
zeroPadValue,
|
|
29
26
|
} from 'ethers'
|
|
30
27
|
import type { TypedContract } from 'ethers-abitype'
|
|
31
|
-
import
|
|
28
|
+
import { memoize } from 'micro-memoize'
|
|
29
|
+
import type { PickDeep, SetRequired } from 'type-fest'
|
|
32
30
|
|
|
33
|
-
import {
|
|
34
|
-
type ChainTransaction,
|
|
35
|
-
type LogFilter,
|
|
36
|
-
type TokenPoolRemote,
|
|
37
|
-
Chain,
|
|
38
|
-
ChainFamily,
|
|
39
|
-
} from '../chain.ts'
|
|
31
|
+
import { type LogFilter, type TokenPoolRemote, Chain } from '../chain.ts'
|
|
40
32
|
import {
|
|
41
33
|
type EVMExtraArgsV1,
|
|
42
34
|
type EVMExtraArgsV2,
|
|
@@ -52,8 +44,11 @@ import type { LeafHasher } from '../hasher/common.ts'
|
|
|
52
44
|
import { supportedChains } from '../supported-chains.ts'
|
|
53
45
|
import {
|
|
54
46
|
type AnyMessage,
|
|
47
|
+
type CCIPCommit,
|
|
48
|
+
type CCIPExecution,
|
|
55
49
|
type CCIPMessage,
|
|
56
50
|
type CCIPRequest,
|
|
51
|
+
type ChainTransaction,
|
|
57
52
|
type CommitReport,
|
|
58
53
|
type ExecutionReceipt,
|
|
59
54
|
type ExecutionReport,
|
|
@@ -62,17 +57,18 @@ import {
|
|
|
62
57
|
type Log_,
|
|
63
58
|
type NetworkInfo,
|
|
64
59
|
type OffchainTokenData,
|
|
60
|
+
type WithLogger,
|
|
65
61
|
CCIPVersion,
|
|
62
|
+
ChainFamily,
|
|
66
63
|
} from '../types.ts'
|
|
67
64
|
import {
|
|
68
|
-
blockRangeGenerator,
|
|
69
65
|
decodeAddress,
|
|
70
66
|
decodeOnRampAddress,
|
|
71
67
|
getAddressBytes,
|
|
72
68
|
getDataBytes,
|
|
73
|
-
getSomeBlockNumberBefore,
|
|
74
69
|
networkInfo,
|
|
75
70
|
parseTypeAndVersion,
|
|
71
|
+
util,
|
|
76
72
|
} from '../utils.ts'
|
|
77
73
|
import type Token_ABI from './abi/BurnMintERC677Token.ts'
|
|
78
74
|
import type FeeQuoter_ABI from './abi/FeeQuoter_1_6.ts'
|
|
@@ -87,23 +83,35 @@ import OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
|
|
|
87
83
|
import type Router_ABI from './abi/Router.ts'
|
|
88
84
|
import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
|
|
89
85
|
import {
|
|
90
|
-
DEFAULT_APPROVE_GAS_LIMIT,
|
|
91
86
|
DEFAULT_GAS_LIMIT,
|
|
92
87
|
commitsFragments,
|
|
93
88
|
defaultAbiCoder,
|
|
94
|
-
getAllFragmentsMatchingEvents,
|
|
95
89
|
interfaces,
|
|
96
90
|
receiptsFragments,
|
|
97
91
|
requestsFragments,
|
|
98
92
|
} from './const.ts'
|
|
99
93
|
import { parseData } from './errors.ts'
|
|
100
94
|
import { getV12LeafHasher, getV16LeafHasher } from './hasher.ts'
|
|
95
|
+
import { getEvmLogs } from './logs.ts'
|
|
101
96
|
import {
|
|
102
97
|
type CCIPMessage_V1_6_EVM,
|
|
103
98
|
type CleanAddressable,
|
|
104
99
|
parseSourceTokenData,
|
|
105
100
|
} from './messages.ts'
|
|
106
101
|
import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from './offchain.ts'
|
|
102
|
+
import {
|
|
103
|
+
fetchAllMessagesInBatch,
|
|
104
|
+
fetchCCIPRequestById,
|
|
105
|
+
fetchCCIPRequestsInTx,
|
|
106
|
+
} from '../requests.ts'
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Type representing a set of unsigned EVM transactions
|
|
110
|
+
*/
|
|
111
|
+
export type UnsignedEVMTx = {
|
|
112
|
+
family: typeof ChainFamily.EVM
|
|
113
|
+
transactions: Pick<TransactionRequest, 'from' | 'to' | 'data'>[]
|
|
114
|
+
}
|
|
107
115
|
|
|
108
116
|
const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)'])
|
|
109
117
|
|
|
@@ -134,81 +142,67 @@ function resultsToMessage(result: Result): Record<string, unknown> {
|
|
|
134
142
|
} as unknown as CCIPMessage
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
/** typeguard for ethers Signer interface (used for `wallet`s) */
|
|
146
|
+
function isSigner(wallet: unknown): wallet is Signer {
|
|
147
|
+
return (
|
|
148
|
+
typeof wallet === 'object' &&
|
|
149
|
+
wallet !== null &&
|
|
150
|
+
'signTransaction' in wallet &&
|
|
151
|
+
'getAddress' in wallet
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* EVM chain implementation supporting Ethereum-compatible networks.
|
|
157
|
+
*/
|
|
137
158
|
export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
159
|
+
static {
|
|
160
|
+
supportedChains[ChainFamily.EVM] = EVMChain
|
|
161
|
+
}
|
|
138
162
|
static readonly family = ChainFamily.EVM
|
|
139
163
|
static readonly decimals = 18
|
|
140
164
|
|
|
141
|
-
|
|
142
|
-
readonly
|
|
165
|
+
provider: JsonRpcApiProvider
|
|
166
|
+
readonly destroy$: Promise<void>
|
|
143
167
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Creates a new EVMChain instance.
|
|
170
|
+
* @param provider - JSON-RPC provider for the EVM network.
|
|
171
|
+
* @param network - Network information for this chain.
|
|
172
|
+
*/
|
|
173
|
+
constructor(provider: JsonRpcApiProvider, network: NetworkInfo, ctx?: WithLogger) {
|
|
174
|
+
super(network, ctx)
|
|
148
175
|
|
|
149
|
-
this.network = network
|
|
150
176
|
this.provider = provider
|
|
177
|
+
this.destroy$ = new Promise<void>((resolve) => (this.destroy = resolve))
|
|
178
|
+
void this.destroy$.finally(() => provider.destroy())
|
|
151
179
|
|
|
152
|
-
this.typeAndVersion =
|
|
153
|
-
|
|
180
|
+
this.typeAndVersion = memoize(this.typeAndVersion.bind(this))
|
|
181
|
+
|
|
182
|
+
this.provider.getBlock = memoize(provider.getBlock.bind(provider), {
|
|
154
183
|
maxSize: 100,
|
|
155
|
-
|
|
184
|
+
maxArgs: 1,
|
|
185
|
+
async: true,
|
|
186
|
+
forceUpdate: ([block]) => typeof block !== 'number',
|
|
156
187
|
})
|
|
157
|
-
this.getTransaction =
|
|
188
|
+
this.getTransaction = memoize(this.getTransaction.bind(this), {
|
|
158
189
|
maxSize: 100,
|
|
159
|
-
|
|
190
|
+
transformKey: (args) =>
|
|
160
191
|
typeof args[0] !== 'string'
|
|
161
192
|
? [(args[0] as unknown as TransactionReceipt).hash]
|
|
162
193
|
: (args as unknown as string[]),
|
|
163
194
|
})
|
|
164
|
-
this.getTokenForTokenPool =
|
|
165
|
-
this.getNativeTokenForRouter =
|
|
195
|
+
this.getTokenForTokenPool = memoize(this.getTokenForTokenPool.bind(this))
|
|
196
|
+
this.getNativeTokenForRouter = memoize(this.getNativeTokenForRouter.bind(this), {
|
|
166
197
|
maxArgs: 1,
|
|
167
|
-
|
|
198
|
+
async: true,
|
|
168
199
|
})
|
|
169
|
-
this.getTokenInfo =
|
|
170
|
-
this.
|
|
171
|
-
|
|
172
|
-
isPromise: true,
|
|
200
|
+
this.getTokenInfo = memoize(this.getTokenInfo.bind(this))
|
|
201
|
+
this.getTokenAdminRegistryFor = memoize(this.getTokenAdminRegistryFor.bind(this), {
|
|
202
|
+
async: true,
|
|
173
203
|
maxArgs: 1,
|
|
174
204
|
})
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// overwrite EVMChain.getWallet to implement custom wallet loading
|
|
178
|
-
// some signers don't like to be `.connect`ed, so pass provider as first param
|
|
179
|
-
static getWallet(_provider: Provider, _opts: { wallet?: unknown }): Promise<Signer> {
|
|
180
|
-
throw new Error('static EVM wallet loading not available')
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// cached wallet/signer getter
|
|
184
|
-
async getWallet(opts: { wallet?: unknown } = {}): Promise<Signer> {
|
|
185
|
-
if (
|
|
186
|
-
typeof opts.wallet === 'number' ||
|
|
187
|
-
(typeof opts.wallet === 'string' && opts.wallet.match(/^(\d+|0x[a-fA-F0-9]{40})$/))
|
|
188
|
-
) {
|
|
189
|
-
// if given a number, numeric string or address, use ethers `provider.getSigner` (e.g. geth or MM)
|
|
190
|
-
return this.provider.getSigner(
|
|
191
|
-
typeof opts.wallet === 'string' && opts.wallet.match(/^0x[a-fA-F0-9]{40}$/)
|
|
192
|
-
? opts.wallet
|
|
193
|
-
: Number(opts.wallet),
|
|
194
|
-
)
|
|
195
|
-
} else if (typeof opts.wallet === 'string') {
|
|
196
|
-
// support receiving private key directly (not recommended)
|
|
197
|
-
try {
|
|
198
|
-
return Promise.resolve(
|
|
199
|
-
new BaseWallet(
|
|
200
|
-
new SigningKey((opts.wallet.startsWith('0x') ? '' : '0x') + opts.wallet),
|
|
201
|
-
this.provider,
|
|
202
|
-
),
|
|
203
|
-
)
|
|
204
|
-
} catch (_) {
|
|
205
|
-
// pass
|
|
206
|
-
}
|
|
207
|
-
} else if (opts.wallet instanceof AbstractSigner) {
|
|
208
|
-
// if given a signer, return/cache it
|
|
209
|
-
return opts.wallet
|
|
210
|
-
}
|
|
211
|
-
return (this.constructor as typeof EVMChain).getWallet(this.provider, opts)
|
|
205
|
+
this.getFeeTokens = memoize(this.getFeeTokens.bind(this), { async: true, maxArgs: 1 })
|
|
212
206
|
}
|
|
213
207
|
|
|
214
208
|
/**
|
|
@@ -218,10 +212,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
218
212
|
return (await this.provider.listAccounts()).map(({ address }) => address)
|
|
219
213
|
}
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Creates a JSON-RPC provider from a URL.
|
|
217
|
+
* @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
|
|
218
|
+
* @returns A ready JSON-RPC provider.
|
|
219
|
+
*/
|
|
225
220
|
static async _getProvider(url: string): Promise<JsonRpcApiProvider> {
|
|
226
221
|
let provider: JsonRpcApiProvider
|
|
227
222
|
let providerReady: Promise<JsonRpcApiProvider>
|
|
@@ -246,37 +241,45 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
246
241
|
return providerReady
|
|
247
242
|
}
|
|
248
243
|
|
|
249
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Creates an EVMChain instance from an existing provider.
|
|
246
|
+
* @param provider - JSON-RPC provider instance.
|
|
247
|
+
* @param ctx - context containing logger.
|
|
248
|
+
* @returns A new EVMChain instance.
|
|
249
|
+
*/
|
|
250
|
+
static async fromProvider(provider: JsonRpcApiProvider, ctx?: WithLogger): Promise<EVMChain> {
|
|
250
251
|
try {
|
|
251
|
-
return new EVMChain(provider, networkInfo(Number((await provider.getNetwork()).chainId)))
|
|
252
|
+
return new EVMChain(provider, networkInfo(Number((await provider.getNetwork()).chainId)), ctx)
|
|
252
253
|
} catch (err) {
|
|
253
254
|
provider.destroy()
|
|
254
255
|
throw err
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Creates an EVMChain instance from an RPC URL.
|
|
261
|
+
* @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
|
|
262
|
+
* @param ctx - context containing logger.
|
|
263
|
+
* @returns A new EVMChain instance.
|
|
264
|
+
*/
|
|
265
|
+
static async fromUrl(url: string, ctx?: WithLogger): Promise<EVMChain> {
|
|
266
|
+
return this.fromProvider(await this._getProvider(url), ctx)
|
|
265
267
|
}
|
|
266
268
|
|
|
269
|
+
/** {@inheritDoc Chain.getBlockTimestamp} */
|
|
267
270
|
async getBlockTimestamp(block: number | 'finalized'): Promise<number> {
|
|
268
|
-
const res = await this.provider.getBlock(block)
|
|
271
|
+
const res = await this.provider.getBlock(block) // cached
|
|
269
272
|
if (!res) throw new Error(`Block not found: ${block}`)
|
|
270
273
|
return res.timestamp
|
|
271
274
|
}
|
|
272
275
|
|
|
276
|
+
/** {@inheritDoc Chain.getTransaction} */
|
|
273
277
|
async getTransaction(hash: string | TransactionReceipt): Promise<ChainTransaction> {
|
|
274
278
|
const tx = typeof hash === 'string' ? await this.provider.getTransactionReceipt(hash) : hash
|
|
275
279
|
if (!tx) throw new Error(`Transaction not found: ${hash as string}`)
|
|
276
280
|
const timestamp = await this.getBlockTimestamp(tx.blockNumber)
|
|
277
281
|
const chainTx = {
|
|
278
282
|
...tx,
|
|
279
|
-
chain: this,
|
|
280
283
|
timestamp,
|
|
281
284
|
logs: [] as Log_[],
|
|
282
285
|
}
|
|
@@ -285,47 +288,62 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
285
288
|
return chainTx
|
|
286
289
|
}
|
|
287
290
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
291
|
+
/** {@inheritDoc Chain.getLogs} */
|
|
292
|
+
async *getLogs(filter: LogFilter & { onlyFallback?: boolean }): AsyncIterableIterator<Log> {
|
|
293
|
+
yield* getEvmLogs(filter, this)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** {@inheritDoc Chain.fetchRequestsInTx} */
|
|
297
|
+
async fetchRequestsInTx(tx: string | ChainTransaction): Promise<CCIPRequest[]> {
|
|
298
|
+
return fetchCCIPRequestsInTx(this, typeof tx === 'string' ? await this.getTransaction(tx) : tx)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** {@inheritDoc Chain.fetchRequestById} */
|
|
302
|
+
override fetchRequestById(
|
|
303
|
+
messageId: string,
|
|
304
|
+
onRamp?: string,
|
|
305
|
+
opts?: { page?: number },
|
|
306
|
+
): Promise<CCIPRequest> {
|
|
307
|
+
return fetchCCIPRequestById(this, messageId, { address: onRamp, ...opts })
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** {@inheritDoc Chain.fetchAllMessagesInBatch} */
|
|
311
|
+
async fetchAllMessagesInBatch<
|
|
312
|
+
R extends PickDeep<
|
|
313
|
+
CCIPRequest,
|
|
314
|
+
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.header.sequenceNumber'
|
|
315
|
+
>,
|
|
316
|
+
>(
|
|
317
|
+
request: R,
|
|
318
|
+
commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
|
|
319
|
+
opts?: { page?: number },
|
|
320
|
+
): Promise<R['message'][]> {
|
|
321
|
+
let opts_: Parameters<EVMChain['getLogs']>[0] | undefined
|
|
322
|
+
if (request.lane.version >= CCIPVersion.V1_6) {
|
|
323
|
+
// specialized getLogs filter for v1.6 CCIPMessageSent events, to filter by dest
|
|
324
|
+
opts_ = {
|
|
325
|
+
...opts,
|
|
326
|
+
topics: [[request.log.topics[0]], [toBeHex(request.lane.destChainSelector, 32)]],
|
|
303
327
|
}
|
|
304
|
-
filter.topics = [Array.from(topics)]
|
|
305
|
-
}
|
|
306
|
-
if (!filter.startBlock && filter.startTime) {
|
|
307
|
-
filter.startBlock = await getSomeBlockNumberBefore(
|
|
308
|
-
this.getBlockTimestamp.bind(this),
|
|
309
|
-
endBlock,
|
|
310
|
-
filter.startTime,
|
|
311
|
-
)
|
|
312
|
-
}
|
|
313
|
-
for (const blockRange of blockRangeGenerator({ ...filter, endBlock })) {
|
|
314
|
-
console.debug('evm getLogs:', {
|
|
315
|
-
...blockRange,
|
|
316
|
-
...(filter.address ? { address: filter.address } : {}),
|
|
317
|
-
...(filter.topics?.length ? { topics: filter.topics } : {}),
|
|
318
|
-
})
|
|
319
|
-
const logs = await this.provider.getLogs({
|
|
320
|
-
...blockRange,
|
|
321
|
-
...(filter.address ? { address: filter.address } : {}),
|
|
322
|
-
...(filter.topics?.length ? { topics: filter.topics } : {}),
|
|
323
|
-
})
|
|
324
|
-
if (!filter.startBlock) logs.reverse()
|
|
325
|
-
yield* logs
|
|
326
328
|
}
|
|
329
|
+
return fetchAllMessagesInBatch(this, request, commit, opts_)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** {@inheritDoc Chain.typeAndVersion} */
|
|
333
|
+
async typeAndVersion(address: string) {
|
|
334
|
+
const contract = new Contract(
|
|
335
|
+
address,
|
|
336
|
+
VersionedContractABI,
|
|
337
|
+
this.provider,
|
|
338
|
+
) as unknown as TypedContract<typeof VersionedContractABI>
|
|
339
|
+
return parseTypeAndVersion(await contract.typeAndVersion())
|
|
327
340
|
}
|
|
328
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Decodes a CCIP message from a log event.
|
|
344
|
+
* @param log - Log event with topics and data.
|
|
345
|
+
* @returns Decoded CCIPMessage or undefined if not a valid CCIP message.
|
|
346
|
+
*/
|
|
329
347
|
static decodeMessage(log: {
|
|
330
348
|
topics?: readonly string[]
|
|
331
349
|
data: unknown
|
|
@@ -383,11 +401,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
383
401
|
...tokenAmount,
|
|
384
402
|
}
|
|
385
403
|
} catch (_) {
|
|
386
|
-
|
|
387
|
-
'legacy sourceTokenData:',
|
|
388
|
-
i,
|
|
389
|
-
(message as { sourceTokenData: string[] }).sourceTokenData[i],
|
|
390
|
-
)
|
|
404
|
+
// legacy sourceTokenData
|
|
391
405
|
}
|
|
392
406
|
}
|
|
393
407
|
if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
|
|
@@ -428,6 +442,12 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
428
442
|
return message as CCIPMessage
|
|
429
443
|
}
|
|
430
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Decodes commit reports from a log event.
|
|
447
|
+
* @param log - Log event with topics and data.
|
|
448
|
+
* @param lane - Lane info (required for CCIP v1.5 and earlier).
|
|
449
|
+
* @returns Array of CommitReport or undefined if not a valid commit event.
|
|
450
|
+
*/
|
|
431
451
|
static decodeCommits(
|
|
432
452
|
log: { topics?: readonly string[]; data: unknown },
|
|
433
453
|
lane?: Omit<Lane, 'destChainSelector'>,
|
|
@@ -478,6 +498,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
478
498
|
}
|
|
479
499
|
}
|
|
480
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Decodes an execution receipt from a log event.
|
|
503
|
+
* @param log - Log event with topics and data.
|
|
504
|
+
* @returns ExecutionReceipt or undefined if not a valid execution event.
|
|
505
|
+
*/
|
|
481
506
|
static decodeReceipt(log: {
|
|
482
507
|
topics?: readonly string[]
|
|
483
508
|
data: unknown
|
|
@@ -502,6 +527,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
502
527
|
}
|
|
503
528
|
}
|
|
504
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Decodes extra arguments from a CCIP message.
|
|
532
|
+
* @param extraArgs - Encoded extra arguments bytes.
|
|
533
|
+
* @returns Decoded extra arguments with tag, or undefined if unknown format.
|
|
534
|
+
*/
|
|
505
535
|
static decodeExtraArgs(
|
|
506
536
|
extraArgs: BytesLike,
|
|
507
537
|
):
|
|
@@ -542,6 +572,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
542
572
|
}
|
|
543
573
|
}
|
|
544
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Encodes extra arguments for a CCIP message.
|
|
577
|
+
* @param args - Extra arguments to encode.
|
|
578
|
+
* @returns Encoded extra arguments as hex string.
|
|
579
|
+
*/
|
|
545
580
|
static encodeExtraArgs(args: ExtraArgs): string {
|
|
546
581
|
if (!args) return '0x'
|
|
547
582
|
if ('computeUnits' in args) {
|
|
@@ -581,6 +616,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
581
616
|
return '0x'
|
|
582
617
|
}
|
|
583
618
|
|
|
619
|
+
/**
|
|
620
|
+
* Converts bytes to a checksummed EVM address.
|
|
621
|
+
* @param bytes - Bytes to convert (must be 20 bytes or 32 bytes with leading zeros).
|
|
622
|
+
* @returns Checksummed EVM address.
|
|
623
|
+
*/
|
|
584
624
|
static getAddress(bytes: BytesLike): string {
|
|
585
625
|
bytes = getBytes(bytes)
|
|
586
626
|
if (bytes.length < 20) throw new Error(`Invalid address: ${hexlify(bytes)}`)
|
|
@@ -594,15 +634,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
594
634
|
return getAddress(hexlify(bytes))
|
|
595
635
|
}
|
|
596
636
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
) as unknown as TypedContract<typeof VersionedContractABI>
|
|
603
|
-
return parseTypeAndVersion(await contract.typeAndVersion())
|
|
604
|
-
}
|
|
605
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Gets lane configuration from an OnRamp contract.
|
|
639
|
+
* @param onRamp - OnRamp contract address.
|
|
640
|
+
* @returns Lane configuration.
|
|
641
|
+
*/
|
|
606
642
|
async getLaneForOnRamp(onRamp: string): Promise<Lane> {
|
|
607
643
|
const [, version] = await this.typeAndVersion(onRamp)
|
|
608
644
|
const onRampABI = version === CCIPVersion.V1_2 ? EVM2EVMOnRamp_1_2_ABI : EVM2EVMOnRamp_1_5_ABI
|
|
@@ -623,6 +659,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
623
659
|
}
|
|
624
660
|
}
|
|
625
661
|
|
|
662
|
+
/** {@inheritDoc Chain.getRouterForOnRamp} */
|
|
626
663
|
async getRouterForOnRamp(onRamp: string, destChainSelector: bigint): Promise<string> {
|
|
627
664
|
const [, version] = await this.typeAndVersion(onRamp)
|
|
628
665
|
let onRampABI
|
|
@@ -651,6 +688,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
651
688
|
}
|
|
652
689
|
}
|
|
653
690
|
|
|
691
|
+
/** {@inheritDoc Chain.getRouterForOffRamp} */
|
|
654
692
|
async getRouterForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
655
693
|
const [, version] = await this.typeAndVersion(offRamp)
|
|
656
694
|
let offRampABI, router
|
|
@@ -684,6 +722,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
684
722
|
return router as string
|
|
685
723
|
}
|
|
686
724
|
|
|
725
|
+
/** {@inheritDoc Chain.getNativeTokenForRouter} */
|
|
687
726
|
async getNativeTokenForRouter(router: string): Promise<string> {
|
|
688
727
|
const contract = new Contract(
|
|
689
728
|
router,
|
|
@@ -693,6 +732,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
693
732
|
return contract.getWrappedNative() as Promise<string>
|
|
694
733
|
}
|
|
695
734
|
|
|
735
|
+
/** {@inheritDoc Chain.getOffRampsForRouter} */
|
|
696
736
|
async getOffRampsForRouter(router: string, sourceChainSelector: bigint): Promise<string[]> {
|
|
697
737
|
const contract = new Contract(
|
|
698
738
|
router,
|
|
@@ -705,6 +745,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
705
745
|
.map(({ offRamp }) => offRamp) as string[]
|
|
706
746
|
}
|
|
707
747
|
|
|
748
|
+
/** {@inheritDoc Chain.getOnRampForRouter} */
|
|
708
749
|
async getOnRampForRouter(router: string, destChainSelector: bigint): Promise<string> {
|
|
709
750
|
const contract = new Contract(
|
|
710
751
|
router,
|
|
@@ -714,6 +755,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
714
755
|
return contract.getOnRamp(destChainSelector) as Promise<string>
|
|
715
756
|
}
|
|
716
757
|
|
|
758
|
+
/** {@inheritDoc Chain.getOnRampForOffRamp} */
|
|
717
759
|
async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
718
760
|
const [, version] = await this.typeAndVersion(offRamp)
|
|
719
761
|
let offRampABI
|
|
@@ -746,6 +788,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
746
788
|
}
|
|
747
789
|
}
|
|
748
790
|
|
|
791
|
+
/** {@inheritDoc Chain.getCommitStoreForOffRamp} */
|
|
749
792
|
async getCommitStoreForOffRamp(offRamp: string): Promise<string> {
|
|
750
793
|
const [, version] = await this.typeAndVersion(offRamp)
|
|
751
794
|
let offRampABI
|
|
@@ -771,6 +814,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
771
814
|
}
|
|
772
815
|
}
|
|
773
816
|
|
|
817
|
+
/** {@inheritDoc Chain.getTokenForTokenPool} */
|
|
774
818
|
async getTokenForTokenPool(tokenPool: string): Promise<string> {
|
|
775
819
|
const contract = new Contract(
|
|
776
820
|
tokenPool,
|
|
@@ -780,6 +824,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
780
824
|
return contract.getToken() as Promise<string>
|
|
781
825
|
}
|
|
782
826
|
|
|
827
|
+
/** {@inheritDoc Chain.getTokenInfo} */
|
|
783
828
|
async getTokenInfo(token: string): Promise<{ decimals: number; symbol: string; name: string }> {
|
|
784
829
|
const contract = new Contract(
|
|
785
830
|
token,
|
|
@@ -794,12 +839,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
794
839
|
return { symbol, decimals: Number(decimals), name }
|
|
795
840
|
}
|
|
796
841
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
842
|
+
/**
|
|
843
|
+
* Gets the leaf hasher for computing Merkle proofs on the destination chain.
|
|
844
|
+
* @param lane - Lane configuration.
|
|
845
|
+
* @param ctx - Context object containing logger.
|
|
846
|
+
* @returns Leaf hasher function.
|
|
847
|
+
*/
|
|
848
|
+
static getDestLeafHasher(
|
|
849
|
+
{ sourceChainSelector, destChainSelector, onRamp, version }: Lane,
|
|
850
|
+
ctx?: WithLogger,
|
|
851
|
+
): LeafHasher {
|
|
803
852
|
switch (version) {
|
|
804
853
|
case CCIPVersion.V1_2:
|
|
805
854
|
case CCIPVersion.V1_5:
|
|
@@ -807,12 +856,17 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
807
856
|
throw new Error(`Unsupported source chain: ${sourceChainSelector}`)
|
|
808
857
|
return getV12LeafHasher(sourceChainSelector, destChainSelector, onRamp) as LeafHasher
|
|
809
858
|
case CCIPVersion.V1_6:
|
|
810
|
-
return getV16LeafHasher(sourceChainSelector, destChainSelector, onRamp) as LeafHasher
|
|
859
|
+
return getV16LeafHasher(sourceChainSelector, destChainSelector, onRamp, ctx) as LeafHasher
|
|
811
860
|
default:
|
|
812
861
|
throw new Error(`Unsupported hasher version for EVM: ${version as string}`)
|
|
813
862
|
}
|
|
814
863
|
}
|
|
815
864
|
|
|
865
|
+
/**
|
|
866
|
+
* Gets any available OnRamp for the given router.
|
|
867
|
+
* @param router - Router contract address.
|
|
868
|
+
* @returns OnRamp contract address.
|
|
869
|
+
*/
|
|
816
870
|
async _getSomeOnRampFor(router: string): Promise<string> {
|
|
817
871
|
// when given a router, we take any onRamp we can find, as usually they all use same registry
|
|
818
872
|
const someOtherNetwork = this.network.isTestnet
|
|
@@ -825,6 +879,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
825
879
|
return this.getOnRampForRouter(router, networkInfo(someOtherNetwork).chainSelector)
|
|
826
880
|
}
|
|
827
881
|
|
|
882
|
+
/** {@inheritDoc Chain.getTokenAdminRegistryFor} */
|
|
828
883
|
async getTokenAdminRegistryFor(address: string): Promise<string> {
|
|
829
884
|
let [type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
830
885
|
if (type === 'TokenAdminRegistry') {
|
|
@@ -850,6 +905,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
850
905
|
return tokenAdminRegistry as string
|
|
851
906
|
}
|
|
852
907
|
|
|
908
|
+
/**
|
|
909
|
+
* Gets the FeeQuoter contract address for a given Router or Ramp.
|
|
910
|
+
* @param address - Router or Ramp contract address.
|
|
911
|
+
* @returns FeeQuoter contract address.
|
|
912
|
+
*/
|
|
853
913
|
async getFeeQuoterFor(address: string): Promise<string> {
|
|
854
914
|
let [type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
855
915
|
if (type === 'FeeQuoter') {
|
|
@@ -872,6 +932,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
872
932
|
return feeQuoter as string
|
|
873
933
|
}
|
|
874
934
|
|
|
935
|
+
/** {@inheritDoc Chain.getFee} */
|
|
875
936
|
async getFee(router_: string, destChainSelector: bigint, message: AnyMessage): Promise<bigint> {
|
|
876
937
|
const router = new Contract(
|
|
877
938
|
router_,
|
|
@@ -887,12 +948,24 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
887
948
|
})
|
|
888
949
|
}
|
|
889
950
|
|
|
890
|
-
|
|
891
|
-
|
|
951
|
+
/**
|
|
952
|
+
* Generate unsigned txs for ccipSend'ing a message
|
|
953
|
+
* @param sender - sender address
|
|
954
|
+
* @param router - address of the Router contract
|
|
955
|
+
* @param destChainSelector - chainSelector of destination chain
|
|
956
|
+
* @param message - AnyMessage to send; if `fee` is not present, it'll be calculated
|
|
957
|
+
* @param approveMax - if tokens approvals are needed, opt into approving maximum allowance
|
|
958
|
+
* @returns Array containing 0 or more unsigned token approvals txs (if needed at the time of
|
|
959
|
+
* generation), followed by a ccipSend TransactionRequest
|
|
960
|
+
*/
|
|
961
|
+
async generateUnsignedSendMessage(
|
|
962
|
+
sender: string,
|
|
963
|
+
router: string,
|
|
892
964
|
destChainSelector: bigint,
|
|
893
|
-
message: AnyMessage & { fee
|
|
894
|
-
opts?: {
|
|
895
|
-
): Promise<
|
|
965
|
+
message: AnyMessage & { fee?: bigint },
|
|
966
|
+
opts?: { approveMax?: boolean },
|
|
967
|
+
): Promise<UnsignedEVMTx> {
|
|
968
|
+
if (!message.fee) message.fee = await this.getFee(router, destChainSelector, message)
|
|
896
969
|
const feeToken = message.feeToken ?? ZeroAddress
|
|
897
970
|
const receiver = zeroPadValue(getAddressBytes(message.receiver), 32)
|
|
898
971
|
const data = hexlify(message.data)
|
|
@@ -908,34 +981,28 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
908
981
|
if (feeToken !== ZeroAddress)
|
|
909
982
|
amountsToApprove[feeToken] = (amountsToApprove[feeToken] ?? 0n) + message.fee
|
|
910
983
|
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
typeof Token_ABI
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
if (allowance < amount) {
|
|
984
|
+
const approveTxs = (
|
|
985
|
+
await Promise.all(
|
|
986
|
+
Object.entries(amountsToApprove).map(async ([token, amount]) => {
|
|
987
|
+
const contract = new Contract(
|
|
988
|
+
token,
|
|
989
|
+
interfaces.Token,
|
|
990
|
+
this.provider,
|
|
991
|
+
) as unknown as TypedContract<typeof Token_ABI>
|
|
992
|
+
const allowance = await contract.allowance(sender, router)
|
|
993
|
+
if (allowance >= amount) return
|
|
922
994
|
const amnt = opts?.approveMax ? 2n ** 256n - 1n : amount
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
gasLimit: DEFAULT_APPROVE_GAS_LIMIT,
|
|
928
|
-
})
|
|
929
|
-
console.info('=>', tx.hash)
|
|
930
|
-
await tx.wait(1, 60_000)
|
|
931
|
-
}
|
|
932
|
-
}),
|
|
933
|
-
)
|
|
995
|
+
return contract.approve.populateTransaction(router, amnt, { from: sender })
|
|
996
|
+
}),
|
|
997
|
+
)
|
|
998
|
+
).filter((tx) => tx != null)
|
|
934
999
|
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1000
|
+
const contract = new Contract(
|
|
1001
|
+
router,
|
|
1002
|
+
interfaces.Router,
|
|
1003
|
+
this.provider,
|
|
1004
|
+
) as unknown as TypedContract<typeof Router_ABI>
|
|
1005
|
+
const sendTx = await contract.ccipSend.populateTransaction(
|
|
939
1006
|
destChainSelector,
|
|
940
1007
|
{
|
|
941
1008
|
receiver,
|
|
@@ -945,26 +1012,85 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
945
1012
|
feeToken,
|
|
946
1013
|
},
|
|
947
1014
|
{
|
|
948
|
-
|
|
1015
|
+
from: sender,
|
|
949
1016
|
// if native fee, include it in value; otherwise, it's transferedFrom feeToken
|
|
950
|
-
...(feeToken === ZeroAddress
|
|
1017
|
+
...(feeToken === ZeroAddress && { value: message.fee }),
|
|
951
1018
|
},
|
|
952
1019
|
)
|
|
953
|
-
const
|
|
954
|
-
return
|
|
1020
|
+
const txRequests = [...approveTxs, sendTx] as SetRequired<typeof sendTx, 'from'>[]
|
|
1021
|
+
return {
|
|
1022
|
+
family: ChainFamily.EVM,
|
|
1023
|
+
transactions: txRequests,
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/** {@inheritDoc Chain.sendMessage} */
|
|
1028
|
+
async sendMessage(
|
|
1029
|
+
router_: string,
|
|
1030
|
+
destChainSelector: bigint,
|
|
1031
|
+
message: AnyMessage & { fee?: bigint },
|
|
1032
|
+
opts: { wallet: unknown; approveMax?: boolean },
|
|
1033
|
+
): Promise<CCIPRequest> {
|
|
1034
|
+
const wallet = opts.wallet
|
|
1035
|
+
if (!isSigner(wallet)) throw new Error(`Wallet must be a Signer, got=${util.inspect(wallet)}`)
|
|
1036
|
+
|
|
1037
|
+
const sender = await wallet.getAddress()
|
|
1038
|
+
const txs = await this.generateUnsignedSendMessage(
|
|
1039
|
+
sender,
|
|
1040
|
+
router_,
|
|
1041
|
+
destChainSelector,
|
|
1042
|
+
message,
|
|
1043
|
+
opts,
|
|
1044
|
+
)
|
|
1045
|
+
const approveTxs = txs.transactions.slice(0, txs.transactions.length - 1)
|
|
1046
|
+
let sendTx: TransactionRequest = txs.transactions[txs.transactions.length - 1]
|
|
1047
|
+
|
|
1048
|
+
// approve all tokens (including feeToken, if needed) in parallel
|
|
1049
|
+
let nonce = await this.provider.getTransactionCount(sender)
|
|
1050
|
+
const responses = await Promise.all(
|
|
1051
|
+
approveTxs.map(async (tx: TransactionRequest) => {
|
|
1052
|
+
tx.nonce = nonce++
|
|
1053
|
+
tx = await wallet.populateTransaction(tx)
|
|
1054
|
+
tx.from = undefined
|
|
1055
|
+
const signed = await wallet.signTransaction(tx)
|
|
1056
|
+
const response = await this.provider.broadcastTransaction(signed)
|
|
1057
|
+
this.logger.debug('approve =>', response.hash)
|
|
1058
|
+
return response
|
|
1059
|
+
}),
|
|
1060
|
+
)
|
|
1061
|
+
if (responses.length) await responses[responses.length - 1].wait(1, 60_000) // wait last tx nonce to be mined
|
|
1062
|
+
|
|
1063
|
+
sendTx.nonce = nonce++
|
|
1064
|
+
// sendTx.gasLimit = await this.provider.estimateGas(sendTx)
|
|
1065
|
+
sendTx = await wallet.populateTransaction(sendTx)
|
|
1066
|
+
sendTx.from = undefined // some signers don't like receiving pre-populated `from`
|
|
1067
|
+
const signed = await wallet.signTransaction(sendTx)
|
|
1068
|
+
const response = await this.provider.broadcastTransaction(signed)
|
|
1069
|
+
this.logger.debug('ccipSend =>', response.hash)
|
|
1070
|
+
await response.wait(1, 60_000)
|
|
1071
|
+
return (await this.fetchRequestsInTx(await this.getTransaction(response.hash)))[0]
|
|
955
1072
|
}
|
|
956
1073
|
|
|
1074
|
+
/** {@inheritDoc Chain.fetchOffchainTokenData} */
|
|
957
1075
|
fetchOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
|
|
958
|
-
return fetchEVMOffchainTokenData(request)
|
|
1076
|
+
return fetchEVMOffchainTokenData(request, this)
|
|
959
1077
|
}
|
|
960
1078
|
|
|
961
|
-
|
|
1079
|
+
/**
|
|
1080
|
+
* Generate unsigned tx to manuallyExecute a message
|
|
1081
|
+
* @param _payer - not used in EVM
|
|
1082
|
+
* @param offRamp - address of the OffRamp contract
|
|
1083
|
+
* @param execReport - execution report
|
|
1084
|
+
* @param opts - gas limit overrides for ccipReceive and tokenPool calls options
|
|
1085
|
+
* @returns array containing one unsigned `manuallyExecute` TransactionRequest object
|
|
1086
|
+
*/
|
|
1087
|
+
async generateUnsignedExecuteReport(
|
|
1088
|
+
_payer: string,
|
|
962
1089
|
offRamp: string,
|
|
963
1090
|
execReport: ExecutionReport,
|
|
964
|
-
opts
|
|
965
|
-
) {
|
|
1091
|
+
opts: { gasLimit?: number; tokensGasLimit?: number },
|
|
1092
|
+
): Promise<UnsignedEVMTx> {
|
|
966
1093
|
const [_, version] = await this.typeAndVersion(offRamp)
|
|
967
|
-
const wallet = await this.getWallet(opts)
|
|
968
1094
|
|
|
969
1095
|
let manualExecTx
|
|
970
1096
|
const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData)
|
|
@@ -974,10 +1100,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
974
1100
|
const contract = new Contract(
|
|
975
1101
|
offRamp,
|
|
976
1102
|
EVM2EVMOffRamp_1_2_ABI,
|
|
977
|
-
|
|
1103
|
+
this.provider,
|
|
978
1104
|
) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
|
|
979
1105
|
const gasOverride = BigInt(opts?.gasLimit ?? 0)
|
|
980
|
-
manualExecTx = await contract.manuallyExecute(
|
|
1106
|
+
manualExecTx = await contract.manuallyExecute.populateTransaction(
|
|
981
1107
|
{
|
|
982
1108
|
...execReport,
|
|
983
1109
|
proofs: execReport.proofs.map((d) => hexlify(d)),
|
|
@@ -992,9 +1118,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
992
1118
|
const contract = new Contract(
|
|
993
1119
|
offRamp,
|
|
994
1120
|
EVM2EVMOffRamp_1_5_ABI,
|
|
995
|
-
|
|
1121
|
+
this.provider,
|
|
996
1122
|
) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_5_ABI>
|
|
997
|
-
manualExecTx = await contract.manuallyExecute(
|
|
1123
|
+
manualExecTx = await contract.manuallyExecute.populateTransaction(
|
|
998
1124
|
{
|
|
999
1125
|
...execReport,
|
|
1000
1126
|
proofs: execReport.proofs.map((d) => hexlify(d)),
|
|
@@ -1027,10 +1153,12 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1027
1153
|
sender,
|
|
1028
1154
|
tokenAmounts,
|
|
1029
1155
|
}
|
|
1030
|
-
const contract = new Contract(
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1156
|
+
const contract = new Contract(
|
|
1157
|
+
offRamp,
|
|
1158
|
+
OffRamp_1_6_ABI,
|
|
1159
|
+
this.provider,
|
|
1160
|
+
) as unknown as TypedContract<typeof OffRamp_1_6_ABI>
|
|
1161
|
+
manualExecTx = await contract.manuallyExecute.populateTransaction(
|
|
1034
1162
|
[
|
|
1035
1163
|
{
|
|
1036
1164
|
...execReport,
|
|
@@ -1056,20 +1184,48 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1056
1184
|
default:
|
|
1057
1185
|
throw new Error(`Unsupported version: ${version}`)
|
|
1058
1186
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1187
|
+
return { family: ChainFamily.EVM, transactions: [manualExecTx] }
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/** {@inheritDoc Chain.executeReport} */
|
|
1191
|
+
async executeReport(
|
|
1192
|
+
offRamp: string,
|
|
1193
|
+
execReport: ExecutionReport,
|
|
1194
|
+
opts: { wallet: unknown; gasLimit?: number; tokensGasLimit?: number },
|
|
1195
|
+
) {
|
|
1196
|
+
const wallet = opts.wallet
|
|
1197
|
+
if (!isSigner(wallet)) throw new Error(`Wallet must be a Signer, got=${util.inspect(wallet)}`)
|
|
1198
|
+
|
|
1199
|
+
const unsignedTxs = await this.generateUnsignedExecuteReport(
|
|
1200
|
+
await wallet.getAddress(),
|
|
1201
|
+
offRamp,
|
|
1202
|
+
execReport,
|
|
1203
|
+
opts,
|
|
1204
|
+
)
|
|
1205
|
+
const unsignedTx = await wallet.populateTransaction(unsignedTxs.transactions[0])
|
|
1206
|
+
unsignedTx.from = undefined // some signers don't like receiving pre-populated `from`
|
|
1207
|
+
const signed = await wallet.signTransaction(unsignedTx)
|
|
1208
|
+
const response = await this.provider.broadcastTransaction(signed)
|
|
1209
|
+
this.logger.debug('ccipSend =>', response.hash)
|
|
1210
|
+
const receipt = await response.wait(1, 60_000)
|
|
1211
|
+
if (!receipt?.hash) throw new Error(`Could not confirm exec tx: ${response.hash}`)
|
|
1212
|
+
if (!receipt.status) throw new Error(`Exec transaction reverted: ${response.hash}`)
|
|
1062
1213
|
return this.getTransaction(receipt)
|
|
1063
1214
|
}
|
|
1064
1215
|
|
|
1216
|
+
/**
|
|
1217
|
+
* Parses raw data into typed structures.
|
|
1218
|
+
* @param data - Raw data to parse.
|
|
1219
|
+
* @returns Parsed data.
|
|
1220
|
+
*/
|
|
1065
1221
|
static parse(data: unknown) {
|
|
1066
1222
|
return parseData(data)
|
|
1067
1223
|
}
|
|
1068
1224
|
|
|
1069
1225
|
/**
|
|
1070
|
-
* Get the supported tokens for a given contract address
|
|
1071
|
-
*
|
|
1072
|
-
* @param
|
|
1226
|
+
* Get the supported tokens for a given contract address.
|
|
1227
|
+
* @param registry - Router, OnRamp, OffRamp or TokenAdminRegistry contract address.
|
|
1228
|
+
* @param opts - Optional parameters.
|
|
1073
1229
|
* @returns An array of supported token addresses.
|
|
1074
1230
|
*/
|
|
1075
1231
|
async getSupportedTokens(registry: string, opts?: { page?: number }): Promise<string[]> {
|
|
@@ -1089,6 +1245,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1089
1245
|
return res as string[]
|
|
1090
1246
|
}
|
|
1091
1247
|
|
|
1248
|
+
/** {@inheritDoc Chain.getRegistryTokenConfig} */
|
|
1092
1249
|
async getRegistryTokenConfig(
|
|
1093
1250
|
registry: string,
|
|
1094
1251
|
token: string,
|
|
@@ -1117,6 +1274,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1117
1274
|
}
|
|
1118
1275
|
}
|
|
1119
1276
|
|
|
1277
|
+
/** {@inheritDoc Chain.getTokenPoolConfigs} */
|
|
1120
1278
|
async getTokenPoolConfigs(tokenPool: string): Promise<{
|
|
1121
1279
|
token: string
|
|
1122
1280
|
router: string
|
|
@@ -1141,6 +1299,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1141
1299
|
})
|
|
1142
1300
|
}
|
|
1143
1301
|
|
|
1302
|
+
/** {@inheritDoc Chain.getTokenPoolRemotes} */
|
|
1144
1303
|
async getTokenPoolRemotes(
|
|
1145
1304
|
tokenPool: string,
|
|
1146
1305
|
remoteChainSelector?: bigint,
|
|
@@ -1216,24 +1375,36 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1216
1375
|
),
|
|
1217
1376
|
)
|
|
1218
1377
|
}
|
|
1219
|
-
|
|
1378
|
+
|
|
1379
|
+
/** {@inheritDoc Chain.getFeeTokens} */
|
|
1380
|
+
async getFeeTokens(router: string) {
|
|
1220
1381
|
const onRamp = await this._getSomeOnRampFor(router)
|
|
1221
1382
|
const [_, version] = await this.typeAndVersion(onRamp)
|
|
1222
1383
|
let tokens
|
|
1223
|
-
let
|
|
1384
|
+
let onRampIface: Interface
|
|
1224
1385
|
switch (version) {
|
|
1225
1386
|
case CCIPVersion.V1_2:
|
|
1226
|
-
|
|
1387
|
+
onRampIface = interfaces.EVM2EVMOnRamp_v1_2
|
|
1227
1388
|
// falls through
|
|
1228
1389
|
case CCIPVersion.V1_5: {
|
|
1229
|
-
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1390
|
+
onRampIface ??= interfaces.EVM2EVMOnRamp_v1_5
|
|
1391
|
+
const fragment = onRampIface.getEvent('FeeConfigSet')!
|
|
1392
|
+
const tokens_ = new Set()
|
|
1393
|
+
for await (const log of this.getLogs({
|
|
1394
|
+
address: onRamp,
|
|
1395
|
+
topics: [fragment.topicHash],
|
|
1396
|
+
startBlock: 1,
|
|
1397
|
+
onlyFallback: true,
|
|
1398
|
+
})) {
|
|
1399
|
+
;(
|
|
1400
|
+
onRampIface.decodeEventLog(fragment, log.data, log.topics) as unknown as {
|
|
1401
|
+
feeConfig: { token: string; enabled: boolean }[]
|
|
1402
|
+
}
|
|
1403
|
+
).feeConfig.forEach(({ token, enabled }) =>
|
|
1404
|
+
enabled ? tokens_.add(token) : tokens_.delete(token),
|
|
1405
|
+
)
|
|
1406
|
+
}
|
|
1407
|
+
tokens = Array.from(tokens_)
|
|
1237
1408
|
break
|
|
1238
1409
|
}
|
|
1239
1410
|
case CCIPVersion.V1_6: {
|
|
@@ -1257,6 +1428,37 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1257
1428
|
),
|
|
1258
1429
|
)
|
|
1259
1430
|
}
|
|
1260
|
-
}
|
|
1261
1431
|
|
|
1262
|
-
|
|
1432
|
+
/** {@inheritDoc Chain.fetchExecutionReceipts} */
|
|
1433
|
+
override async *fetchExecutionReceipts(
|
|
1434
|
+
offRamp: string,
|
|
1435
|
+
request: PickDeep<CCIPRequest, 'lane' | 'message.header.messageId' | 'tx.timestamp'>,
|
|
1436
|
+
commit?: CCIPCommit,
|
|
1437
|
+
opts?: { page?: number },
|
|
1438
|
+
): AsyncIterableIterator<CCIPExecution> {
|
|
1439
|
+
let opts_: Parameters<EVMChain['getLogs']>[0] | undefined = opts
|
|
1440
|
+
if (request.lane.version < CCIPVersion.V1_6) {
|
|
1441
|
+
opts_ = {
|
|
1442
|
+
...opts,
|
|
1443
|
+
topics: [
|
|
1444
|
+
interfaces.EVM2EVMOffRamp_v1_5.getEvent('ExecutionStateChanged')!.topicHash,
|
|
1445
|
+
null,
|
|
1446
|
+
request.message.header.messageId,
|
|
1447
|
+
],
|
|
1448
|
+
// onlyFallback: false,
|
|
1449
|
+
}
|
|
1450
|
+
} else /* >= V1.6 */ {
|
|
1451
|
+
opts_ = {
|
|
1452
|
+
...opts,
|
|
1453
|
+
topics: [
|
|
1454
|
+
interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged')!.topicHash,
|
|
1455
|
+
toBeHex(request.lane.sourceChainSelector, 32),
|
|
1456
|
+
null,
|
|
1457
|
+
request.message.header.messageId,
|
|
1458
|
+
],
|
|
1459
|
+
// onlyFallback: false,
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
yield* super.fetchExecutionReceipts(offRamp, request, commit, opts_)
|
|
1463
|
+
}
|
|
1464
|
+
}
|