@chainlink/ccip-sdk 1.1.1 → 1.2.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 (85) 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 +71 -16
  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 +106 -27
  41. package/dist/evm/index.js.map +1 -1
  42. package/dist/extra-args.d.ts +18 -8
  43. package/dist/extra-args.d.ts.map +1 -1
  44. package/dist/extra-args.js +6 -6
  45. package/dist/extra-args.js.map +1 -1
  46. package/dist/gas.d.ts +1 -1
  47. package/dist/gas.d.ts.map +1 -1
  48. package/dist/gas.js +7 -2
  49. package/dist/gas.js.map +1 -1
  50. package/dist/index.d.ts +2 -2
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/requests.d.ts +11 -5
  54. package/dist/requests.d.ts.map +1 -1
  55. package/dist/requests.js +4 -7
  56. package/dist/requests.js.map +1 -1
  57. package/dist/solana/index.d.ts +2 -2
  58. package/dist/solana/utils.js +2 -2
  59. package/dist/solana/utils.js.map +1 -1
  60. package/dist/ton/index.d.ts.map +1 -1
  61. package/dist/ton/index.js +34 -26
  62. package/dist/ton/index.js.map +1 -1
  63. package/dist/utils.d.ts +10 -4
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/utils.js +10 -4
  66. package/dist/utils.js.map +1 -1
  67. package/package.json +7 -7
  68. package/src/api/index.ts +271 -59
  69. package/src/api/types.ts +126 -1
  70. package/src/chain.ts +120 -42
  71. package/src/errors/codes.ts +1 -0
  72. package/src/errors/index.ts +1 -0
  73. package/src/errors/recovery.ts +2 -0
  74. package/src/errors/specialized.ts +33 -1
  75. package/src/evm/abi/OffRamp_2_0.ts +19 -21
  76. package/src/evm/abi/TokenPool_2_0.ts +0 -1
  77. package/src/evm/gas.ts +18 -20
  78. package/src/evm/index.ts +126 -26
  79. package/src/extra-args.ts +18 -8
  80. package/src/gas.ts +8 -3
  81. package/src/index.ts +4 -0
  82. package/src/requests.ts +18 -12
  83. package/src/solana/utils.ts +2 -2
  84. package/src/ton/index.ts +47 -26
  85. package/src/utils.ts +10 -4
package/src/evm/index.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  JsonRpcProvider,
13
13
  WebSocketProvider,
14
14
  ZeroAddress,
15
+ formatUnits,
15
16
  getAddress,
16
17
  hexlify,
17
18
  isBytesLike,
@@ -19,6 +20,7 @@ import {
19
20
  isHexString,
20
21
  keccak256,
21
22
  toBeHex,
23
+ toBigInt,
22
24
  zeroPadValue,
23
25
  } from 'ethers'
24
26
  import type { TypedContract } from 'ethers-abitype'
