@chainlink/ccip-sdk 1.0.0 → 1.1.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.
Files changed (91) hide show
  1. package/dist/api/index.d.ts +4 -4
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +110 -11
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/types.d.ts +34 -0
  6. package/dist/api/types.d.ts.map +1 -1
  7. package/dist/chain.d.ts +93 -4
  8. package/dist/chain.d.ts.map +1 -1
  9. package/dist/chain.js +78 -8
  10. package/dist/chain.js.map +1 -1
  11. package/dist/errors/codes.d.ts +1 -1
  12. package/dist/errors/codes.d.ts.map +1 -1
  13. package/dist/errors/codes.js +2 -1
  14. package/dist/errors/codes.js.map +1 -1
  15. package/dist/errors/index.d.ts +2 -2
  16. package/dist/errors/index.d.ts.map +1 -1
  17. package/dist/errors/index.js +2 -2
  18. package/dist/errors/index.js.map +1 -1
  19. package/dist/errors/recovery.js +1 -1
  20. package/dist/errors/recovery.js.map +1 -1
  21. package/dist/errors/specialized.d.ts +22 -19
  22. package/dist/errors/specialized.d.ts.map +1 -1
  23. package/dist/errors/specialized.js +30 -25
  24. package/dist/errors/specialized.js.map +1 -1
  25. package/dist/evm/abi/OffRamp_2_0.d.ts +24 -12
  26. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -1
  27. package/dist/evm/abi/OffRamp_2_0.js +16 -8
  28. package/dist/evm/abi/OffRamp_2_0.js.map +1 -1
  29. package/dist/evm/abi/TokenPool_2_0.d.ts +1552 -0
  30. package/dist/evm/abi/TokenPool_2_0.d.ts.map +1 -0
  31. package/dist/evm/abi/TokenPool_2_0.js +1637 -0
  32. package/dist/evm/abi/TokenPool_2_0.js.map +1 -0
  33. package/dist/evm/const.d.ts +1 -0
  34. package/dist/evm/const.d.ts.map +1 -1
  35. package/dist/evm/const.js +2 -0
  36. package/dist/evm/const.js.map +1 -1
  37. package/dist/evm/index.d.ts +10 -4
  38. package/dist/evm/index.d.ts.map +1 -1
  39. package/dist/evm/index.js +138 -41
  40. package/dist/evm/index.js.map +1 -1
  41. package/dist/evm/messages.d.ts +2 -33
  42. package/dist/evm/messages.d.ts.map +1 -1
  43. package/dist/evm/messages.js +0 -210
  44. package/dist/evm/messages.js.map +1 -1
  45. package/dist/gas.d.ts +4 -0
  46. package/dist/gas.d.ts.map +1 -1
  47. package/dist/gas.js +27 -21
  48. package/dist/gas.js.map +1 -1
  49. package/dist/index.d.ts +2 -2
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/messages.d.ts +34 -0
  54. package/dist/messages.d.ts.map +1 -0
  55. package/dist/messages.js +211 -0
  56. package/dist/messages.js.map +1 -0
  57. package/dist/solana/cleanup.js +2 -2
  58. package/dist/solana/cleanup.js.map +1 -1
  59. package/dist/solana/exec.js +1 -1
  60. package/dist/solana/exec.js.map +1 -1
  61. package/dist/solana/index.d.ts +19 -19
  62. package/dist/solana/index.d.ts.map +1 -1
  63. package/dist/solana/index.js +14 -0
  64. package/dist/solana/index.js.map +1 -1
  65. package/dist/sui/index.d.ts.map +1 -1
  66. package/dist/sui/index.js +14 -1
  67. package/dist/sui/index.js.map +1 -1
  68. package/dist/ton/index.d.ts.map +1 -1
  69. package/dist/ton/index.js +3 -1
  70. package/dist/ton/index.js.map +1 -1
  71. package/package.json +5 -5
  72. package/src/api/index.ts +126 -11
  73. package/src/api/types.ts +43 -0
  74. package/src/chain.ts +131 -11
  75. package/src/errors/codes.ts +2 -1
  76. package/src/errors/index.ts +1 -1
  77. package/src/errors/recovery.ts +1 -1
  78. package/src/errors/specialized.ts +35 -30
  79. package/src/evm/abi/OffRamp_2_0.ts +16 -8
  80. package/src/evm/abi/TokenPool_2_0.ts +1636 -0
  81. package/src/evm/const.ts +2 -0
  82. package/src/evm/index.ts +230 -75
  83. package/src/evm/messages.ts +3 -285
  84. package/src/gas.ts +27 -19
  85. package/src/index.ts +2 -1
  86. package/src/messages.ts +278 -0
  87. package/src/solana/cleanup.ts +2 -2
  88. package/src/solana/exec.ts +1 -1
  89. package/src/solana/index.ts +17 -0
  90. package/src/sui/index.ts +17 -0
  91. package/src/ton/index.ts +5 -1
