@chainlink/ccip-sdk 1.1.1 → 1.2.1

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.
Files changed (100) hide show
  1. package/dist/api/index.d.ts +165 -15
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +236 -61
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/types.d.ts +119 -1
  6. package/dist/api/types.d.ts.map +1 -1
  7. package/dist/chain.d.ts +53 -27
  8. package/dist/chain.d.ts.map +1 -1
  9. package/dist/chain.js +72 -17
  10. package/dist/chain.js.map +1 -1
  11. package/dist/errors/codes.d.ts +1 -0
  12. package/dist/errors/codes.d.ts.map +1 -1
  13. package/dist/errors/codes.js +1 -0
  14. package/dist/errors/codes.js.map +1 -1
  15. package/dist/errors/index.d.ts +1 -1
  16. package/dist/errors/index.d.ts.map +1 -1
  17. package/dist/errors/index.js +1 -1
  18. package/dist/errors/index.js.map +1 -1
  19. package/dist/errors/recovery.d.ts.map +1 -1
  20. package/dist/errors/recovery.js +1 -0
  21. package/dist/errors/recovery.js.map +1 -1
  22. package/dist/errors/specialized.d.ts +21 -0
  23. package/dist/errors/specialized.d.ts.map +1 -1
  24. package/dist/errors/specialized.js +31 -1
  25. package/dist/errors/specialized.js.map +1 -1
  26. package/dist/evm/abi/OffRamp_2_0.d.ts +18 -17
  27. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -1
  28. package/dist/evm/abi/OffRamp_2_0.js +19 -21
  29. package/dist/evm/abi/OffRamp_2_0.js.map +1 -1
  30. package/dist/evm/abi/TokenPool_2_0.d.ts +0 -4
  31. package/dist/evm/abi/TokenPool_2_0.d.ts.map +1 -1
  32. package/dist/evm/abi/TokenPool_2_0.js +0 -1
  33. package/dist/evm/abi/TokenPool_2_0.js.map +1 -1
  34. package/dist/evm/gas.d.ts +14 -4
  35. package/dist/evm/gas.d.ts.map +1 -1
  36. package/dist/evm/gas.js +7 -6
  37. package/dist/evm/gas.js.map +1 -1
  38. package/dist/evm/index.d.ts +39 -8
  39. package/dist/evm/index.d.ts.map +1 -1
  40. package/dist/evm/index.js +114 -28
  41. package/dist/evm/index.js.map +1 -1
  42. package/dist/evm/types.d.ts +1 -1
  43. package/dist/evm/types.d.ts.map +1 -1
  44. package/dist/extra-args.d.ts +18 -8
  45. package/dist/extra-args.d.ts.map +1 -1
  46. package/dist/extra-args.js +6 -6
  47. package/dist/extra-args.js.map +1 -1
  48. package/dist/gas.d.ts +1 -1
  49. package/dist/gas.d.ts.map +1 -1
  50. package/dist/gas.js +7 -2
  51. package/dist/gas.js.map +1 -1
  52. package/dist/index.d.ts +2 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js.map +1 -1
  55. package/dist/offchain.d.ts.map +1 -1
  56. package/dist/offchain.js +6 -6
  57. package/dist/offchain.js.map +1 -1
  58. package/dist/requests.d.ts +11 -5
  59. package/dist/requests.d.ts.map +1 -1
  60. package/dist/requests.js +6 -7
  61. package/dist/requests.js.map +1 -1
  62. package/dist/solana/index.d.ts +2 -2
  63. package/dist/solana/index.d.ts.map +1 -1
  64. package/dist/solana/index.js +1 -1
  65. package/dist/solana/index.js.map +1 -1
  66. package/dist/solana/utils.js +2 -2
  67. package/dist/solana/utils.js.map +1 -1
  68. package/dist/sui/index.d.ts.map +1 -1
  69. package/dist/sui/index.js +1 -1
  70. package/dist/sui/index.js.map +1 -1
  71. package/dist/ton/index.d.ts.map +1 -1
  72. package/dist/ton/index.js +34 -26
  73. package/dist/ton/index.js.map +1 -1
  74. package/dist/utils.d.ts +10 -4
  75. package/dist/utils.d.ts.map +1 -1
  76. package/dist/utils.js +10 -4
  77. package/dist/utils.js.map +1 -1
  78. package/package.json +9 -9
  79. package/src/api/index.ts +271 -59
  80. package/src/api/types.ts +126 -1
  81. package/src/chain.ts +121 -43
  82. package/src/errors/codes.ts +1 -0
  83. package/src/errors/index.ts +1 -0
  84. package/src/errors/recovery.ts +2 -0
  85. package/src/errors/specialized.ts +33 -1
  86. package/src/evm/abi/OffRamp_2_0.ts +19 -21
  87. package/src/evm/abi/TokenPool_2_0.ts +0 -1
  88. package/src/evm/gas.ts +18 -20
  89. package/src/evm/index.ts +141 -28
  90. package/src/evm/types.ts +1 -1
  91. package/src/extra-args.ts +18 -8
  92. package/src/gas.ts +8 -3
  93. package/src/index.ts +4 -0
  94. package/src/offchain.ts +6 -8
  95. package/src/requests.ts +19 -12
  96. package/src/solana/index.ts +3 -1
  97. package/src/solana/utils.ts +2 -2
  98. package/src/sui/index.ts +3 -1
  99. package/src/ton/index.ts +47 -26
  100. package/src/utils.ts +10 -4