@@ -47,6 +49,7 @@ import {
47
49
  CCIPHasherVersionUnsupportedError,
48
50
  CCIPLogDataInvalidError,
49
51
  CCIPSourceChainUnsupportedError,
52
+ CCIPTokenDecimalsInsufficientError,
50
53
  CCIPTokenNotConfiguredError,
51
54
  CCIPTokenPoolChainConfigNotFoundError,
52
55
  CCIPTransactionNotFoundError,
@@ -692,7 +695,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
692
695
  offRampABI = OffRamp_1_6_ABI
693
696
  // falls through
694
697
  case CCIPVersion.V2_0: {
695
- offRampABI = OffRamp_2_0_ABI
698
+ offRampABI ??= OffRamp_2_0_ABI
696
699
  const contract = new Contract(
697
700
  offRamp,
698
701
  offRampABI,
@@ -1003,9 +1006,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1003
1006
  }
1004
1007
 
1005
1008
  /**
1006
- * {@inheritDoc Chain.generateUnsignedSendMessage}
1007
- * @returns Array containing 0 or more unsigned token approvals txs (if needed at the time of
1008
- * generation), followed by a ccipSend TransactionRequest
1009
+ * Generates unsigned EVM transactions for sending a CCIP message.
1010
+ *
1011
+ * @param opts - Send message options with sender address for populating transaction fields.
1012
+ * @returns Unsigned EVM transaction set containing 0 or more token approval txs
1013
+ * (if needed at the time of generation), followed by a ccipSend TransactionRequest.
1014
+ *
1015
+ * @remarks
1016
+ * When a token in `tokenAmounts` has `ZeroAddress` as its address, the corresponding
1017
+ * amount is included as native `value` in the `ccipSend` transaction instead of
1018
+ * going through the ERC-20 approve flow.
1009
1019
  */
1010
1020
  async generateUnsignedSendMessage(
1011
1021
  opts: Parameters<Chain['generateUnsignedSendMessage']>[0],
@@ -1138,7 +1148,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1138
1148
  async generateUnsignedExecute(
1139
1149
  opts: Parameters<Chain['generateUnsignedExecute']>[0],
1140
1150
  ): Promise<UnsignedEVMTx> {
1141
- const { offRamp, input } = await this.resolveExecuteOpts(opts)
1151
+ const { offRamp, input, gasLimit } = await this.resolveExecuteOpts(opts)
1142
1152
  if ('verifications' in input) {
1143
1153
  const contract = new Contract(
1144
1154
  offRamp,
@@ -1166,14 +1176,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1166
1176
  messageId,
1167
1177
  input.verifications.map(({ destAddress }) => destAddress),
1168
1178
  input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1169
- BigInt(opts.gasLimit ?? 0),
1179
+ BigInt(gasLimit ?? 0),
1170
1180
  { from: offRamp }, // internal method
1171
1181
  )
1172
1182
  const execTx = await contract.execute.populateTransaction(
1173
1183
  input.encodedMessage,
1174
1184
  input.verifications.map(({ destAddress }) => destAddress),
1175
1185
  input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1176
- BigInt(opts.gasLimit ?? 0),
1186
+ BigInt(gasLimit ?? 0),
1177
1187
  )
1178
1188
  execTx.gasLimit = txGasLimit + 40000n // plus `execute`'s overhead
1179
1189
  return { family: ChainFamily.EVM, transactions: [execTx] }
@@ -1190,7 +1200,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1190
1200
  interfaces.EVM2EVMOffRamp_v1_2,
1191
1201
  this.provider,
1192
1202
  ) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
1193
- const gasOverride = BigInt(opts.gasLimit ?? 0)
1203
+ const gasOverride = BigInt(gasLimit ?? 0)
1194
1204
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1195
1205
  {
1196
1206
  ...input,
@@ -1217,9 +1227,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1217
1227
  },
1218
1228
  [
1219
1229
  {
1220
- receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1230
+ receiverExecutionGasLimit: BigInt(gasLimit ?? 0),
1221
1231
  tokenGasOverrides: input.message.tokenAmounts.map(() =>
1222
- BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1232
+ BigInt(opts.tokensGasLimit ?? gasLimit ?? 0),
1223
1233
  ),
1224
1234
  },
1225
1235
  ],
@@ -1272,9 +1282,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1272
1282
  [
1273
1283
  [
1274
1284
  {
1275
- receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1285
+ receiverExecutionGasLimit: BigInt(gasLimit ?? 0),
1276
1286
  tokenGasOverrides: input.message.tokenAmounts.map(() =>
1277
- BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1287
+ BigInt(opts.tokensGasLimit ?? gasLimit ?? 0),
1278
1288
  ),
1279
1289
  },
1280
1290
  ],
@@ -1288,27 +1298,27 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1288
1298
 
1289
1299
  /* Executing a message for the first time has some hard try/catches on-chain
1290
1300
  * so we need to ensure some lower-bounds gasLimits */
1291
- let gasLimit = await this.provider.estimateGas(manualExecTx)
1301
+ let txGasLimit = await this.provider.estimateGas(manualExecTx)
1292
1302
  if (
1293
1303
  'gasLimit' in input.message &&
1294
1304
  input.message.gasLimit &&
1295
- gasLimit < input.message.gasLimit + 100000n
1305
+ txGasLimit < input.message.gasLimit + 100000n
1296
1306
  )
1297
1307
  // if message requested gasLimit, ensure execution more than 100k above requested, otherwise it's clearly a try/catch fail
1298
- gasLimit = BigInt(input.message.gasLimit) + 200000n
1299
- else if ('gasLimit' in input.message && !input.message.gasLimit && gasLimit < 240000n)
1308
+ txGasLimit = BigInt(input.message.gasLimit) + 200000n
1309
+ else if ('gasLimit' in input.message && !input.message.gasLimit && txGasLimit < 240000n)
1300
1310
  // if message didn't request gasLimit, ensure execution gasLimit is above 240k (empiric)
1301
- gasLimit = 240000n
1302
- manualExecTx.gasLimit = gasLimit
1311
+ txGasLimit = 240000n
1312
+ manualExecTx.gasLimit = txGasLimit
1303
1313
 
1304
1314
  return { family: ChainFamily.EVM, transactions: [manualExecTx] }
1305
1315
  }
1306
1316
 
1307
1317
  /**
1308
1318
  * {@inheritDoc Chain.execute}
1309
- * @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
1310
- * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
1311
- * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
1319
+ * @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer.
1320
+ * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm.
1321
+ * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts.
1312
1322
  */
1313
1323
  async execute(opts: Parameters<Chain['execute']>[0]) {
1314
1324
  const wallet = opts.wallet
@@ -1399,7 +1409,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1399
1409
  }
1400
1410
  }
1401
1411
 
1402
- /** {@inheritDoc Chain.getTokenPoolConfig} */
1412
+ /**
1413
+ * Fetches the token pool configuration for an EVM token pool contract.
1414
+ *
1415
+ * @param tokenPool - Token pool contract address.
1416
+ * @returns Token pool config containing token, router, typeAndVersion, and optionally minBlockConfirmations.
1417
+ *
1418
+ * @remarks
1419
+ * For pools with version \>= 2.0, also returns `minBlockConfirmations` for
1420
+ * Faster-Than-Finality (FTF) support. Pre-2.0 pools omit this field.
1421
+ */
1403
1422
  async getTokenPoolConfig(tokenPool: string): Promise<{
1404
1423
  token: string
1405
1424
  router: string
@@ -1444,7 +1463,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1444
1463
  )
1445
1464
  }
1446
1465
 
1447
- /** {@inheritDoc Chain.getTokenPoolRemotes} */
1466
+ /**
1467
+ * Fetches remote chain configurations for an EVM token pool contract.
1468
+ *
1469
+ * @param tokenPool - Token pool address on the current chain.
1470
+ * @param remoteChainSelector - Optional chain selector to filter results to a single destination.
1471
+ * @returns Record mapping chain names to {@link TokenPoolRemote} configs.
1472
+ *
1473
+ * @remarks
1474
+ * Handles 3 pool version branches:
1475
+ * - v1.5: single remote pool via `getRemotePool`, standard rate limiters.
1476
+ * - v1.6: multiple remote pools via `getRemotePools`, standard rate limiters.
1477
+ * - v2.0+: multiple remote pools plus FTF (Faster-Than-Finality) rate limiters
1478
+ * (`customBlockConfirmationsOutboundRateLimiterState` / `customBlockConfirmationsInboundRateLimiterState`).
1479
+ *
1480
+ * @throws {@link CCIPTokenPoolChainConfigNotFoundError} if remote token is not configured for a chain.
1481
+ */
1448
1482
  async getTokenPoolRemotes(
1449
1483
  tokenPool: string,
1450
1484
  remoteChainSelector?: bigint,
@@ -1729,10 +1763,76 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1729
1763
  override async estimateReceiveExecution(
1730
1764
  opts: Parameters<NonNullable<Chain['estimateReceiveExecution']>>[0],
1731
1765
  ): Promise<number> {
1766
+ const convertAmounts = (
1767
+ tokenAmounts?: readonly ((
1768
+ | { token: string }
1769
+ | { destTokenAddress: string; extraData?: string }
1770
+ ) & {
1771
+ amount: bigint
1772
+ })[],
1773
+ ) =>
1774
+ !tokenAmounts
1775
+ ? undefined
1776
+ : Promise.all(
1777
+ tokenAmounts.map(async (ta) => {
1778
+ if (!('destTokenAddress' in ta)) return ta
1779
+ let amount = ta.amount
1780
+ if (isHexString(ta.extraData, 32)) {
1781
+ // extraData is source token decimals in most pools derived from standard TP contracts;
1782
+ // we can identify for it being exactly 32B and being a small integer; otherwise, assume same decimals
1783
+ const sourceDecimals = toBigInt(ta.extraData)
1784
+ if (0 < sourceDecimals && sourceDecimals <= 36) {
1785
+ const { decimals: destDecimals } = await this.getTokenInfo(ta.destTokenAddress)
1786
+ amount =
1787
+ (amount * BigInt(10) ** BigInt(destDecimals)) /
1788
+ BigInt(10) ** BigInt(sourceDecimals)
1789
+ if (amount === 0n)
1790
+ throw new CCIPTokenDecimalsInsufficientError(
1791
+ ta.destTokenAddress,
1792
+ destDecimals,
1793
+ this.network.name,
1794
+ formatUnits(amount, sourceDecimals),
1795
+ )
1796
+ }
1797
+ }
1798
+ return { token: ta.destTokenAddress, amount }
1799
+ }),
1800
+ )
1801
+
1802
+ let opts_
1803
+ if (!('offRamp' in opts)) {
1804
+ const { lane, message, metadata } = await this.getMessageById(opts.messageId)
1805
+
1806
+ const offRamp =
1807
+ ('offRampAddress' in message && message.offRampAddress) ||
1808
+ metadata?.offRamp ||
1809
+ (await this.apiClient!.getExecutionInput(opts.messageId)).offRamp
1810
+
1811
+ opts_ = {
1812
+ offRamp,
1813
+ message: {
1814
+ sourceChainSelector: lane.sourceChainSelector,
1815
+ messageId: message.messageId,
1816
+ receiver: message.receiver,
1817
+ sender: message.sender,
1818
+ data: message.data,
1819
+ destTokenAmounts: await convertAmounts(message.tokenAmounts),
1820
+ },
1821
+ }
1822
+ } else {
1823
+ opts_ = {
1824
+ ...opts,
1825
+ message: {
1826
+ ...opts.message,
1827
+ destTokenAmounts: await convertAmounts(opts.message.destTokenAmounts),
1828
+ },
1829
+ }
1830
+ }
1831
+
1732
1832
  const destRouter = await this.getRouterForOffRamp(
1733
- opts.offRamp,
1734
- opts.message.sourceChainSelector,
1833
+ opts_.offRamp,
1834
+ opts_.message.sourceChainSelector,
1735
1835
  )
1736
- return estimateExecGas({ provider: this.provider, router: destRouter, ...opts })
1836
+ return estimateExecGas({ provider: this.provider, router: destRouter, ...opts_ })
1737
1837
  }
1738
1838
  }
package/src/extra-args.ts CHANGED
@@ -68,7 +68,7 @@ export type EVMExtraArgsV2 = EVMExtraArgsV1 & {
68
68
  export type GenericExtraArgsV3 = {
69
69
  /** Gas limit for execution on the destination chain (uint32). */
70
70
  gasLimit: bigint
71
- /** Number of block confirmations required. */
71
+ /** Number of source-chain block confirmations to wait before relaying the message. */
72
72
  blockConfirmations: number
73
73
  /** Cross-chain verifier addresses (EVM addresses). */
74
74
  ccvs: string[]
@@ -132,7 +132,17 @@ export type SuiExtraArgsV1 = EVMExtraArgsV2 & {
132
132
  }
133
133
 
134
134
  /**
135
- * Union type of all supported extra arguments formats.
135
+ * Union of all supported extra arguments formats for CCIP messages.
136
+ *
137
+ * The SDK auto-detects the correct variant based on the fields provided:
138
+ * - {@link EVMExtraArgsV1} - EVM legacy (gasLimit only)
139
+ * - {@link EVMExtraArgsV2} - EVM with out-of-order execution support
140
+ * - {@link GenericExtraArgsV3} - Generic V3 with minimum block confirmations, cross-chain verifiers, custom executor, and per-token receiver/args
141
+ * - {@link SVMExtraArgsV1} - Solana (compute units, accounts)
142
+ * - {@link SuiExtraArgsV1} - Sui (gas limit, receiver object IDs)
143
+ *
144
+ * @see {@link encodeExtraArgs} - Encode extra arguments for on-chain use.
145
+ * @see {@link decodeExtraArgs} - Decode extra arguments from bytes.
136
146
  */
137
147
  export type ExtraArgs =
138
148
  | EVMExtraArgsV1
@@ -146,10 +156,10 @@ export type ExtraArgs =
146
156
  * The args are *to* a dest network, but are encoded as a message *from* this source chain.
147
157
  * E.g. Solana uses Borsh to encode extraArgs in its produced requests, even those targeting EVM.
148
158
  *
149
- * @param args - Extra arguments to encode
150
- * @param from - Source chain family for encoding format (defaults to EVM)
151
- * @returns Encoded extra arguments as hex string
152
- * @throws {@link CCIPChainFamilyUnsupportedError} if chain family not supported
159
+ * @param args - Extra arguments to encode.
160
+ * @param from - Source chain family for encoding format (defaults to EVM).
161
+ * @returns Encoded extra arguments as hex string.
162
+ * @throws {@link CCIPChainFamilyUnsupportedError} if chain family not supported.
153
163
  *
154
164
  * @example
155
165
  * ```typescript
@@ -175,8 +185,8 @@ export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily
175
185
  * @param data - Extra arguments bytearray data.
176
186
  * @param from - Optional chain family to narrow decoding attempts.
177
187
  * @returns Extra arguments object if found, undefined otherwise.
178
- * @throws {@link CCIPChainFamilyUnsupportedError} if specified chain family not supported
179
- * @throws {@link CCIPExtraArgsParseError} if data cannot be parsed as valid extra args
188
+ * @throws {@link CCIPChainFamilyUnsupportedError} if specified chain family not supported.
189
+ * @throws {@link CCIPExtraArgsParseError} if data cannot be parsed as valid extra args.
180
190
  *
181
191
  * @example
182
192
  * ```typescript
package/src/gas.ts CHANGED
@@ -28,7 +28,7 @@ export type EstimateMessageInput = {
28
28
  data?: BytesLike
29
29
  /**
30
30
  * optional tokenAmounts; `amount` with either source `token` (as in MessageInput) or
31
- * `{ sourceTokenAddress?, sourcePoolAddress, destTokenAddress }` (as in v1.5..v1.7 tokenAmounts)
31
+ * `{ sourceTokenAddress?, sourcePoolAddress, destTokenAddress }` (as in v1.5..v2.0 tokenAmounts)
32
32
  * can be provided
33
33
  */
34
34
  tokenAmounts?: readonly ({
@@ -122,7 +122,12 @@ export async function estimateReceiveExecution({
122
122
  const tokenAmount =
123
123
  'destTokenAddress' in ta
124
124
  ? ta
125
- : await sourceToDestTokenAddresses(source, dest.network.chainSelector, onRamp, ta)
125
+ : await sourceToDestTokenAddresses({
126
+ source,
127
+ onRamp,
128
+ destChainSelector: dest.network.chainSelector,
129
+ sourceTokenAmount: ta,
130
+ })
126
131
  const sourceTokenAddress =
127
132
  'token' in ta
128
133
  ? ta.token
@@ -147,10 +152,10 @@ export async function estimateReceiveExecution({
147
152
  }),
148
153
  )
149
154
  return dest.estimateReceiveExecution({
150
- receiver: message.receiver,
151
155
  offRamp,
152
156
  message: {
153
157
  messageId: message.messageId ?? hexlify(randomBytes(32)),
158
+ receiver: message.receiver,
154
159
  sender: message.sender,
155
160
  data: message.data,
156
161
  sourceChainSelector: source.network.chainSelector,
package/src/index.ts CHANGED
@@ -12,6 +12,9 @@ export type {
12
12
  APIErrorResponse,
13
13
  CCIPAPIClientContext,
14
14
  LaneLatencyResponse,
15
+ MessageSearchFilters,
16
+ MessageSearchPage,
17
+ MessageSearchResult,
15
18
  } from './api/index.ts'
16
19
  export {
17
20
  CCIPAPIClient,
@@ -41,6 +44,7 @@ export {
41
44
  type EVMExtraArgsV1,
42
45
  type EVMExtraArgsV2,
43
46
  type ExtraArgs,
47
+ type GenericExtraArgsV3,
44
48
  type SVMExtraArgsV1,
45
49
  type SuiExtraArgsV1,
46
50
  decodeExtraArgs,
package/src/requests.ts CHANGED
@@ -89,9 +89,9 @@ function decodeJsonMessage(data: Record<string, unknown> | undefined) {
89
89
  k?.match(/(selector|amount|nonce|number|limit|bitmap|juels)$/i)
90
90
  ? BigInt(v as string | number | bigint)
91
91
  : k?.match(/(^dest.*address)|(receiver|offramp|accounts)/i)
92
- ? decodeAddress(v as BytesLike, destFamily)
92
+ ? decodeAddress((typeof v === 'bigint' ? v.toString() : v) as BytesLike, destFamily)
93
93
  : k?.match(/((source.*address)|sender|issuer|origin|onramp|(feetoken$)|(token.*address$))/i)
94
- ? decodeAddress(v as BytesLike, sourceFamily)
94
+ ? decodeAddress((typeof v === 'bigint' ? v.toString() : v) as BytesLike, sourceFamily)
95
95
  : v instanceof Uint8Array ||
96
96
  (Array.isArray(v) && v.length >= 4 && v.every((e) => typeof e === 'number'))
97
97
  ? hexlify(getDataBytes(v as readonly number[]))
@@ -456,10 +456,7 @@ export async function* getMessagesForSender(
456
456
  * Resolves token routing by querying the TokenAdminRegistry and TokenPool
457
457
  * to find the corresponding destination chain token.
458
458
  *
459
- * @param source - Source chain instance
460
- * @param destChainSelector - Destination chain selector
461
- * @param onRamp - OnRamp contract address
462
- * @param sourceTokenAmount - Token amount object containing `token` and `amount`
459
+ * @param opts - options to convert source to dest token addresses
463
460
  * @returns Extended token amount with `sourcePoolAddress`, `sourceTokenAddress`, and `destTokenAddress`
464
461
  *
465
462
  * @throws {@link CCIPTokenNotInRegistryError} if token is not registered in TokenAdminRegistry
@@ -479,12 +476,21 @@ export async function* getMessagesForSender(
479
476
  * console.log(`Dest token: ${tokenAmount.destTokenAddress}`)
480
477
  * ```
481
478
  */
482
- export async function sourceToDestTokenAddresses<S extends { token: string }>(
483
- source: Chain,
484
- destChainSelector: bigint,
485
- onRamp: string,
486
- sourceTokenAmount: S,
487
- ): Promise<
479
+ export async function sourceToDestTokenAddresses<S extends { token: string }>({
480
+ source,
481
+ onRamp,
482
+ destChainSelector,
483
+ sourceTokenAmount,
484
+ }: {
485
+ /** Source chain instance */
486
+ source: Chain
487
+ /** OnRamp contract address */
488
+ onRamp: string
489
+ /** Destination chain selector */
490
+ destChainSelector: bigint
491
+ /** Token amount object containing `token` and `amount` */
492
+ sourceTokenAmount: S
493
+ }): Promise<
488
494
  S & {
489
495
  sourcePoolAddress: string
490
496
  sourceTokenAddress: string
@@ -27,7 +27,7 @@ import {
27
27
  CCIPTransactionNotFinalizedError,
28
28
  } from '../errors/index.ts'
29
29
  import type { ChainLog, WithLogger } from '../types.ts'
30
- import { getDataBytes, sleep } from '../utils.ts'
30
+ import { bigIntReplacer, getDataBytes, sleep } from '../utils.ts'
31
31
  import type { IDL as BASE_TOKEN_POOL_IDL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
32
32
  import type { UnsignedSolanaTx, Wallet } from './types.ts'
33
33
  import type { RateLimiterState } from '../chain.ts'
@@ -347,7 +347,7 @@ export async function simulateTransaction(
347
347
  throw new SendTransactionError({
348
348
  action: 'simulate',
349
349
  signature: '',
350
- transactionMessage: JSON.stringify(result.value.err),
350
+ transactionMessage: JSON.stringify(result.value.err, bigIntReplacer),
351
351
  logs: result.value.logs!,
352
352
  })
353
353
  }
package/src/ton/index.ts CHANGED
@@ -3,7 +3,18 @@ import { Buffer } from 'buffer'
3
3
  import { type Transaction, Address, Cell, beginCell, toNano } from '@ton/core'
4
4
  import { TonClient } from '@ton/ton'
5
5
  import { type AxiosAdapter, getAdapter } from 'axios'
6
- import { type BytesLike, hexlify, isBytesLike, isHexString, toBeArray, toBeHex } from 'ethers'
6
+ import {
7
+ type BytesLike,
8
+ concat,
9
+ dataLength,
10
+ dataSlice,
11
+ hexlify,
12
+ isBytesLike,
13
+ isHexString,
14
+ toBeArray,
15
+ toBeHex,
16
+ toBigInt,
17
+ } from 'ethers'
7
18
  import { type Memoized, memoize } from 'micro-memoize'
8
19
  import type { PickDeep } from 'type-fest'
9
20
 
@@ -45,6 +56,7 @@ import {
45
56
  bytesToBuffer,
46
57
  createRateLimitedFetch,
47
58
  decodeAddress,
59
+ getDataBytes,
48
60
  networkInfo,
49
61
  parseTypeAndVersion,
50
62
  sleep,
@@ -64,6 +76,16 @@ function isTvmError(error: unknown): error is Error & { exitCode: number } {
64
76
  return error instanceof Error && 'exitCode' in error && typeof error.exitCode === 'number'
65
77
  }
66
78
 
79
+ function bitsToBytes(bits: string): Uint8Array {
80
+ return Uint8Array.from(bits.match(/.{1,8}/g)!.map((byte) => parseInt(byte.padEnd(8, '0'), 2)))
81
+ }
82
+
83
+ function bytesToBits(bytes: Uint8Array): string {
84
+ return Array.from(bytes)
85
+ .map((B) => B.toString(2).padStart(8, '0'))
86
+ .join('')
87
+ }
88
+
67
89
  /**
68
90
  * TON chain implementation supporting TON networks.
69
91
  *
@@ -702,14 +724,14 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
702
724
  */
703
725
  static encodeExtraArgs(args: ExtraArgs): string {
704
726
  if ('gasLimit' in args && 'allowOutOfOrderExecution' in args) {
705
- const cell = beginCell()
706
- .storeUint(Number(EVMExtraArgsV2Tag), 32) // magic tag
707
- .storeUint(args.gasLimit, 256) // gasLimit
708
- .storeBit(args.allowOutOfOrderExecution) // bool
709
- .endCell()
710
-
711
- // Return full BOC including headers
712
- return '0x' + cell.toBoc().toString('hex')
727
+ return concat([
728
+ EVMExtraArgsV2Tag,
729
+ bitsToBytes(
730
+ '1' +
731
+ bytesToBits(toBeArray(args.gasLimit, 32)) +
732
+ (args.allowOutOfOrderExecution ? '1' : '0'),
733
+ ),
734
+ ])
713
735
  }
714
736
  return '0x'
715
737
  }
@@ -729,25 +751,24 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
729
751
  static decodeExtraArgs(
730
752
  extraArgs: BytesLike,
731
753
  ): (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' }) | undefined {
732
- const data = bytesToBuffer(extraArgs)
733
-
734
- try {
735
- // Parse BOC format to extract cell data
736
- const cell = Cell.fromBoc(data)[0]!
737
- const slice = cell.beginParse()
754
+ if (dataSlice(extraArgs, 0, 4) !== EVMExtraArgsV2Tag || dataLength(extraArgs) < 5) return
755
+ const data = getDataBytes(extraArgs).subarray(4)
756
+ let bits = bytesToBits(data)
757
+ let gasLimit = 0n
758
+ if (bits[0] === '1') {
759
+ if (bits.length < 1 + 32 * 8) return
760
+ gasLimit = toBigInt(bitsToBytes(bits.substring(1, 1 + 32 * 8)))
761
+ bits = bits.substring(1 + 32 * 8)
762
+ } else {
763
+ bits = bits.substring(1)
764
+ }
738
765
 
739
- // Load and verify magic tag to ensure correct extra args type
740
- const magicTag = slice.loadUint(32)
741
- if (magicTag !== Number(EVMExtraArgsV2Tag)) return undefined
766
+ const allowOutOfOrderExecution = bits[0] === '1'
742
767
 
743
- return {
744
- _tag: 'EVMExtraArgsV2',
745
- gasLimit: slice.loadUintBig(256),
746
- allowOutOfOrderExecution: slice.loadBit(),
747
- }
748
- } catch {
749
- // Return undefined for any parsing errors (invalid BOC, malformed data, etc.)
750
- return undefined
768
+ return {
769
+ _tag: 'EVMExtraArgsV2',
770
+ gasLimit,
771
+ allowOutOfOrderExecution,
751
772
  }
752
773
  }
753
774
 
package/src/utils.ts CHANGED
@@ -190,10 +190,16 @@ export const networkInfo = memoize(function networkInfo_(
190
190
 
191
191
  const BLOCK_RANGE = 10_000
192
192
  /**
193
- * Generates exclusive block ranges [fromBlock, toBlock]
194
- * If startBlock is given, moves forward from there (up to latestBlock),
195
- * Otherwise, moves backwards down to genesis (you probably want to break/return before that)
196
- **/
193
+ * Generates block ranges for paginated log queries.
194
+ *
195
+ * @param params - Range parameters:
196
+ * - `singleBlock` - yields a single `{ fromBlock, toBlock }` for that block.
197
+ * - `endBlock` + optional `startBlock` - if `startBlock` is given, moves forward
198
+ * from there up to `endBlock`; otherwise moves backward from `endBlock` to genesis.
199
+ * - `page` - step size per range (default 10 000).
200
+ * @returns Generator of `{ fromBlock, toBlock }` pairs, optionally with a `progress` percentage
201
+ * string when iterating forward.
202
+ */
197
203
  export function* blockRangeGenerator(
198
204
  params: { page?: number } & ({ endBlock: number; startBlock?: number } | { singleBlock: number }),
199
205
  ) {