@chainlink/ccip-sdk 1.3.0 → 1.4.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 (93) hide show
  1. package/dist/api/index.d.ts +17 -6
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +17 -6
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/aptos/index.d.ts +7 -1
  6. package/dist/aptos/index.d.ts.map +1 -1
  7. package/dist/aptos/index.js +20 -0
  8. package/dist/aptos/index.js.map +1 -1
  9. package/dist/chain.d.ts +64 -6
  10. package/dist/chain.d.ts.map +1 -1
  11. package/dist/chain.js +37 -2
  12. package/dist/chain.js.map +1 -1
  13. package/dist/errors/recovery.js +1 -1
  14. package/dist/errors/specialized.d.ts +17 -3
  15. package/dist/errors/specialized.d.ts.map +1 -1
  16. package/dist/errors/specialized.js +17 -3
  17. package/dist/errors/specialized.js.map +1 -1
  18. package/dist/evm/const.d.ts.map +1 -1
  19. package/dist/evm/const.js +3 -0
  20. package/dist/evm/const.js.map +1 -1
  21. package/dist/evm/errors.d.ts.map +1 -1
  22. package/dist/evm/errors.js +20 -5
  23. package/dist/evm/errors.js.map +1 -1
  24. package/dist/evm/index.d.ts +11 -2
  25. package/dist/evm/index.d.ts.map +1 -1
  26. package/dist/evm/index.js +65 -14
  27. package/dist/evm/index.js.map +1 -1
  28. package/dist/gas.d.ts +2 -1
  29. package/dist/gas.d.ts.map +1 -1
  30. package/dist/gas.js +2 -1
  31. package/dist/gas.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/offchain.d.ts +1 -0
  36. package/dist/offchain.d.ts.map +1 -1
  37. package/dist/offchain.js +1 -0
  38. package/dist/offchain.js.map +1 -1
  39. package/dist/requests.d.ts +6 -5
  40. package/dist/requests.d.ts.map +1 -1
  41. package/dist/requests.js +6 -5
  42. package/dist/requests.js.map +1 -1
  43. package/dist/solana/idl/1.6.0/FEE_QUOTER.d.ts +1719 -0
  44. package/dist/solana/idl/1.6.0/FEE_QUOTER.d.ts.map +1 -0
  45. package/dist/solana/idl/1.6.0/FEE_QUOTER.js +1719 -0
  46. package/dist/solana/idl/1.6.0/FEE_QUOTER.js.map +1 -0
  47. package/dist/solana/index.d.ts +17 -1
  48. package/dist/solana/index.d.ts.map +1 -1
  49. package/dist/solana/index.js +37 -9
  50. package/dist/solana/index.js.map +1 -1
  51. package/dist/solana/utils.d.ts +6 -3
  52. package/dist/solana/utils.d.ts.map +1 -1
  53. package/dist/solana/utils.js +17 -16
  54. package/dist/solana/utils.js.map +1 -1
  55. package/dist/sui/index.d.ts +1 -1
  56. package/dist/sui/index.d.ts.map +1 -1
  57. package/dist/sui/index.js +1 -1
  58. package/dist/sui/index.js.map +1 -1
  59. package/dist/ton/index.d.ts +11 -9
  60. package/dist/ton/index.d.ts.map +1 -1
  61. package/dist/ton/index.js +11 -9
  62. package/dist/ton/index.js.map +1 -1
  63. package/dist/ton/send.d.ts +7 -0
  64. package/dist/ton/send.d.ts.map +1 -1
  65. package/dist/ton/send.js +7 -0
  66. package/dist/ton/send.js.map +1 -1
  67. package/dist/types.d.ts +1 -1
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/utils.d.ts +9 -2
  70. package/dist/utils.d.ts.map +1 -1
  71. package/dist/utils.js +16 -5
  72. package/dist/utils.js.map +1 -1
  73. package/package.json +7 -7
  74. package/src/api/index.ts +17 -6
  75. package/src/aptos/index.ts +31 -0
  76. package/src/chain.ts +67 -7
  77. package/src/errors/recovery.ts +1 -1
  78. package/src/errors/specialized.ts +17 -3
  79. package/src/evm/const.ts +3 -0
  80. package/src/evm/errors.ts +22 -5
  81. package/src/evm/index.ts +102 -13
  82. package/src/gas.ts +2 -1
  83. package/src/index.ts +1 -0
  84. package/src/offchain.ts +1 -0
  85. package/src/requests.ts +6 -5
  86. package/src/solana/idl/1.6.0/FEE_QUOTER.ts +3440 -0
  87. package/src/solana/index.ts +55 -10
  88. package/src/solana/utils.ts +29 -19
  89. package/src/sui/index.ts +1 -1
  90. package/src/ton/index.ts +11 -9
  91. package/src/ton/send.ts +7 -0
  92. package/src/types.ts +1 -1
  93. package/src/utils.ts +19 -6