package/src/evm/const.ts CHANGED
@@ -19,6 +19,7 @@ import OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
19
19
  import PriceRegistry_1_2_ABI from './abi/PriceRegistry_1_2.ts'
20
20
  import Router_ABI from './abi/Router.ts'
21
21
  import TokenAdminRegistry_ABI from './abi/TokenAdminRegistry_1_5.ts'
22
+ import TokenPool_2_0_ABI from './abi/TokenPool_2_0.ts'
22
23
 
23
24
  export const defaultAbiCoder = AbiCoder.defaultAbiCoder()
24
25
 
@@ -38,6 +39,7 @@ export const interfaces = {
38
39
  FeeQuoter: new Interface(FeeQuoter_ABI),
39
40
  TokenPool_v1_5_1: new Interface(TokenPool_1_5_1_ABI),
40
41
  TokenPool_v1_5: new Interface(TokenPool_1_5_ABI),
42
+ TokenPool_v2_0: new Interface(TokenPool_2_0_ABI),
41
43
  TokenPool_v1_6: new Interface(TokenPool_1_6_ABI),
42
44
  CommitStore_v1_5: new Interface(CommitStore_1_5_ABI),
43
45
  CommitStore_v1_2: new Interface(CommitStore_1_2_ABI),
package/src/evm/index.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  getAddress,
16
16
  hexlify,
17
17
  isBytesLike,
18
+ isError,
18
19
  isHexString,
19
20
  keccak256,
20
21
  toBeHex,
@@ -27,9 +28,12 @@ import type { PickDeep, SetRequired } from 'type-fest'
27
28
  import {
28
29
  type ChainContext,
29
30
  type GetBalanceOpts,
31
+ type LaneFeatures,
30
32
  type LogFilter,
33
+ type RateLimiterState,
31
34
  type TokenPoolRemote,
32
35
  Chain,
36
+ LaneFeature,
33
37
  } from '../chain.ts'
34
38
  import {
35
39
  CCIPAddressInvalidEvmError,
@@ -38,11 +42,11 @@ import {
38
42
  CCIPContractNotRouterError,
39
43
  CCIPContractTypeInvalidError,
40
44
  CCIPDataFormatUnsupportedError,
45
+ CCIPError,
41
46
  CCIPExecTxNotConfirmedError,
42
47
  CCIPExecTxRevertedError,
43
48
  CCIPHasherVersionUnsupportedError,
44
49
  CCIPLogDataInvalidError,
45
- CCIPNotImplementedError,
46
50
  CCIPSourceChainUnsupportedError,
47
51
  CCIPTokenNotConfiguredError,
48
52
  CCIPTokenPoolChainConfigNotFoundError,
@@ -95,6 +99,7 @@ import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
95
99
  import type OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
96
100
  import type Router_ABI from './abi/Router.ts'
97
101
  import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
102
+ import type TokenPool_2_0_ABI from './abi/TokenPool_2_0.ts'
98
103
  import {
99
104
  CCV_INDEXER_URL,
100
105
  VersionedContractABI,
@@ -111,21 +116,21 @@ import {
111
116
  import { estimateExecGas } from './gas.ts'
112
117
  import { getV12LeafHasher, getV16LeafHasher } from './hasher.ts'
113
118
  import { getEvmLogs } from './logs.ts'
114
- import {
115
- type CCIPMessage_V1_6_EVM,
116
- type CCIPMessage_V2_0,
117
- type CleanAddressable,
118
- type MessageV1,
119
- type TokenTransferV1,
120
- decodeMessageV1,
121
- } from './messages.ts'
122
- export { decodeMessageV1 }
123
- export type { MessageV1, TokenTransferV1 }
119
+ import type { CCIPMessage_V1_6_EVM, CCIPMessage_V2_0, CleanAddressable } from './messages.ts'
124
120
  import { encodeEVMOffchainTokenData } from './offchain.ts'
125
121
  import { buildMessageForDest, decodeMessage, getMessagesInBatch } from '../requests.ts'
126
122
  import { type UnsignedEVMTx, resultToObject } from './types.ts'
123
+ import { decodeMessageV1 } from '../messages.ts'
127
124
  export type { UnsignedEVMTx }
128
125
 
126
+ /** Raw on-chain TokenBucket struct returned by TokenPool rate limiter queries. */
127
+ type RateLimiterBucket = { tokens: bigint; isEnabled: boolean; capacity: bigint; rate: bigint }
128
+
129
+ /** Converts an on-chain bucket to the public RateLimiterState, stripping `isEnabled`. */
130
+ function toRateLimiterState(b: RateLimiterBucket): RateLimiterState {
131
+ return b.isEnabled ? { tokens: b.tokens, capacity: b.capacity, rate: b.rate } : null
132
+ }
133
+
129
134
  /** typeguard for ethers Signer interface (used for `wallet`s) */
130
135
  function isSigner(wallet: unknown): wallet is Signer {
131
136
  return (
@@ -625,6 +630,45 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
625
630
  }
626
631
  }
627
632
 
633
+ /**
634
+ * {@inheritDoc Chain.getLaneFeatures}
635
+ */
636
+ override async getLaneFeatures(opts: {
637
+ router: string
638
+ destChainSelector: bigint
639
+ token?: string
640
+ }): Promise<Partial<LaneFeatures>> {
641
+ const onRamp = await this.getOnRampForRouter(opts.router, opts.destChainSelector)
642
+ const [, version] = await this.typeAndVersion(onRamp)
643
+
644
+ const result: Partial<LaneFeatures> = {}
645
+
646
+ // default FTF value for V2_0+ lanes if no token/pool or pool doesn't specify
647
+ if (version >= CCIPVersion.V2_0) result[LaneFeature.MIN_BLOCK_CONFIRMATIONS] = 1
648
+
649
+ // MIN_BLOCK_CONFIRMATIONS — V2_0+ only
650
+ if (opts.token) {
651
+ const { tokenPool } = await this.getRegistryTokenConfig(
652
+ await this.getTokenAdminRegistryFor(onRamp),
653
+ opts.token,
654
+ )
655
+ if (tokenPool) {
656
+ const { minBlockConfirmations } = await this.getTokenPoolConfig(tokenPool)
657
+ if (minBlockConfirmations != null)
658
+ result[LaneFeature.MIN_BLOCK_CONFIRMATIONS] = minBlockConfirmations
659
+
660
+ const remote = await this.getTokenPoolRemote(tokenPool, opts.destChainSelector)
661
+ result[LaneFeature.RATE_LIMITS] = remote.outboundRateLimiterState
662
+ if (minBlockConfirmations && 'customBlockConfirmationsOutboundRateLimiterState' in remote) {
663
+ result[LaneFeature.CUSTOM_BLOCK_CONFIRMATIONS_RATE_LIMITS] =
664
+ remote.customBlockConfirmationsOutboundRateLimiterState
665
+ }
666
+ }
667
+ }
668
+
669
+ return result
670
+ }
671
+
628
672
  /**
629
673
  * {@inheritDoc Chain.getRouterForOffRamp}
630
674
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
@@ -887,11 +931,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
887
931
  ? type.includes('OnRamp')
888
932
  ? interfaces.EVM2EVMOnRamp_v1_5
889
933
  : interfaces.EVM2EVMOffRamp_v1_5
890
- : type.includes('OnRamp')
891
- ? interfaces.OnRamp_v1_6
892
- : interfaces.OffRamp_v1_6,
934
+ : version < CCIPVersion.V2_0
935
+ ? type.includes('OnRamp')
936
+ ? interfaces.OnRamp_v1_6
937
+ : interfaces.OffRamp_v1_6
938
+ : type.includes('OnRamp')
939
+ ? interfaces.OnRamp_v2_0
940
+ : interfaces.OffRamp_v2_0,
893
941
  this.provider,
894
- ) as unknown as TypedContract<typeof OnRamp_1_6_ABI | typeof OffRamp_1_6_ABI>
942
+ ) as unknown as TypedContract<
943
+ | typeof EVM2EVMOnRamp_1_5_ABI
944
+ | typeof EVM2EVMOffRamp_1_5_ABI
945
+ | typeof OnRamp_1_6_ABI
946
+ | typeof OffRamp_1_6_ABI
947
+ | typeof OnRamp_2_0_ABI
948
+ | typeof OffRamp_2_0_ABI
949
+ >
895
950
  const { tokenAdminRegistry } = await contract.getStaticConfig()
896
951
  return tokenAdminRegistry as string
897
952
  }
@@ -975,10 +1030,12 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
975
1030
  )
976
1031
 
977
1032
  // make sure to approve once per token, for the total amount (including fee, if needed)
978
- const amountsToApprove = (message.tokenAmounts ?? []).reduce(
979
- (acc, { token, amount }) => ({ ...acc, [token]: (acc[token] ?? 0n) + amount }),
980
- {} as { [token: string]: bigint },
981
- )
1033
+ const amountsToApprove = (message.tokenAmounts ?? [])
1034
+ .filter(({ token }) => token && token !== ZeroAddress)
1035
+ .reduce(
1036
+ (acc, { token, amount }) => ({ ...acc, [token]: (acc[token] ?? 0n) + amount }),
1037
+ {} as { [token: string]: bigint },
1038
+ )
982
1039
  if (feeToken !== ZeroAddress)
983
1040
  amountsToApprove[feeToken] = (amountsToApprove[feeToken] ?? 0n) + message.fee
984
1041
 
@@ -992,7 +1049,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
992
1049
  ) as unknown as TypedContract<typeof Token_ABI>
993
1050
  const allowance = await contract.allowance(sender, router)
994
1051
  if (allowance >= amount) return
995
- const amnt = opts.approveMax ? 2n ** 256n - 1n : amount
1052
+ const amnt = opts.approveMax ? BigInt(2) ** BigInt(256) - BigInt(1) : amount
996
1053
  return contract.approve.populateTransaction(router, amnt, { from: sender })
997
1054
  }),
998
1055
  )
@@ -1003,6 +1060,13 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1003
1060
  interfaces.Router,
1004
1061
  this.provider,
1005
1062
  ) as unknown as TypedContract<typeof Router_ABI>
1063
+
1064
+ // if `token` is ZeroAddress, send its `amount` as `value` to router/EtherSenderReceiver (plus possibly native fee)
1065
+ // if native fee, include it in value; otherwise, it's transferedFrom feeToken
1066
+ const value = (message.tokenAmounts ?? [])
1067
+ .filter(({ token }) => token === ZeroAddress)
1068
+ .reduce((acc, { amount }) => acc + amount, feeToken === ZeroAddress ? message.fee : 0n)
1069
+
1006
1070
  const sendTx = await contract.ccipSend.populateTransaction(
1007
1071
  destChainSelector,
1008
1072
  {
@@ -1012,11 +1076,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1012
1076
  extraArgs,
1013
1077
  feeToken,
1014
1078
  },
1015
- {
1016
- from: sender,
1017
- // if native fee, include it in value; otherwise, it's transferedFrom feeToken
1018
- ...(feeToken === ZeroAddress && { value: message.fee }),
1019
- },
1079
+ { from: sender, ...(value > 0n ? { value } : {}) },
1020
1080
  )
1021
1081
  const txRequests = [...approveTxs, sendTx] as SetRequired<typeof sendTx, 'from'>[]
1022
1082
  return {
@@ -1100,6 +1160,13 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1100
1160
  const txGasLimit = await contract.executeSingleMessage.estimateGas(
1101
1161
  {
1102
1162
  ...message,
1163
+ onRampAddress: zeroPadValue(getAddressBytes(message.onRampAddress), 32),
1164
+ sender: zeroPadValue(getAddressBytes(message.sender), 32),
1165
+ tokenTransfer: message.tokenTransfer.map((ta) => ({
1166
+ ...ta,
1167
+ sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
1168
+ sourceTokenAddress: zeroPadValue(getAddressBytes(ta.sourceTokenAddress), 32),
1169
+ })),
1103
1170
  executionGasLimit: BigInt(message.executionGasLimit),
1104
1171
  ccipReceiveGasLimit: BigInt(message.ccipReceiveGasLimit),
1105
1172
  finality: BigInt(message.finality),
@@ -1226,6 +1293,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1226
1293
  default:
1227
1294
  throw new CCIPVersionUnsupportedError(version)
1228
1295
  }
1296
+
1297
+ /* Executing a message for the first time has some hard try/catches on-chain
1298
+ * so we need to ensure some lower-bounds gasLimits */
1299
+ let gasLimit = await this.provider.estimateGas(manualExecTx)
1300
+ if (
1301
+ 'gasLimit' in input.message &&
1302
+ input.message.gasLimit &&
1303
+ gasLimit < input.message.gasLimit + 100000n
1304
+ )
1305
+ // if message requested gasLimit, ensure execution more than 100k above requested, otherwise it's clearly a try/catch fail
1306
+ gasLimit = BigInt(input.message.gasLimit) + 200000n
1307
+ else if ('gasLimit' in input.message && !input.message.gasLimit && gasLimit < 240000n)
1308
+ // if message didn't request gasLimit, ensure execution gasLimit is above 240k (empiric)
1309
+ gasLimit = 240000n
1310
+ manualExecTx.gasLimit = gasLimit
1311
+
1229
1312
  return { family: ChainFamily.EVM, transactions: [manualExecTx] }
1230
1313
  }
1231
1314
 
@@ -1252,7 +1335,8 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1252
1335
  const response = await submitTransaction(wallet, populatedTx, this.provider)
1253
1336
  this.logger.debug('manuallyExecute =>', response.hash)
1254
1337
 
1255
- const receipt = await response.wait(1, 60_000)
1338
+ let receipt = await response.wait(0)
1339
+ if (!receipt) receipt = await response.wait(1, 240_000)
1256
1340
  if (!receipt?.hash) throw new CCIPExecTxNotConfirmedError(response.hash)
1257
1341
  if (!receipt.status) throw new CCIPExecTxRevertedError(response.hash)
1258
1342
  const tx = await this.getTransaction(receipt)
@@ -1328,24 +1412,44 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1328
1412
  token: string
1329
1413
  router: string
1330
1414
  typeAndVersion: string
1415
+ minBlockConfirmations?: number
1331
1416
  }> {
1332
- const [_, , typeAndVersion] = await this.typeAndVersion(tokenPool)
1333
-
1334
- const contract = new Contract(
1335
- tokenPool,
1336
- interfaces.TokenPool_v1_6,
1337
- this.provider,
1338
- ) as unknown as TypedContract<typeof TokenPool_ABI>
1417
+ const [_, version, typeAndVersion] = await this.typeAndVersion(tokenPool)
1339
1418
 
1419
+ let contract, router, minBlockConfirmations
1420
+ if (version < CCIPVersion.V2_0) {
1421
+ contract = new Contract(
1422
+ tokenPool,
1423
+ interfaces.TokenPool_v1_6,
1424
+ this.provider,
1425
+ ) as unknown as TypedContract<typeof TokenPool_ABI>
1426
+ router = contract.getRouter()
1427
+ } else {
1428
+ contract = new Contract(
1429
+ tokenPool,
1430
+ interfaces.TokenPool_v2_0,
1431
+ this.provider,
1432
+ ) as unknown as TypedContract<typeof TokenPool_2_0_ABI>
1433
+ router = contract.getDynamicConfig().then(([router]) => router)
1434
+ minBlockConfirmations = contract.getMinBlockConfirmations().catch((err) => {
1435
+ if (isError(err, 'CALL_EXCEPTION')) return 0
1436
+ throw CCIPError.from(err)
1437
+ })
1438
+ }
1340
1439
  const token = contract.getToken()
1341
- const router = contract.getRouter()
1342
- return Promise.all([token, router]).then(([token, router]) => {
1343
- return {
1344
- token: token as string,
1345
- router: router as string,
1346
- typeAndVersion,
1347
- }
1348
- })
1440
+
1441
+ return Promise.all([token, router, minBlockConfirmations]).then(
1442
+ ([token, router, minBlockConfirmations]) => {
1443
+ return {
1444
+ token: token as CleanAddressable<typeof token>,
1445
+ router: router as CleanAddressable<typeof router>,
1446
+ typeAndVersion,
1447
+ ...(minBlockConfirmations != null && {
1448
+ minBlockConfirmations: Number(minBlockConfirmations),
1449
+ }),
1450
+ }
1451
+ },
1452
+ )
1349
1453
  }
1350
1454
 
1351
1455
  /** {@inheritDoc Chain.getTokenPoolRemotes} */
@@ -1359,53 +1463,93 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1359
1463
  if (remoteChainSelector) supportedChains = Promise.resolve([networkInfo(remoteChainSelector)])
1360
1464
 
1361
1465
  let remotePools: Promise<string[][]>
1362
- let contract
1466
+ let remoteInfo
1363
1467
  if (version < '1.5.1') {
1364
- const contract_ = new Contract(
1468
+ const contract = new Contract(
1365
1469
  tokenPool,
1366
1470
  interfaces.TokenPool_v1_5,
1367
1471
  this.provider,
1368
1472
  ) as unknown as TypedContract<typeof TokenPool_1_5_ABI>
1369
- contract = contract_
1370
1473
  supportedChains ??= contract.getSupportedChains().then((chains) => chains.map(networkInfo))
1371
1474
  remotePools = supportedChains.then((chains) =>
1372
1475
  Promise.all(
1373
1476
  chains.map((chain) =>
1374
- contract_
1477
+ contract
1375
1478
  .getRemotePool(chain.chainSelector)
1376
1479
  .then((remotePool) => [decodeAddress(remotePool, chain.family)]),
1377
1480
  ),
1378
1481
  ),
1379
1482
  )
1380
- } else {
1381
- const contract_ = new Contract(
1483
+ remoteInfo = supportedChains.then((chains) =>
1484
+ Promise.all(
1485
+ chains.map((chain) =>
1486
+ Promise.all([
1487
+ contract.getRemoteToken(chain.chainSelector),
1488
+ resultToObject(contract.getCurrentOutboundRateLimiterState(chain.chainSelector)),
1489
+ resultToObject(contract.getCurrentInboundRateLimiterState(chain.chainSelector)),
1490
+ ] as const),
1491
+ ),
1492
+ ),
1493
+ )
1494
+ } else if (version < CCIPVersion.V2_0) {
1495
+ const contract = new Contract(
1382
1496
  tokenPool,
1383
1497
  interfaces.TokenPool_v1_6,
1384
1498
  this.provider,
1385
1499
  ) as unknown as TypedContract<typeof TokenPool_ABI>
1386
- contract = contract_
1387
1500
  supportedChains ??= contract.getSupportedChains().then((chains) => chains.map(networkInfo))
1388
1501
  remotePools = supportedChains.then((chains) =>
1389
1502
  Promise.all(
1390
1503
  chains.map((chain) =>
1391
- contract_
1504
+ contract
1392
1505
  .getRemotePools(chain.chainSelector)
1393
1506
  .then((pools) => pools.map((remotePool) => decodeAddress(remotePool, chain.family))),
1394
1507
  ),
1395
1508
  ),
1396
1509
  )
1397
- }
1398
- const remoteInfo = supportedChains.then((chains) =>
1399
- Promise.all(
1400
- chains.map((chain) =>
1401
- Promise.all([
1402
- contract.getRemoteToken(chain.chainSelector),
1403
- resultToObject(contract.getCurrentInboundRateLimiterState(chain.chainSelector)),
1404
- resultToObject(contract.getCurrentOutboundRateLimiterState(chain.chainSelector)),
1405
- ] as const),
1510
+ remoteInfo = supportedChains.then((chains) =>
1511
+ Promise.all(
1512
+ chains.map((chain) =>
1513
+ Promise.all([
1514
+ contract.getRemoteToken(chain.chainSelector),
1515
+ resultToObject(contract.getCurrentOutboundRateLimiterState(chain.chainSelector)),
1516
+ resultToObject(contract.getCurrentInboundRateLimiterState(chain.chainSelector)),
1517
+ ] as const),
1518
+ ),
1406
1519
  ),
1407
- ),
1408
- )
1520
+ )
1521
+ } else {
1522
+ const contract = new Contract(
1523
+ tokenPool,
1524
+ interfaces.TokenPool_v2_0,
1525
+ this.provider,
1526
+ ) as unknown as TypedContract<typeof TokenPool_2_0_ABI>
1527
+ supportedChains ??= contract.getSupportedChains().then((chains) => chains.map(networkInfo))
1528
+ remotePools = supportedChains.then((chains) =>
1529
+ Promise.all(
1530
+ chains.map((chain) =>
1531
+ contract
1532
+ .getRemotePools(chain.chainSelector)
1533
+ .then((pools) => pools.map((remotePool) => decodeAddress(remotePool, chain.family))),
1534
+ ),
1535
+ ),
1536
+ )
1537
+ remoteInfo = supportedChains.then((chains) =>
1538
+ Promise.all(
1539
+ chains.map((chain) =>
1540
+ Promise.all([
1541
+ contract.getRemoteToken(chain.chainSelector),
1542
+ contract.getCurrentRateLimiterState(chain.chainSelector, false),
1543
+ contract.getCurrentRateLimiterState(chain.chainSelector, true),
1544
+ ] as const).then(
1545
+ ([remoteToken, [outbound, inbound], [customOutbound, customInbound]]) => {
1546
+ return [remoteToken, outbound, inbound, customOutbound, customInbound] as const
1547
+ },
1548
+ ),
1549
+ ),
1550
+ ),
1551
+ )
1552
+ }
1409
1553
  return Promise.all([supportedChains, remotePools, remoteInfo]).then(
1410
1554
  ([supportedChains, remotePools, remoteInfo]) =>
1411
1555
  Object.fromEntries(
@@ -1418,8 +1562,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1418
1562
  {
1419
1563
  remoteToken: decodeAddress(remoteTokenRaw, chain.family),
1420
1564
  remotePools: remotePools[i]!.map((pool) => decodeAddress(pool, chain.family)),
1421
- inboundRateLimiterState: remoteInfo[i]![1].isEnabled ? remoteInfo[i]![1] : null,
1422
- outboundRateLimiterState: remoteInfo[i]![2].isEnabled ? remoteInfo[i]![2] : null,
1565
+ outboundRateLimiterState: toRateLimiterState(remoteInfo[i]![1]),
1566
+ inboundRateLimiterState: toRateLimiterState(remoteInfo[i]![2]),
1567
+ ...(remoteInfo[i]!.length === 5 && {
1568
+ customBlockConfirmationsOutboundRateLimiterState: toRateLimiterState(
1569
+ remoteInfo[i]![3],
1570
+ ),
1571
+ customBlockConfirmationsInboundRateLimiterState: toRateLimiterState(
1572
+ remoteInfo[i]![4],
1573
+ ),
1574
+ }),
1423
1575
  },
1424
1576
  ] as const
1425
1577
  }),
@@ -1461,7 +1613,8 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1461
1613
  tokens = Array.from(tokens_)
1462
1614
  break
1463
1615
  }
1464
- case CCIPVersion.V1_6: {
1616
+ case CCIPVersion.V1_6:
1617
+ case CCIPVersion.V2_0: {
1465
1618
  const feeQuoter = await this.getFeeQuoterFor(onRamp)
1466
1619
  const contract = new Contract(
1467
1620
  feeQuoter,
@@ -1489,15 +1642,13 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1489
1642
  ): Promise<CCIPVerifications> {
1490
1643
  const { offRamp, request } = opts
1491
1644
  if (request.lane.version >= CCIPVersion.V2_0) {
1492
- const message = request.message as CCIPMessage_V2_0
1493
- if (!message.encodedMessage)
1494
- throw new CCIPNotImplementedError(`CCIPAPIClient getMessageById v2 encodedMessage`)
1645
+ const { encodedMessage } = request.message as CCIPMessage_V2_0
1495
1646
  const contract = new Contract(
1496
1647
  offRamp,
1497
1648
  interfaces.OffRamp_v2_0,
1498
1649
  this.provider,
1499
1650
  ) as unknown as TypedContract<typeof OffRamp_2_0_ABI>
1500
- const ccvs = await contract.getCCVsForMessage(message.encodedMessage)
1651
+ const ccvs = await contract.getCCVsForMessage(encodedMessage)
1501
1652
  const [requiredCCVs, optionalCCVs, optionalThreshold] = ccvs.map(
1502
1653
  resultToObject,
1503
1654
  ) as unknown as CleanAddressable<typeof ccvs>
@@ -1511,20 +1662,24 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1511
1662
  const apiRes = await this.apiClient.getMessageById(request.message.messageId)
1512
1663
  if ('verifiers' in apiRes.message) {
1513
1664
  const verifiers = apiRes.message.verifiers as {
1514
- items: {
1665
+ items?: {
1515
1666
  destAddress: string
1516
1667
  sourceAddress: string
1517
- verification: { data: string; timestamp: string }
1668
+ verification?: { data: string; timestamp: string }
1518
1669
  }[]
1519
1670
  }
1520
1671
  return {
1521
1672
  verificationPolicy,
1522
- verifications: verifiers.items.map((item) => ({
1523
- destAddress: item.destAddress,
1524
- sourceAddress: item.sourceAddress,
1525
- ccvData: item.verification.data,
1526
- timestamp: new Date(item.verification.timestamp).getTime() / 1e3,
1527
- })),
1673
+ verifications: (verifiers.items ?? [])
1674
+ .filter((item) => item.verification?.data)
1675
+ .map((item) => ({
1676
+ destAddress: item.destAddress,
1677
+ sourceAddress: item.sourceAddress,
1678
+ ccvData: item.verification!.data,
1679
+ ...(!!item.verification?.timestamp && {
1680
+ timestamp: new Date(item.verification.timestamp).getTime() / 1e3,
1681
+ }),
1682
+ })),
1528
1683
  }
1529
1684
  }
1530
1685
  }