@chainlink/ccip-sdk 1.2.1 → 1.3.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 (108) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +10 -20
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/aptos/index.d.ts +2 -2
  6. package/dist/aptos/index.d.ts.map +1 -1
  7. package/dist/aptos/index.js +1 -1
  8. package/dist/aptos/index.js.map +1 -1
  9. package/dist/chain.d.ts +75 -2
  10. package/dist/chain.d.ts.map +1 -1
  11. package/dist/chain.js +19 -0
  12. package/dist/chain.js.map +1 -1
  13. package/dist/errors/codes.d.ts +1 -0
  14. package/dist/errors/codes.d.ts.map +1 -1
  15. package/dist/errors/codes.js +1 -0
  16. package/dist/errors/codes.js.map +1 -1
  17. package/dist/errors/index.d.ts +1 -1
  18. package/dist/errors/index.d.ts.map +1 -1
  19. package/dist/errors/index.js +1 -1
  20. package/dist/errors/index.js.map +1 -1
  21. package/dist/errors/recovery.d.ts.map +1 -1
  22. package/dist/errors/recovery.js +1 -0
  23. package/dist/errors/recovery.js.map +1 -1
  24. package/dist/errors/specialized.d.ts +8 -0
  25. package/dist/errors/specialized.d.ts.map +1 -1
  26. package/dist/errors/specialized.js +10 -0
  27. package/dist/errors/specialized.js.map +1 -1
  28. package/dist/evm/abi/CCTPVerifier_2_0.d.ts +1118 -0
  29. package/dist/evm/abi/CCTPVerifier_2_0.d.ts.map +1 -0
  30. package/dist/evm/abi/CCTPVerifier_2_0.js +1147 -0
  31. package/dist/evm/abi/CCTPVerifier_2_0.js.map +1 -0
  32. package/dist/evm/abi/USDCTokenPoolProxy_2_0.d.ts +825 -0
  33. package/dist/evm/abi/USDCTokenPoolProxy_2_0.d.ts.map +1 -0
  34. package/dist/evm/abi/USDCTokenPoolProxy_2_0.js +873 -0
  35. package/dist/evm/abi/USDCTokenPoolProxy_2_0.js.map +1 -0
  36. package/dist/evm/abi/VersionedVerifierResolver_2_0.d.ts +350 -0
  37. package/dist/evm/abi/VersionedVerifierResolver_2_0.d.ts.map +1 -0
  38. package/dist/evm/abi/VersionedVerifierResolver_2_0.js +370 -0
  39. package/dist/evm/abi/VersionedVerifierResolver_2_0.js.map +1 -0
  40. package/dist/evm/const.d.ts +3 -0
  41. package/dist/evm/const.d.ts.map +1 -1
  42. package/dist/evm/const.js +8 -0
  43. package/dist/evm/const.js.map +1 -1
  44. package/dist/evm/index.d.ts +24 -3
  45. package/dist/evm/index.d.ts.map +1 -1
  46. package/dist/evm/index.js +193 -7
  47. package/dist/evm/index.js.map +1 -1
  48. package/dist/index.d.ts +3 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/offchain.d.ts +27 -0
  53. package/dist/offchain.d.ts.map +1 -1
  54. package/dist/offchain.js +44 -2
  55. package/dist/offchain.js.map +1 -1
  56. package/dist/requests.d.ts +1 -25
  57. package/dist/requests.d.ts.map +1 -1
  58. package/dist/requests.js +0 -57
  59. package/dist/requests.js.map +1 -1
  60. package/dist/solana/index.d.ts +2 -2
  61. package/dist/solana/index.d.ts.map +1 -1
  62. package/dist/solana/index.js +6 -10
  63. package/dist/solana/index.js.map +1 -1
  64. package/dist/solana/utils.d.ts +1 -1
  65. package/dist/solana/utils.d.ts.map +1 -1
  66. package/dist/solana/utils.js +12 -14
  67. package/dist/solana/utils.js.map +1 -1
  68. package/dist/sui/index.d.ts +2 -2
  69. package/dist/sui/index.d.ts.map +1 -1
  70. package/dist/sui/index.js +1 -1
  71. package/dist/sui/index.js.map +1 -1
  72. package/dist/ton/index.d.ts +2 -2
  73. package/dist/ton/index.d.ts.map +1 -1
  74. package/dist/ton/index.js +28 -49
  75. package/dist/ton/index.js.map +1 -1
  76. package/dist/ton/send.d.ts +13 -1
  77. package/dist/ton/send.d.ts.map +1 -1
  78. package/dist/ton/send.js +16 -16
  79. package/dist/ton/send.js.map +1 -1
  80. package/dist/types.d.ts +1 -1
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/utils.d.ts +16 -0
  83. package/dist/utils.d.ts.map +1 -1
  84. package/dist/utils.js +38 -4
  85. package/dist/utils.js.map +1 -1
  86. package/package.json +4 -4
  87. package/src/api/index.ts +9 -23
  88. package/src/aptos/index.ts +5 -1
  89. package/src/chain.ts +85 -2
  90. package/src/errors/codes.ts +1 -0
  91. package/src/errors/index.ts +1 -0
  92. package/src/errors/recovery.ts +2 -0
  93. package/src/errors/specialized.ts +15 -0
  94. package/src/evm/abi/CCTPVerifier_2_0.ts +1146 -0
  95. package/src/evm/abi/USDCTokenPoolProxy_2_0.ts +872 -0
  96. package/src/evm/abi/VersionedVerifierResolver_2_0.ts +369 -0
  97. package/src/evm/const.ts +8 -0
  98. package/src/evm/index.ts +262 -8
  99. package/src/index.ts +6 -2
  100. package/src/offchain.ts +53 -1
  101. package/src/requests.ts +1 -59
  102. package/src/solana/index.ts +11 -11
  103. package/src/solana/utils.ts +24 -17
  104. package/src/sui/index.ts +2 -1
  105. package/src/ton/index.ts +41 -56
  106. package/src/ton/send.ts +20 -21
  107. package/src/types.ts +1 -1
  108. package/src/utils.ts +52 -4