package/src/chain.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type BytesLike, dataLength } from 'ethers'
1
+ import { type BytesLike, dataLength, keccak256 } from 'ethers'
2
2
  import type { PickDeep, SetOptional } from 'type-fest'
3
3
 
4
4
  import { type LaneLatencyResponse, CCIPAPIClient } from './api/index.ts'
@@ -24,6 +24,7 @@ import type {
24
24
  SuiExtraArgsV1,
25
25
  } from './extra-args.ts'
26
26
  import type { LeafHasher } from './hasher/common.ts'
27
+ import { decodeMessageV1 } from './messages.ts'
27
28
  import { getOffchainTokenData } from './offchain.ts'
28
29
  import { getMessagesInTx } from './requests.ts'
29
30
  import { DEFAULT_GAS_LIMIT } from './shared/constants.ts'
@@ -68,13 +69,13 @@ const V3_FIELDS = new Set([
68
69
  'tokenArgs',
69
70
  ])
70
71
 
71
- /** Throw if any key in extraArgs is not in the allowed set. */
72
+ /** Throw {@link CCIPArgumentInvalidError} if any key in extraArgs is not in the allowed set. */
72
73
  function assertNoUnknownFields(
73
74
  extraArgs: Partial<ExtraArgs>,
74
75
  allowed: Set<string>,
75
76
  variant: string,
76
77
  ): void {
77
- const unknown = Object.keys(extraArgs).filter((k) => !allowed.has(k))
78
+ const unknown = Object.keys(extraArgs).filter((k) => k !== '_tag' && !allowed.has(k))
78
79
  if (unknown.length)
79
80
  throw new CCIPArgumentInvalidError(
80
81
  'extraArgs',
@@ -201,7 +202,7 @@ export type TokenInfo = {
201
202
  */
202
203
  export const LaneFeature = {
203
204
  /**
204
- * Minimum block confirmations for Faster Time to Finality (FTF).
205
+ * Minimum block confirmations for Faster-Than-Finality (FTF).
205
206
  * - **absent**: the lane does not support FTF (pre-v2.0 lane).
206
207
  * - **0**: the lane supports FTF, but it is not enabled for this
207
208
  * token (e.g. the token pool predates FTF, or FTF is configured
@@ -285,6 +286,11 @@ export type RateLimiterState = {
285
286
  * @remarks
286
287
  * Each entry represents the configuration needed to transfer tokens
287
288
  * from the current chain to a specific destination chain.
289
+ *
290
+ * The `customBlockConfirmationsOutboundRateLimiterState` and
291
+ * `customBlockConfirmationsInboundRateLimiterState` fields are present only for
292
+ * TokenPool v2.0+ contracts. These provide separate rate limits applied when
293
+ * Faster-Than-Finality (FTF) custom block confirmations are used.
288
294
  */
289
295
  export type TokenPoolRemote = {
290
296
  /** Address of the remote token on the destination chain. */
@@ -333,7 +339,7 @@ export type TokenPoolConfig = {
333
339
  */
334
340
  typeAndVersion?: string
335
341
  /**
336
- * Min custom block confirmations for Faster Time to Finality (FTF),
342
+ * Min custom block confirmations for Faster-Than-Finality (FTF),
337
343
  * if TokenPool version \>= v2.0.0 and FTF is supported on this lane.
338
344
  * `0` indicates FTF is supported but not enabled for this token; `>0` indicates FTF is enabled
339
345
  * with this many minimum confirmations.
@@ -396,7 +402,7 @@ export type ExecuteOpts = (
396
402
  | {
397
403
  /**
398
404
  * messageId of message to execute; requires `apiClient`.
399
- * @remarks Currently throws CCIPNotImplementedError - API endpoint pending.
405
+ * The SDK will fetch execution inputs (offRamp, proofs/verifications) from the CCIP API.
400
406
  */
401
407
  messageId: string
402
408
  }
@@ -1019,16 +1025,62 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1019
1025
  * When `opts` already contains `input` the method is a no-op and returns it unchanged.
1020
1026
  * When `opts` contains only a `messageId` it calls `apiClient.getExecutionInput` and merges
1021
1027
  * the result back with any extra opts fields (e.g. `gasLimit`).
1028
+ * If opts.gasLimit is undefined and `estimateReceiveExecution` is available, try to estimate gasLimitOverride
1022
1029
  *
1023
1030
  * @throws {@link CCIPApiClientNotAvailableError} if `messageId` is provided but no apiClient
1024
1031
  */
1025
1032
  protected async resolveExecuteOpts(
1026
1033
  opts: ExecuteOpts,
1027
- ): Promise<Omit<ExecuteOpts, 'messageId'> & { offRamp: string; input: ExecutionInput }> {
1028
- if ('input' in opts) return opts
1029
- if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
1030
- const { offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId)
1031
- return { ...opts, offRamp, input }
1034
+ ): Promise<Extract<ExecuteOpts, { input: unknown }>> {
1035
+ let opts_: Extract<typeof opts, { input: unknown }>
1036
+ if ('input' in opts) {
1037
+ opts_ = opts
1038
+ } else if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
1039
+ else {
1040
+ const { offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId)
1041
+ opts_ = { ...opts, offRamp, input }
1042
+ }
1043
+
1044
+ if (
1045
+ opts_.gasLimit == null &&
1046
+ this.estimateReceiveExecution &&
1047
+ (!('message' in opts_.input) ||
1048
+ !opts_.input.message.tokenAmounts.length ||
1049
+ opts_.input.message.tokenAmounts.every((ta) => 'destTokenAddress' in ta))
1050
+ ) {
1051
+ let message
1052
+ if ('message' in opts_.input) {
1053
+ message = {
1054
+ ...opts_.input.message,
1055
+ // pass `tokenAmount` with `destTokenAddress` to estimate
1056
+ destTokenAmounts: opts_.input.message.tokenAmounts,
1057
+ }
1058
+ } else {
1059
+ const decoded = decodeMessageV1(opts_.input.encodedMessage)
1060
+ message = {
1061
+ ...decoded,
1062
+ messageId: keccak256(opts_.input.encodedMessage),
1063
+ destTokenAmounts: decoded.tokenTransfer,
1064
+ }
1065
+ }
1066
+ try {
1067
+ const estimated = await this.estimateReceiveExecution({
1068
+ offRamp: opts_.offRamp,
1069
+ message,
1070
+ })
1071
+ this.logger.debug('Estimated receiver execution:', estimated)
1072
+ if (
1073
+ ('gasLimit' in message && estimated > message.gasLimit) ||
1074
+ ('ccipReceiveGasLimit' in message && estimated > message.ccipReceiveGasLimit)
1075
+ )
1076
+ opts_.gasLimit = estimated
1077
+ } catch (err) {
1078
+ // ignore if receiver fails, let estimation of execute method itself throw if needed
1079
+ this.logger.debug('Failed to auto-estimateReceiveExecution for:', opts, err)
1080
+ }
1081
+ }
1082
+
1083
+ return opts_
1032
1084
  }
1033
1085
 
1034
1086
  /**
@@ -1056,21 +1108,24 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1056
1108
  /**
1057
1109
  * Execute messages in report in an offRamp.
1058
1110
  *
1059
- * @param opts - {@link ExecuteOpts} with chain-specific wallet to sign and send tx
1060
- * @returns Promise resolving to transaction of the execution
1111
+ * @param opts - {@link ExecuteOpts} with chain-specific wallet to sign and send tx.
1112
+ * @returns Promise resolving to transaction of the execution.
1061
1113
  *
1062
- * @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer
1063
- * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
1064
- * @throws {@link CCIPMerkleRootMismatchError} if merkle proof is invalid
1114
+ * @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer.
1115
+ * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm.
1116
+ * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts.
1117
+ * @throws {@link CCIPMerkleRootMismatchError} if merkle proof is invalid.
1118
+ *
1119
+ * @example Manual execution using message ID (simplified, requires API)
1120
+ * ```typescript
1121
+ * const receipt = await dest.execute({ messageId: '0x...', wallet })
1122
+ * ```
1065
1123
  *
1066
- * @example Manual execution of pending message
1124
+ * @example Manual execution using transaction hash
1067
1125
  * ```typescript
1068
1126
  * const input = await source.getExecutionInput({ request, verifications })
1069
1127
  * const receipt = await dest.execute({ offRamp, input, wallet })
1070
- * console.log(`Executed: ${receipt.log.transactionHash}`)
1071
1128
  * ```
1072
- * @throws {@link CCIPWalletNotSignerError} if wallet cannot sign transactions
1073
- * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
1074
1129
  */
1075
1130
  abstract execute(
1076
1131
  opts: ExecuteOpts & {
@@ -1118,10 +1173,10 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1118
1173
  * Uses this chain's selector as the source.
1119
1174
  *
1120
1175
  * @param destChainSelector - Destination CCIP chain selector (bigint)
1176
+ * @param numberOfBlocks - Optional number of block confirmations to use for latency
1177
+ * calculation. When omitted or 0, uses the lane's default finality. When provided
1178
+ * as a positive integer, the API returns latency for that custom finality value.
1121
1179
  * @returns Promise resolving to {@link LaneLatencyResponse} containing:
1122
- * - `lane.sourceNetworkInfo` - Source chain metadata (name, selector, chainId)
1123
- * - `lane.destNetworkInfo` - Destination chain metadata
1124
- * - `lane.routerAddress` - Router contract address on source chain
1125
1180
  * - `totalMs` - Estimated delivery time in milliseconds
1126
1181
  *
1127
1182
  * @throws {@link CCIPApiClientNotAvailableError} if apiClient was disabled (set to `null`)
@@ -1137,19 +1192,31 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1137
1192
  * try {
1138
1193
  * const latency = await chain.getLaneLatency(4949039107694359620n) // Arbitrum
1139
1194
  * console.log(`Estimated delivery: ${Math.round(latency.totalMs / 60000)} minutes`)
1140
- * console.log(`Router: ${latency.lane.routerAddress}`)
1141
1195
  * } catch (err) {
1142
1196
  * if (err instanceof CCIPHttpError) {
1143
1197
  * console.error(`API error: ${err.context.apiErrorCode}`)
1144
1198
  * }
1145
1199
  * }
1146
1200
  * ```
1201
+ *
1202
+ * @example Get latency with custom block confirmations
1203
+ * ```typescript
1204
+ * const latency = await chain.getLaneLatency(4949039107694359620n, 10)
1205
+ * console.log(`Latency with 10 confirmations: ${Math.round(latency.totalMs / 60000)} minutes`)
1206
+ * ```
1147
1207
  */
1148
- async getLaneLatency(destChainSelector: bigint): Promise<LaneLatencyResponse> {
1208
+ async getLaneLatency(
1209
+ destChainSelector: bigint,
1210
+ numberOfBlocks?: number,
1211
+ ): Promise<LaneLatencyResponse> {
1149
1212
  if (!this.apiClient) {
1150
1213
  throw new CCIPApiClientNotAvailableError()
1151
1214
  }
1152
- return this.apiClient.getLaneLatency(this.network.chainSelector, destChainSelector)
1215
+ return this.apiClient.getLaneLatency(
1216
+ this.network.chainSelector,
1217
+ destChainSelector,
1218
+ numberOfBlocks,
1219
+ )
1153
1220
  }
1154
1221
 
1155
1222
  /**
@@ -1438,8 +1505,15 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1438
1505
  * Returns a copy of a message, populating missing fields like `extraArgs` with defaults.
1439
1506
  * It's expected to return a message suitable at least for basic token transfers.
1440
1507
  *
1441
- * @param message - AnyMessage (from source), containing at least `receiver`
1442
- * @returns A message suitable for `sendMessage` to this destination chain family
1508
+ * @param message - AnyMessage (from source), containing at least `receiver`.
1509
+ * @returns A message suitable for `sendMessage` to this destination chain family.
1510
+ *
1511
+ * @remarks
1512
+ * V3 (GenericExtraArgsV3) is auto-detected when any V3-only field is present
1513
+ * (e.g. `blockConfirmations`, `ccvs`, `ccvArgs`, `executor`, `executorArgs`,
1514
+ * `tokenReceiver`, `tokenArgs`). Otherwise defaults to V2 (EVMExtraArgsV2).
1515
+ *
1516
+ * @throws {@link CCIPArgumentInvalidError} if extraArgs contains unknown fields for the detected version.
1443
1517
  */
1444
1518
  static buildMessageForDest(
1445
1519
  message: Parameters<ChainStatic['buildMessageForDest']>[0],
@@ -1481,23 +1555,27 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
1481
1555
 
1482
1556
  /**
1483
1557
  * Estimate `ccipReceive` execution cost (gas, computeUnits) for this *dest*
1484
- * @param opts - estimation options
1558
+ * @param opts - estimation options, either `messageId` (for api) or `offRamp`, `message` (with `destTokenAmounts`)
1485
1559
  * @returns estimated execution cost (gas or computeUnits)
1486
1560
  */
1487
- estimateReceiveExecution?(opts: {
1488
- offRamp: string
1489
- receiver: string
1490
- message: {
1491
- sourceChainSelector: bigint
1492
- messageId: string
1493
- sender?: string
1494
- data?: BytesLike
1495
- destTokenAmounts?: readonly {
1496
- token: string
1497
- amount: bigint
1498
- }[]
1499
- }
1500
- }): Promise<number>
1561
+ estimateReceiveExecution?(
1562
+ opts:
1563
+ | {
1564
+ offRamp: string
1565
+ message: {
1566
+ sourceChainSelector: bigint
1567
+ messageId: string
1568
+ receiver: string
1569
+ sender?: string
1570
+ data?: BytesLike
1571
+ destTokenAmounts?: readonly ((
1572
+ | { token: string }
1573
+ | { destTokenAddress: string; extraData?: string }
1574
+ ) & { amount: bigint })[]
1575
+ }
1576
+ }
1577
+ | { messageId: string },
1578
+ ): Promise<number>
1501
1579
  }
1502
1580
 
1503
1581
  /**
@@ -144,6 +144,7 @@ export const CCIPErrorCode = {
144
144
  HTTP_ERROR: 'HTTP_ERROR',
145
145
  RPC_NOT_FOUND: 'RPC_NOT_FOUND',
146
146
  TIMEOUT: 'TIMEOUT',
147
+ ABORT: 'ABORT',
147
148
 
148
149
  // API Client
149
150
  API_CLIENT_NOT_AVAILABLE: 'API_CLIENT_NOT_AVAILABLE',
@@ -156,6 +156,7 @@ export { CCIPBorshMethodUnknownError, CCIPBorshTypeUnknownError } from './specia
156
156
 
157
157
  // Specialized errors - HTTP & Data
158
158
  export {
159
+ CCIPAbortError,
159
160
  CCIPBlockBeforeTimestampNotFoundError,
160
161
  CCIPDataFormatUnsupportedError,
161
162
  CCIPDataParseError,
@@ -170,6 +170,8 @@ export const DEFAULT_RECOVERY_HINTS: Partial<Record<CCIPErrorCode, string>> = {
170
170
  RPC_NOT_FOUND: 'No RPC endpoint found. Configure an RPC URL.',
171
171
  TIMEOUT:
172
172
  'Request timed out. Check network connectivity and try again. Consider increasing timeoutMs if the server is slow.',
173
+ ABORT:
174
+ 'Request was aborted. This is usually intentional (e.g. user cancellation or component unmount).',
173
175
 
174
176
  VIEM_ADAPTER_ERROR:
175
177
  'Check that your viem client has both account and chain defined. For WalletClient, use createWalletClient({ chain, account, ... }).',
@@ -1,6 +1,7 @@
1
1
  import { type CCIPErrorOptions, CCIPError } from './CCIPError.ts'
2
2
  import { CCIPErrorCode } from './codes.ts'
3
3
  import { isTransientHttpStatus } from '../http-status.ts'
4
+ import { bigIntReplacer } from '../utils.ts'
4
5
 
5
6
  // Chain/Network
6
7
 
@@ -166,7 +167,10 @@ export class CCIPMessageInvalidError extends CCIPError {
166
167
  override readonly name = 'CCIPMessageInvalidError'
167
168
  /** Creates a message invalid error. */
168
169
  constructor(data: unknown, options?: CCIPErrorOptions) {
169
- const dataStr = typeof data === 'object' && data !== null ? JSON.stringify(data) : String(data)
170
+ const dataStr =
171
+ typeof data === 'object' && data !== null
172
+ ? JSON.stringify(data, bigIntReplacer)
173
+ : String(data)
170
174
  super(CCIPErrorCode.MESSAGE_INVALID, `Invalid CCIP message format: ${dataStr}`, {
171
175
  ...options,
172
176
  isTransient: false,
@@ -1203,6 +1207,34 @@ export class CCIPTimeoutError extends CCIPError {
1203
1207
  }
1204
1208
  }
1205
1209
 
1210
+ /**
1211
+ * Thrown when a request is aborted via an AbortSignal.
1212
+ *
1213
+ * @example
1214
+ * ```typescript
1215
+ * const controller = new AbortController()
1216
+ * setTimeout(() => controller.abort(), 1000)
1217
+ * try {
1218
+ * await api.searchMessages({ sender: '0x...' }, { signal: controller.signal })
1219
+ * } catch (error) {
1220
+ * if (error instanceof CCIPAbortError) {
1221
+ * console.log(`Request was cancelled: ${error.context.operation}`)
1222
+ * }
1223
+ * }
1224
+ * ```
1225
+ */
1226
+ export class CCIPAbortError extends CCIPError {
1227
+ override readonly name = 'CCIPAbortError'
1228
+ /** Creates an abort error. */
1229
+ constructor(operation: string, options?: CCIPErrorOptions) {
1230
+ super(CCIPErrorCode.ABORT, `Request aborted: ${operation}`, {
1231
+ ...options,
1232
+ isTransient: false,
1233
+ context: { ...options?.context, operation },
1234
+ })
1235
+ }
1236
+ }
1237
+
1206
1238
  /**
1207
1239
  * Thrown for not implemented features.
1208
1240
  *
@@ -222,9 +222,13 @@ export default [
222
222
  name: 'getAllSourceChainConfigs',
223
223
  inputs: [],
224
224
  outputs: [
225
- { name: '', type: 'uint64[]', internalType: 'uint64[]' },
226
225
  {
227
- name: '',
226
+ name: 'sourceChainSelectors',
227
+ type: 'uint64[]',
228
+ internalType: 'uint64[]',
229
+ },
230
+ {
231
+ name: 'sourceChainConfigs',
228
232
  type: 'tuple[]',
229
233
  internalType: 'struct OffRamp.SourceChainConfig[]',
230
234
  components: [
@@ -418,25 +422,6 @@ export default [
418
422
  ],
419
423
  anonymous: false,
420
424
  },
421
- {
422
- type: 'event',
423
- name: 'MaxGasBufferToUpdateStateUpdated',
424
- inputs: [
425
- {
426
- name: 'oldMaxGasBufferToUpdateState',
427
- type: 'uint32',
428
- indexed: false,
429
- internalType: 'uint32',
430
- },
431
- {
432
- name: 'newMaxGasBufferToUpdateState',
433
- type: 'uint32',
434
- indexed: false,
435
- internalType: 'uint32',
436
- },
437
- ],
438
- anonymous: false,
439
- },
440
425
  {
441
426
  type: 'event',
442
427
  name: 'OwnershipTransferRequested',
@@ -611,6 +596,19 @@ export default [
611
596
  name: 'InvalidEncodingVersion',
612
597
  inputs: [{ name: 'version', type: 'uint8', internalType: 'uint8' }],
613
598
  },
599
+ {
600
+ type: 'error',
601
+ name: 'InvalidFinalityForReceiver',
602
+ inputs: [
603
+ { name: 'receiver', type: 'address', internalType: 'address' },
604
+ { name: 'msgBlockDepth', type: 'uint16', internalType: 'uint16' },
605
+ {
606
+ name: 'requiredBlockDepth',
607
+ type: 'uint16',
608
+ internalType: 'uint16',
609
+ },
610
+ ],
611
+ },
614
612
  {
615
613
  type: 'error',
616
614
  name: 'InvalidGasLimitOverride',
@@ -1546,7 +1546,6 @@ export default [
1546
1546
  name: 'InvalidTransferFeeBps',
1547
1547
  inputs: [{ name: 'bps', type: 'uint256', internalType: 'uint256' }],
1548
1548
  },
1549
- { type: 'error', name: 'MismatchedArrayLengths', inputs: [] },
1550
1549
  { type: 'error', name: 'MustBeProposedOwner', inputs: [] },
1551
1550
  {
1552
1551
  type: 'error',
package/src/evm/gas.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ type BytesLike,
2
3
  type JsonRpcApiProvider,
3
4
  Contract,
4
5
  FunctionFragment,
@@ -14,7 +15,6 @@ import {
14
15
  import type { TypedContract } from 'ethers-abitype'
15
16
  import { memoize } from 'micro-memoize'
16
17
 
17
- import type { Chain } from '../chain.ts'
18
18
  import TokenABI from './abi/BurnMintERC677Token.ts'
19
19
  import RouterABI from './abi/Router.ts'
20
20
  import { defaultAbiCoder, interfaces } from './const.ts'
@@ -77,13 +77,17 @@ const findBalancesSlot = memoize(
77
77
  { maxArgs: 1 },
78
78
  )
79
79
 
80
- type EstimateExecGasOpts = Pick<
81
- Parameters<NonNullable<Chain['estimateReceiveExecution']>>[0],
82
- 'message' | 'receiver'
83
- > & {
84
- /* */
80
+ type EstimateExecGasOpts = {
85
81
  provider: JsonRpcApiProvider
86
82
  router: string
83
+ message: {
84
+ sourceChainSelector: bigint
85
+ messageId: string
86
+ receiver: string
87
+ sender?: string
88
+ data?: BytesLike
89
+ destTokenAmounts?: readonly { token: string; amount: bigint }[]
90
+ }
87
91
  }
88
92
 
89
93
  /**
@@ -91,12 +95,7 @@ type EstimateExecGasOpts = Pick<
91
95
  * @param opts - Options for estimation: provider, destRouter, receiver address and message
92
96
  * @returns Estimated gasLimit
93
97
  */
94
- export async function estimateExecGas({
95
- provider,
96
- router,
97
- receiver,
98
- message,
99
- }: EstimateExecGasOpts) {
98
+ export async function estimateExecGas({ provider, router, message }: EstimateExecGasOpts) {
100
99
  // we need to override the state, increasing receiver's balance for each token, to simulate the
101
100
  // state after tokens were transferred by the offRamp just before calling `ccipReceive`
102
101
  const destAmounts: Record<string, bigint> = {}
@@ -106,25 +105,24 @@ export async function estimateExecGas({
106
105
  const tokenContract = new Contract(token, TokenABI, provider) as unknown as TypedContract<
107
106
  typeof TokenABI
108
107
  >
109
- const currentBalance = await tokenContract.balanceOf(receiver)
108
+ const currentBalance = await tokenContract.balanceOf(message.receiver)
110
109
  destAmounts[token] = currentBalance
111
110
  }
112
111
  destAmounts[token]! += amount
113
- const balancesSlot = await findBalancesSlot(token, provider, receiver, router)
112
+ const balancesSlot = await findBalancesSlot(token, provider, message.receiver, router)
114
113
  stateOverrides[token] = {
115
114
  stateDiff: {
116
- [solidityPackedKeccak256(['uint256', 'uint256'], [receiver, balancesSlot])]: toBeHex(
117
- destAmounts[token]!,
118
- 32,
119
- ),
115
+ [solidityPackedKeccak256(['uint256', 'uint256'], [message.receiver, balancesSlot])]:
116
+ toBeHex(destAmounts[token]!, 32),
120
117
  },
121
118
  }
122
119
  }
123
120
 
121
+ const senderBytes = getAddressBytes(message.sender ?? '0x')
124
122
  const receiverMsg: Any2EVMMessage = {
125
123
  ...message,
126
124
  destTokenAmounts: message.destTokenAmounts ?? [],
127
- sender: zeroPadValue(getAddressBytes(message.sender ?? '0x'), 32),
125
+ sender: senderBytes.length < 32 ? zeroPadValue(senderBytes, 32) : hexlify(senderBytes),
128
126
  data: hexlify(getDataBytes(message.data || '0x')),
129
127
  sourceChainSelector: message.sourceChainSelector,
130
128
  }
@@ -138,7 +136,7 @@ export async function estimateExecGas({
138
136
  (await provider.send('eth_estimateGas', [
139
137
  {
140
138
  from: router,
141
- to: receiver,
139
+ to: message.receiver,
142
140
  data: calldata,
143
141
  },
144
142
  'latest',