package/src/evm/index.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  type LogFilter,
35
35
  type RateLimiterState,
36
36
  type TokenPoolRemote,
37
+ type TokenPrice,
37
38
  type TokenTransferFeeConfig,
38
39
  type TokenTransferFeeOpts,
39
40
  type TotalFeesEstimate,
@@ -56,7 +57,6 @@ import {
56
57
  CCIPTokenNotConfiguredError,
57
58
  CCIPTokenPoolChainConfigNotFoundError,
58
59
  CCIPTransactionNotFoundError,
59
- CCIPVersionFeatureUnavailableError,
60
60
  CCIPVersionRequiresLaneError,
61
61
  CCIPVersionUnsupportedError,
62
62
  CCIPWalletInvalidError,
@@ -87,6 +87,7 @@ import {
87
87
  decodeOnRampAddress,
88
88
  getAddressBytes,
89
89
  getDataBytes,
90
+ getSomeBlockNumberBefore,
90
91
  networkInfo,
91
92
  parseTypeAndVersion,
92
93
  } from '../utils.ts'
@@ -250,6 +251,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
250
251
  this.getFeeTokens = memoize(this.getFeeTokens.bind(this), { async: true, maxArgs: 1 })
251
252
  this.detectUsdcDomains = memoize(this.detectUsdcDomains.bind(this))
252
253
  this.resolveVerifier = memoize(this.resolveVerifier.bind(this))
254
+ this.getFeeQuoterFor = memoize(this.getFeeQuoterFor.bind(this), {
255
+ async: true,
256
+ maxArgs: 1,
257
+ })
253
258
  }
254
259
 
255
260
  /**
@@ -970,17 +975,26 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
970
975
  * @throws {@link CCIPVersionFeatureUnavailableError} if contract version is below v1.6
971
976
  */