package/src/ton/index.ts CHANGED
@@ -1,26 +1,38 @@
1
1
  import { Buffer } from 'buffer'
2
2
 
3
- import { type Transaction, Address, Cell, beginCell, toNano } from '@ton/core'
3
+ import {
4
+ type Transaction,
5
+ Address,
6
+ BitReader,
7
+ BitString,
8
+ Cell,
9
+ Slice,
10
+ beginCell,
11
+ toNano,
12
+ } from '@ton/core'
4
13
  import { TonClient } from '@ton/ton'
5
14
  import { type AxiosAdapter, getAdapter } from 'axios'
6
15
  import {
7
16
  type BytesLike,
8
- concat,
9
- dataLength,
10
17
  dataSlice,
11
18
  hexlify,
12
19
  isBytesLike,
13
20
  isHexString,
14
21
  toBeArray,
15
22
  toBeHex,
16
- toBigInt,
17
23
  } from 'ethers'
18
24
  import { type Memoized, memoize } from 'micro-memoize'
19
25
  import type { PickDeep } from 'type-fest'
20
26
 
21
27
  import { streamTransactionsForAddress } from './logs.ts'
22
- import { generateUnsignedCcipSend, getFee as getFeeImpl } from './send.ts'
23
- import { type ChainContext, type GetBalanceOpts, type LogFilter, Chain } from '../chain.ts'
28
+ import { encodeExtraArgsCell, generateUnsignedCcipSend, getFee as getFeeImpl } from './send.ts'
29
+ import {
30
+ type ChainContext,
31
+ type GetBalanceOpts,
32
+ type LogFilter,
33
+ type TokenTransferFeeOpts,
34
+ Chain,
35
+ } from '../chain.ts'
24
36
  import {
25
37
  CCIPArgumentInvalidError,
26
38
  CCIPExecutionReportChainMismatchError,
@@ -56,7 +68,6 @@ import {
56
68
  bytesToBuffer,
57
69
  createRateLimitedFetch,
58
70
  decodeAddress,
59
- getDataBytes,
60
71
  networkInfo,
61
72
  parseTypeAndVersion,
62
73
  sleep,
@@ -76,16 +87,6 @@ function isTvmError(error: unknown): error is Error & { exitCode: number } {
76
87
  return error instanceof Error && 'exitCode' in error && typeof error.exitCode === 'number'
77
88
  }
78
89
 
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
-
89
90
  /**
90
91
  * TON chain implementation supporting TON networks.
91
92
  *
@@ -668,24 +669,14 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
668
669
 
669
670
  // Load extraArgs from ref 2
670
671
  const extraArgsCell = bodySlice.loadRef()
671
- const extraArgsSlice = extraArgsCell.beginParse()
672
-
673
- // Read tag (32 bits)
674
- const extraArgsTag = extraArgsSlice.loadUint(32)
675
- if (extraArgsTag !== Number(EVMExtraArgsV2Tag)) return undefined
676
-
677
- // Read gasLimit (maybe uint256): 1 bit flag + 256 bits if present
678
- const hasGasLimit = extraArgsSlice.loadBit()
679
- const gasLimit = hasGasLimit ? extraArgsSlice.loadUintBig(256) : 0n
680
-
681
- // Read allowOutOfOrderExecution (1 bit)
682
- const allowOutOfOrderExecution = extraArgsSlice.loadBit()
683
672
 
684
673
  // Build extraArgs as raw hex matching reference format
685
- const extraArgs = '0x' + extraArgsCell.toBoc().toString('hex')
674
+ const extraArgs = '0x' + extraArgsCell.bits.toString().toLowerCase().replace('_', '0')
675
+ const parsed = this.decodeExtraArgs(extraArgs)
676
+ if (!parsed) return
677
+ const { _tag, ...extraArgsObj } = parsed
686
678
 
687
679
  // Load tokenAmounts from ref 3
688
- const _tokenAmountsCell = bodySlice.loadRef()
689
680
  const tokenAmounts: CCIPMessage_V1_6_EVM['tokenAmounts'] = [] // TODO: FIXME: parse when implemented
690
681
 
691
682
  // Load feeToken (inline address in body)
@@ -704,8 +695,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
704
695
  feeTokenAmount,
705
696
  feeValueJuels,
706
697
  extraArgs,
707
- gasLimit,
708
- allowOutOfOrderExecution,
698
+ ...extraArgsObj,
709
699
  }
710
700
  } catch {
711
701
  return undefined
@@ -723,17 +713,8 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
723
713
  * @returns Hex string of BOC-encoded extra args (0x-prefixed)
724
714
  */
725
715
  static encodeExtraArgs(args: ExtraArgs): string {
726
- if ('gasLimit' in args && 'allowOutOfOrderExecution' in args) {
727
- return concat([
728
- EVMExtraArgsV2Tag,
729
- bitsToBytes(
730
- '1' +
731
- bytesToBits(toBeArray(args.gasLimit, 32)) +
732
- (args.allowOutOfOrderExecution ? '1' : '0'),
733
- ),
734
- ])
735
- }
736
- return '0x'
716
+ const cell = encodeExtraArgsCell(args)
717
+ return '0x' + cell.bits.toString().toLowerCase().replace('_', '0')
737
718
  }
738
719
 
739
720
  /**
@@ -751,19 +732,23 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
751
732
  static decodeExtraArgs(
752
733
  extraArgs: BytesLike,
753
734
  ): (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' }) | undefined {
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)
735
+ let bytes
736
+ try {
737
+ bytes = bytesToBuffer(extraArgs)
738
+
739
+ // If the data is BOC-wrapped (starts with TON BOC magic 0xb5ee9c72), extract bits
740
+ if (dataSlice(bytes, 0, 4) !== EVMExtraArgsV2Tag) {
741
+ const cell = Cell.fromBoc(bytes)[0]!
742
+ bytes = bytesToBuffer('0x' + cell.bits.toString().toLowerCase().replace('_', '0'))
743
+ }
744
+ if (dataSlice(bytes, 0, 4) !== EVMExtraArgsV2Tag) return
745
+ } catch {
746
+ return
764
747
  }
765
748
 
766
- const allowOutOfOrderExecution = bits[0] === '1'
749
+ const slice = new Slice(new BitReader(new BitString(bytes, 32, bytes.length * 8)), [])
750
+ const gasLimit = slice.loadMaybeUintBig(256) ?? 0n
751
+ const allowOutOfOrderExecution = slice.loadBit()
767
752
 
768
753
  return {
769
754
  _tag: 'EVMExtraArgsV2',
@@ -1226,7 +1211,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
1226
1211
  * {@inheritDoc Chain.getTokenPoolConfig}
1227
1212
  * @throws {@link CCIPNotImplementedError} always (not implemented for TON)
1228
1213
  */
1229
- async getTokenPoolConfig(_tokenPool: string): Promise<never> {
1214
+ async getTokenPoolConfig(_tokenPool: string, _feeOpts?: TokenTransferFeeOpts): Promise<never> {
1230
1215
  return Promise.reject(new CCIPNotImplementedError('getTokenPoolConfig'))
1231
1216
  }
1232
1217
 
package/src/ton/send.ts CHANGED
@@ -3,10 +3,10 @@ import { type TonClient, Address } from '@ton/ton'
3
3
  import { zeroPadValue } from 'ethers'
4
4
 
5
5
  import type { UnsignedTONTx } from './types.ts'
6
- import { CCIPError, CCIPErrorCode } from '../errors/index.ts'
7
- import { EVMExtraArgsV2Tag } from '../extra-args.ts'
8
- import type { AnyMessage, WithLogger } from '../types.ts'
9
- import { bytesToBuffer, getDataBytes } from '../utils.ts'
6
+ import { CCIPError, CCIPErrorCode, CCIPExtraArgsInvalidError } from '../errors/index.ts'
7
+ import { type ExtraArgs, EVMExtraArgsV2Tag } from '../extra-args.ts'
8
+ import { type AnyMessage, type WithLogger, ChainFamily } from '../types.ts'
9
+ import { bigIntReplacer, bytesToBuffer, getDataBytes } from '../utils.ts'
10
10
 
11
11
  /** Opcode for Router ccipSend operation */
12
12
  export const CCIP_SEND_OPCODE = 0x31768d95
@@ -51,28 +51,27 @@ function encodeTokenAmounts(
51
51
  * Format per chainlink-ton TL-B:
52
52
  * - tag: 32-bit opcode (0x181dcf10)
53
53
  * - gasLimit: Maybe<uint256> (1 bit flag + 256 bits if present)
54
- * - allowOutOfOrderExecution: 1 bit (must be true)
54
+ * - allowOutOfOrderExecution: 1 bit
55
+ * @param extraArgs - Extra arguments for CCIP message
56
+ * @returns Cell encoding the extra arguments
55
57
  */
56
- function encodeExtraArgsCell(extraArgs: AnyMessage['extraArgs']): Cell {
57
- const allowOutOfOrderExecution = true
58
-
59
- let gasLimit = 0n
60
- let hasGasLimit = false
61
-
62
- if ('gasLimit' in extraArgs && extraArgs.gasLimit > 0n) {
63
- hasGasLimit = true
58
+ export function encodeExtraArgsCell(extraArgs: ExtraArgs): Cell {
59
+ if (
60
+ Object.keys(extraArgs).filter((k) => k !== '_tag').length !== 2 ||
61
+ !('gasLimit' in extraArgs && 'allowOutOfOrderExecution' in extraArgs)
62
+ )
63
+ throw new CCIPExtraArgsInvalidError(ChainFamily.TON, JSON.stringify(extraArgs, bigIntReplacer))
64
+
65
+ let gasLimit: bigint | null = null
66
+ if (extraArgs.gasLimit > 0n) {
64
67
  gasLimit = extraArgs.gasLimit
65
68
  }
66
69
 
67
- const builder = beginCell()
68
- .storeUint(Number(EVMExtraArgsV2Tag), 32) // 0x181dcf10
69
- .storeBit(hasGasLimit)
70
-
71
- if (hasGasLimit) {
72
- builder.storeUint(gasLimit, 256)
73
- }
70
+ const builder = beginCell().storeUint(Number(EVMExtraArgsV2Tag), 32) // 0x181dcf10
71
+ builder.storeMaybeUint(gasLimit, 256)
72
+ builder.storeBit(extraArgs.allowOutOfOrderExecution)
74
73
 
75
- return builder.storeBit(allowOutOfOrderExecution).endCell()
74
+ return builder.endCell()
76
75
  }
77
76
 
78
77
  /**
package/src/types.ts CHANGED
@@ -470,7 +470,7 @@ export type AnyMessage = {
470
470
  * }
471
471
  * ```
472
472
  */
473
- export type MessageInput = Partial<AnyMessage> & {
473
+ export type MessageInput = Partial<Omit<AnyMessage, 'extraArgs'>> & {
474
474
  receiver: AnyMessage['receiver']
475
475
  extraArgs?: Partial<ExtraArgs>
476
476
  fee?: bigint
package/src/utils.ts CHANGED
@@ -16,12 +16,14 @@ import yaml from 'yaml'
16
16
 
17
17
  import type { Chain, ChainStatic } from './chain.ts'
18
18
  import {
19
+ CCIPAbortError,
19
20
  CCIPBlockBeforeTimestampNotFoundError,
20
21
  CCIPChainFamilyUnsupportedError,
21
22
  CCIPChainNotFoundError,
22
23
  CCIPDataFormatUnsupportedError,
23
24
  CCIPError,
24
25
  CCIPHttpError,
26
+ CCIPTimeoutError,
25
27
  CCIPTypeVersionInvalidError,
26
28
  HttpStatus,
27
29
  } from './errors/index.ts'
@@ -614,9 +616,8 @@ export function parseTypeAndVersion(
614
616
  ): Awaited<ReturnType<Chain['typeAndVersion']>> {
615
617
  const match = typeAndVersion.match(/^(\w.+\S)\s+v?(\d+\.\d+(?:\.\d+)?)([^\d.].*)?$/)
616
618
  if (!match) throw new CCIPTypeVersionInvalidError(typeAndVersion)
617
- const [, typeRaw, version] = match
618
619
  // some string normalization
619
- const type = typeRaw!
620
+ const type = match[1]!
620
621
  .replaceAll(/-(\w)/g, (_, w: string) => w.toUpperCase()) // kebabToPascal
621
622
  .replace(/ccip/gi, 'CCIP')
622
623
  .replace(
@@ -624,8 +625,15 @@ export function parseTypeAndVersion(
624
625
  (_, o: string, n: string, ramp: string) =>
625
626
  `${o.toUpperCase()}${n.toLowerCase()}${ramp.charAt(0).toUpperCase()}${ramp.slice(1).toLowerCase()}`,
626
627
  ) // ccipOfframp -> CCIPOffRamp
627
- if (!match[3]) return [type, version!, typeAndVersion]
628
- else return [type, version!, typeAndVersion, match[3]]
628
+ .replace('router', 'Router') // ccip-router -> CCIPRouter
629
+
630
+ let version = match[2]!
631
+ // for core contracts, always use patch `.0`, to match CCIPVersion
632
+ if (type.match(/((o(n|ff)ramp)|router)\b/gi))
633
+ version = version.replace(/^(\d+\.\d+)(?:\.\d+)?$/, '$1.0')
634
+
635
+ if (!match[3]) return [type, version, typeAndVersion]
636
+ else return [type, version, typeAndVersion, match[3]]
629
637
  }
630
638
 
631
639
  /* eslint-disable jsdoc/require-jsdoc */
@@ -815,6 +823,46 @@ export function createRateLimitedFetch(
815
823
  }
816
824
  }
817
825
 
826
+ /**
827
+ * Performs a fetch request with timeout and abort signal support.
828
+ *
829
+ * @param url - URL to fetch
830
+ * @param operation - Operation name for error context
831
+ * @param opts - Optional timeout, abort signal, fetch function, and extra RequestInit fields
832
+ * @returns Promise resolving to Response
833
+ * @throws CCIPTimeoutError if request times out
834
+ * @throws CCIPAbortError if request is aborted via signal
835
+ */
836
+ export async function fetchWithTimeout(
837
+ url: string,
838
+ operation: string,
839
+ opts?: {
840
+ timeoutMs?: number
841
+ signal?: AbortSignal
842
+ fetch?: typeof globalThis.fetch
843
+ init?: RequestInit
844
+ },
845
+ ): Promise<Response> {
846
+ const timeoutMs = opts?.timeoutMs ?? 30_000
847
+ const fetchFn = opts?.fetch ?? globalThis.fetch.bind(globalThis)
848
+ const timeoutSignal = AbortSignal.timeout(timeoutMs)
849
+ const combinedSignal = opts?.signal
850
+ ? AbortSignal.any([timeoutSignal, opts.signal])
851
+ : timeoutSignal
852
+
853
+ try {
854
+ return await fetchFn(url, { ...opts?.init, signal: combinedSignal })
855
+ } catch (error) {
856
+ if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {
857
+ if (opts?.signal?.aborted) {
858
+ throw new CCIPAbortError(operation)
859
+ }
860
+ throw new CCIPTimeoutError(operation, timeoutMs)
861
+ }
862
+ throw error
863
+ }
864
+ }
865
+
818
866
  // barebones `node:util` backfill, if needed
819
867
  const util =
820
868
  'util' in globalThis