972
977
  async getFeeQuoterFor(address: string): Promise<string> {
973
- let [type, version, typeAndVersion] = await this.typeAndVersion(address)
974
- if (type === 'FeeQuoter') {
978
+ const [type, version, typeAndVersion] = await this.typeAndVersion(address)
979
+ if (type === 'FeeQuoter' || type === 'PriceRegistry') {
975
980
  return address
976
981
  } else if (type === 'Router') {
977
- address = await this._getSomeOnRampFor(address)
978
- ;[type, version, typeAndVersion] = await this.typeAndVersion(address)
982
+ return this.getFeeQuoterFor(await this._getSomeOnRampFor(address)) // use cache
979
983
  } else if (!type.includes('Ramp')) {
980
984
  throw new CCIPContractNotRouterError(address, typeAndVersion)
981
985
  }
982
- if (version < CCIPVersion.V1_6)
983
- throw new CCIPVersionFeatureUnavailableError('feeQuoter', version, 'v1.6')
986
+
987
+ if (version < CCIPVersion.V1_6) {
988
+ const contract = new Contract(
989
+ address,
990
+ version === CCIPVersion.V1_2
991
+ ? interfaces.EVM2EVMOnRamp_v1_2
992
+ : interfaces.EVM2EVMOnRamp_v1_5,
993
+ this.provider,
994
+ ) as unknown as TypedContract<typeof EVM2EVMOnRamp_1_2_ABI | typeof EVM2EVMOnRamp_1_5_ABI>
995
+ const { priceRegistry } = await contract.getDynamicConfig()
996
+ return priceRegistry as string
997
+ }
984
998
 
985
999
  const isOnRamp = type.includes('OnRamp')
986
1000
  const contract = new Contract(
@@ -1017,7 +1031,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1017
1031
  this.provider,
1018
1032
  ) as unknown as TypedContract<typeof Router_ABI>
1019
1033
  return contract.getFee(destChainSelector, {
1020
- receiver: zeroPadValue(getAddressBytes(populatedMessage.receiver), 32),
1034
+ receiver: (() => {
1035
+ const receiverBytes = getAddressBytes(populatedMessage.receiver)
1036
+ return receiverBytes.length <= 32 ? zeroPadValue(receiverBytes, 32) : hexlify(receiverBytes)
1037
+ })(),
1021
1038
  data: hexlify(populatedMessage.data ?? '0x'),
1022
1039
  tokenAmounts: populatedMessage.tokenAmounts ?? [],
1023
1040
  feeToken: populatedMessage.feeToken ?? ZeroAddress,
@@ -1133,6 +1150,52 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1133
1150
  return undefined
1134
1151
  }
1135
1152
 
1153
+ /** {@inheritDoc Chain.getTokenPrice} */
1154
+ override async getTokenPrice(opts: {
1155
+ router: string
1156
+ token: string
1157
+ timestamp?: number
1158
+ }): Promise<TokenPrice> {
1159
+ let { token } = opts
1160
+
1161
+ // Resolve native token (ZeroAddress) to wrapped native
1162
+ if (token === ZeroAddress) {
1163
+ token = await this.getNativeTokenForRouter(opts.router)
1164
+ }
1165
+
1166
+ const priceContractAddress = await this.getFeeQuoterFor(opts.router)
1167
+
1168
+ // Both PriceRegistry (v1.2/v1.5) and FeeQuoter (v1.6+) expose
1169
+ // getTokenPrice(address) → { value: uint224, timestamp: uint32 }
1170
+ const contract = new Contract(
1171
+ priceContractAddress,
1172
+ interfaces.FeeQuoter,
1173
+ this.provider,
1174
+ ) as unknown as TypedContract<typeof FeeQuoter_ABI>
1175
+
1176
+ // If timestamp provided, resolve to block number for historical query
1177
+ let blockTag: number | undefined
1178
+ if (opts.timestamp != null) {
1179
+ const { number: latestBlock } = (await this.provider.getBlock('latest'))!
1180
+ blockTag = await getSomeBlockNumberBefore(
1181
+ async (block: number) => (await this.provider.getBlock(block))!.timestamp,
1182
+ latestBlock,
1183
+ opts.timestamp,
1184
+ this,
1185
+ )
1186
+ }
1187
+
1188
+ const [result, { decimals }] = await Promise.all([
1189
+ blockTag != null
1190
+ ? contract.getTokenPrice.staticCall(token, { blockTag })
1191
+ : contract.getTokenPrice(token),
1192
+ this.getTokenInfo(token),
1193
+ ])
1194
+
1195
+ const rawPrice = BigInt(result.value)
1196
+ return { price: Number(rawPrice) * 10 ** (decimals - 36) }
1197
+ }
1198
+
1136
1199
  /** {@inheritDoc Chain.getTotalFeesEstimate} */
1137
1200
  override async getTotalFeesEstimate(
1138
1201
  opts: Parameters<Chain['getTotalFeesEstimate']>[0],
@@ -1152,7 +1215,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1152
1215
  let tokenArgs: string = '0x'
1153
1216
  if (extraArgs && 'blockConfirmations' in extraArgs) {
1154
1217
  blockConfirmations = extraArgs.blockConfirmations as number
1155
- tokenArgs = hexlify(extraArgs.tokenArgs as BytesLike)
1218
+ if (extraArgs.tokenArgs) tokenArgs = hexlify(extraArgs.tokenArgs)
1156
1219
  }
1157
1220
 
1158
1221
  // Skip pool-level fee lookup for pre-v2.0 lanes
@@ -1257,7 +1320,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1257
1320
  }
1258
1321
 
1259
1322
  const feeToken = message.feeToken ?? ZeroAddress
1260
- const receiver = zeroPadValue(getAddressBytes(message.receiver), 32)
1323
+ const receiverBytes = getAddressBytes(message.receiver)
1324
+ const receiver =
1325
+ receiverBytes.length <= 32 ? zeroPadValue(receiverBytes, 32) : hexlify(receiverBytes)
1261
1326
  const data = hexlify(message.data ?? '0x')
1262
1327
  const extraArgs = hexlify(
1263
1328
  (this.constructor as typeof EVMChain).encodeExtraArgs(message.extraArgs),
@@ -1639,7 +1704,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1639
1704
  * Fetches the token pool configuration for an EVM token pool contract.
1640
1705
  *
1641
1706
  * @param tokenPool - Token pool contract address.
1642
- * @param feeOpts - Optional parameters to also fetch token transfer fee config.
1707
+ * @param feeOpts - Optional parameters to also fetch token transfer fee config:
1708
+ * - `destChainSelector` — destination chain selector.
1709
+ * - `blockConfirmationsRequested` — number of block confirmations (0 = standard, positive = FTF).
1710
+ * - `tokenArgs` — hex-encoded bytes passed to the pool contract.
1643
1711
  * @returns Token pool config containing token, router, typeAndVersion, and optionally
1644
1712
  * minBlockConfirmations and tokenTransferFeeConfig.
1645
1713
  *
@@ -1658,7 +1726,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1658
1726
  minBlockConfirmations?: number
1659
1727
  tokenTransferFeeConfig?: TokenTransferFeeConfig
1660
1728
  }> {
1661
- const [_, version, typeAndVersion] = await this.typeAndVersion(tokenPool)
1729
+ const [type, version, typeAndVersion] = await this.typeAndVersion(tokenPool)
1662
1730
 
1663
1731
  let token, router, minBlockConfirmations, tokenTransferFeeConfig
1664
1732
  if (version < CCIPVersion.V2_0) {
@@ -1670,6 +1738,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1670
1738
  token = contract.getToken()
1671
1739
  router = contract.getRouter()
1672
1740
  } else {
1741
+ if (type === 'USDCTokenPoolProxy') {
1742
+ const proxy = new Contract(
1743
+ tokenPool,
1744
+ interfaces.USDCTokenPoolProxy_v2_0,
1745
+ this.provider,
1746
+ ) as unknown as TypedContract<typeof USDCTokenPoolProxy_2_0_ABI>
1747
+ tokenPool = (await proxy.getPools())['cctpV2PoolWithCCV'] as string
1748
+ }
1673
1749
  const contract = new Contract(
1674
1750
  tokenPool,
1675
1751
  interfaces.TokenPool_v2_0,
@@ -1678,6 +1754,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1678
1754
  token = contract.getToken()
1679
1755
  router = contract.getDynamicConfig().then(([router]) => router)
1680
1756
  minBlockConfirmations = contract.getMinBlockConfirmations().catch((err) => {
1757
+ this.logger.debug(
1758
+ typeAndVersion,
1759
+ 'threw when fetching minBlockConfirmations, defaulting to 0:',
1760
+ err,
1761
+ )
1681
1762
  if (isError(err, 'CALL_EXCEPTION')) return 0
1682
1763
  throw CCIPError.from(err)
1683
1764
  })
@@ -1750,7 +1831,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1750
1831
  tokenPool: string,
1751
1832
  remoteChainSelector?: bigint,
1752
1833
  ): Promise<Record<string, TokenPoolRemote>> {
1753
- const [_, version] = await this.typeAndVersion(tokenPool)
1834
+ const [type, version] = await this.typeAndVersion(tokenPool)
1754
1835
 
1755
1836
  let supportedChains: Promise<NetworkInfo[]> | undefined
1756
1837
  if (remoteChainSelector) supportedChains = Promise.resolve([networkInfo(remoteChainSelector)])
@@ -1812,6 +1893,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1812
1893
  ),
1813
1894
  )
1814
1895
  } else {
1896
+ if (type === 'USDCTokenPoolProxy') {
1897
+ const proxy = new Contract(
1898
+ tokenPool,
1899
+ interfaces.USDCTokenPoolProxy_v2_0,
1900
+ this.provider,
1901
+ ) as unknown as TypedContract<typeof USDCTokenPoolProxy_2_0_ABI>
1902
+ tokenPool = (await proxy.getPools())['cctpV2PoolWithCCV'] as string
1903
+ }
1815
1904
  const contract = new Contract(
1816
1905
  tokenPool,
1817
1906
  interfaces.TokenPool_v2_0,
package/src/gas.ts CHANGED
@@ -57,11 +57,12 @@ export type EstimateReceiveExecutionOpts = {
57
57
  * Estimate CCIP gasLimit needed to execute a request on a contract receiver.
58
58
  *
59
59
  * @param opts - {@link EstimateReceiveExecutionOpts} for estimation
60
- * @returns Estimated gasLimit
60
+ * @returns Estimated execution gas (base transaction cost subtracted)
61
61
  *
62
62
  * @throws {@link CCIPMethodUnsupportedError} if dest chain doesn't support estimation
63
63
  * @throws {@link CCIPContractTypeInvalidError} if routerOrRamp is not a valid contract type
64
64
  * @throws {@link CCIPTokenDecimalsInsufficientError} if dest token has insufficient decimals
65
+ * @throws {@link CCIPOnRampRequiredError} if no OnRamp found for the given OffRamp and source chain
65
66
  *
66
67
  * @example
67
68
  * ```typescript
package/src/index.ts CHANGED
@@ -37,6 +37,7 @@ export type {
37
37
  TokenInfo,
38
38
  TokenPoolConfig,
39
39
  TokenPoolRemote,
40
+ TokenPrice,
40
41
  TokenTransferFee,
41
42
  TokenTransferFeeConfig,
42
43
  TokenTransferFeeOpts,
package/src/offchain.ts CHANGED
@@ -86,6 +86,7 @@ export const CCTP_FINALITY_STANDARD = 2000
86
86
  * @param destDomain - CCTP destination domain identifier
87
87
  * @param networkType - network type (mainnet or testnet)
88
88
  * @returns Array of fee tiers with finality thresholds and BPS fees
89
+ * @throws {@link CCIPUsdcBurnFeesError} if the HTTP request fails or the response is not a valid array of fee tiers
89
90
  */
90
91
  export async function getUsdcBurnFees(
91
92
  sourceDomain: number,
package/src/requests.ts CHANGED
@@ -310,6 +310,7 @@ const BLOCK_LOG_WINDOW_SIZE = 5000
310
310
  * @param range - Object containing minSeqNr and maxSeqNr for the batch range.
311
311
  * @param opts - Optional log filtering parameters.
312
312
  * @returns Array of messages in the batch.
313
+ * @throws {@link CCIPMessageBatchIncompleteError} if not all messages in the batch range could be found in source chain logs
313
314
  * @see {@link getVerifications} - Get commit report to determine batch range
314
315
  */
315
316
  export async function getMessagesInBatch<
@@ -409,12 +410,12 @@ export async function getMessagesInBatch<
409
410
  * import { sourceToDestTokenAddresses, EVMChain } from '@chainlink/ccip-sdk'
410
411
  *
411
412
  * const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
412
- * const tokenAmount = await sourceToDestTokenAddresses(
413
+ * const tokenAmount = await sourceToDestTokenAddresses({
413
414
  * source,
414
- * destChainSelector,
415
- * '0xOnRamp...',
416
- * { token: '0xLINK...', amount: 1000000000000000000n }
417
- * )
415
+ * onRamp: '0xOnRamp...',
416
+ * destChainSelector: 14767482510784806043n,
417
+ * sourceTokenAmount: { token: '0xLINK...', amount: 1000000000000000000n },
418
+ * })
418
419
  * console.log(`Pool: ${tokenAmount.sourcePoolAddress}`)
419
420
  * console.log(`Dest token: ${tokenAmount.destTokenAddress}`)
420
421
  * ```