@chainlink/ccip-sdk 0.95.0 → 0.97.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 (217) hide show
  1. package/README.md +2 -2
  2. package/dist/all-chains.d.ts +23 -0
  3. package/dist/all-chains.d.ts.map +1 -0
  4. package/dist/all-chains.js +24 -0
  5. package/dist/all-chains.js.map +1 -0
  6. package/dist/api/index.d.ts +31 -19
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/index.js +46 -25
  9. package/dist/api/index.js.map +1 -1
  10. package/dist/api/types.d.ts +24 -30
  11. package/dist/api/types.d.ts.map +1 -1
  12. package/dist/aptos/exec.d.ts +2 -2
  13. package/dist/aptos/exec.d.ts.map +1 -1
  14. package/dist/aptos/exec.js.map +1 -1
  15. package/dist/aptos/hasher.d.ts.map +1 -1
  16. package/dist/aptos/hasher.js +1 -1
  17. package/dist/aptos/hasher.js.map +1 -1
  18. package/dist/aptos/index.d.ts +43 -15
  19. package/dist/aptos/index.d.ts.map +1 -1
  20. package/dist/aptos/index.js +112 -105
  21. package/dist/aptos/index.js.map +1 -1
  22. package/dist/aptos/types.d.ts +2 -19
  23. package/dist/aptos/types.d.ts.map +1 -1
  24. package/dist/aptos/types.js +0 -11
  25. package/dist/aptos/types.js.map +1 -1
  26. package/dist/chain.d.ts +734 -174
  27. package/dist/chain.d.ts.map +1 -1
  28. package/dist/chain.js +216 -31
  29. package/dist/chain.js.map +1 -1
  30. package/dist/commits.d.ts +4 -6
  31. package/dist/commits.d.ts.map +1 -1
  32. package/dist/commits.js +4 -4
  33. package/dist/commits.js.map +1 -1
  34. package/dist/errors/CCIPError.d.ts +33 -4
  35. package/dist/errors/CCIPError.d.ts.map +1 -1
  36. package/dist/errors/CCIPError.js +33 -4
  37. package/dist/errors/CCIPError.js.map +1 -1
  38. package/dist/errors/codes.d.ts +5 -0
  39. package/dist/errors/codes.d.ts.map +1 -1
  40. package/dist/errors/codes.js +5 -1
  41. package/dist/errors/codes.js.map +1 -1
  42. package/dist/errors/index.d.ts +2 -2
  43. package/dist/errors/index.d.ts.map +1 -1
  44. package/dist/errors/index.js +2 -2
  45. package/dist/errors/index.js.map +1 -1
  46. package/dist/errors/recovery.d.ts.map +1 -1
  47. package/dist/errors/recovery.js +6 -1
  48. package/dist/errors/recovery.js.map +1 -1
  49. package/dist/errors/specialized.d.ts +1702 -121
  50. package/dist/errors/specialized.d.ts.map +1 -1
  51. package/dist/errors/specialized.js +1729 -125
  52. package/dist/errors/specialized.js.map +1 -1
  53. package/dist/errors/utils.d.ts.map +1 -1
  54. package/dist/errors/utils.js +0 -1
  55. package/dist/errors/utils.js.map +1 -1
  56. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  57. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  58. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  59. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  60. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  61. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  62. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  63. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  64. package/dist/evm/const.d.ts +12 -2
  65. package/dist/evm/const.d.ts.map +1 -1
  66. package/dist/evm/const.js +8 -2
  67. package/dist/evm/const.js.map +1 -1
  68. package/dist/evm/errors.d.ts.map +1 -1
  69. package/dist/evm/errors.js +7 -2
  70. package/dist/evm/errors.js.map +1 -1
  71. package/dist/evm/extra-args.d.ts +25 -0
  72. package/dist/evm/extra-args.d.ts.map +1 -0
  73. package/dist/evm/extra-args.js +309 -0
  74. package/dist/evm/extra-args.js.map +1 -0
  75. package/dist/evm/gas.d.ts.map +1 -1
  76. package/dist/evm/gas.js +7 -12
  77. package/dist/evm/gas.js.map +1 -1
  78. package/dist/evm/hasher.d.ts.map +1 -1
  79. package/dist/evm/hasher.js +23 -13
  80. package/dist/evm/hasher.js.map +1 -1
  81. package/dist/evm/index.d.ts +140 -35
  82. package/dist/evm/index.d.ts.map +1 -1
  83. package/dist/evm/index.js +306 -226
  84. package/dist/evm/index.js.map +1 -1
  85. package/dist/evm/messages.d.ts +59 -5
  86. package/dist/evm/messages.d.ts.map +1 -1
  87. package/dist/evm/messages.js +210 -0
  88. package/dist/evm/messages.js.map +1 -1
  89. package/dist/evm/offchain.js.map +1 -1
  90. package/dist/evm/types.d.ts +7 -2
  91. package/dist/evm/types.d.ts.map +1 -1
  92. package/dist/evm/types.js +22 -1
  93. package/dist/evm/types.js.map +1 -1
  94. package/dist/execution.d.ts +62 -22
  95. package/dist/execution.d.ts.map +1 -1
  96. package/dist/execution.js +102 -51
  97. package/dist/execution.js.map +1 -1
  98. package/dist/extra-args.d.ts +113 -4
  99. package/dist/extra-args.d.ts.map +1 -1
  100. package/dist/extra-args.js +38 -3
  101. package/dist/extra-args.js.map +1 -1
  102. package/dist/gas.d.ts +31 -5
  103. package/dist/gas.d.ts.map +1 -1
  104. package/dist/gas.js +43 -9
  105. package/dist/gas.js.map +1 -1
  106. package/dist/index.d.ts +11 -10
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +8 -8
  109. package/dist/index.js.map +1 -1
  110. package/dist/requests.d.ts +101 -22
  111. package/dist/requests.d.ts.map +1 -1
  112. package/dist/requests.js +115 -24
  113. package/dist/requests.js.map +1 -1
  114. package/dist/selectors.d.ts.map +1 -1
  115. package/dist/selectors.js +24 -0
  116. package/dist/selectors.js.map +1 -1
  117. package/dist/shared/bcs-codecs.d.ts +61 -0
  118. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  119. package/dist/shared/bcs-codecs.js +102 -0
  120. package/dist/shared/bcs-codecs.js.map +1 -0
  121. package/dist/shared/constants.d.ts +3 -0
  122. package/dist/shared/constants.d.ts.map +1 -0
  123. package/dist/shared/constants.js +3 -0
  124. package/dist/shared/constants.js.map +1 -0
  125. package/dist/solana/exec.d.ts +2 -2
  126. package/dist/solana/exec.d.ts.map +1 -1
  127. package/dist/solana/exec.js.map +1 -1
  128. package/dist/solana/index.d.ts +148 -30
  129. package/dist/solana/index.d.ts.map +1 -1
  130. package/dist/solana/index.js +137 -44
  131. package/dist/solana/index.js.map +1 -1
  132. package/dist/sui/hasher.d.ts.map +1 -1
  133. package/dist/sui/hasher.js +1 -1
  134. package/dist/sui/hasher.js.map +1 -1
  135. package/dist/sui/index.d.ts +49 -19
  136. package/dist/sui/index.d.ts.map +1 -1
  137. package/dist/sui/index.js +76 -43
  138. package/dist/sui/index.js.map +1 -1
  139. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  140. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  141. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  142. package/dist/sui/manuallyExec/index.d.ts +2 -2
  143. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  144. package/dist/ton/exec.d.ts +2 -2
  145. package/dist/ton/exec.d.ts.map +1 -1
  146. package/dist/ton/exec.js.map +1 -1
  147. package/dist/ton/index.d.ts +66 -27
  148. package/dist/ton/index.d.ts.map +1 -1
  149. package/dist/ton/index.js +172 -47
  150. package/dist/ton/index.js.map +1 -1
  151. package/dist/ton/send.d.ts +52 -0
  152. package/dist/ton/send.d.ts.map +1 -0
  153. package/dist/ton/send.js +166 -0
  154. package/dist/ton/send.js.map +1 -0
  155. package/dist/ton/types.d.ts +2 -2
  156. package/dist/ton/types.d.ts.map +1 -1
  157. package/dist/ton/types.js.map +1 -1
  158. package/dist/types.d.ts +148 -12
  159. package/dist/types.d.ts.map +1 -1
  160. package/dist/types.js +6 -1
  161. package/dist/types.js.map +1 -1
  162. package/dist/utils.d.ts +79 -4
  163. package/dist/utils.d.ts.map +1 -1
  164. package/dist/utils.js +92 -7
  165. package/dist/utils.js.map +1 -1
  166. package/package.json +16 -11
  167. package/src/all-chains.ts +26 -0
  168. package/src/api/index.ts +58 -34
  169. package/src/api/types.ts +24 -31
  170. package/src/aptos/exec.ts +2 -2
  171. package/src/aptos/hasher.ts +1 -1
  172. package/src/aptos/index.ts +127 -129
  173. package/src/aptos/types.ts +2 -15
  174. package/src/chain.ts +837 -191
  175. package/src/commits.ts +9 -9
  176. package/src/errors/CCIPError.ts +33 -4
  177. package/src/errors/codes.ts +5 -1
  178. package/src/errors/index.ts +2 -1
  179. package/src/errors/recovery.ts +9 -1
  180. package/src/errors/specialized.ts +1745 -132
  181. package/src/errors/utils.ts +0 -1
  182. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  183. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  184. package/src/evm/const.ts +10 -3
  185. package/src/evm/errors.ts +6 -2
  186. package/src/evm/extra-args.ts +360 -0
  187. package/src/evm/gas.ts +14 -13
  188. package/src/evm/hasher.ts +30 -18
  189. package/src/evm/index.ts +376 -281
  190. package/src/evm/messages.ts +323 -11
  191. package/src/evm/offchain.ts +2 -2
  192. package/src/evm/types.ts +20 -2
  193. package/src/execution.ts +126 -71
  194. package/src/extra-args.ts +118 -4
  195. package/src/gas.ts +44 -11
  196. package/src/index.ts +14 -11
  197. package/src/requests.ts +128 -24
  198. package/src/selectors.ts +24 -0
  199. package/src/shared/bcs-codecs.ts +132 -0
  200. package/src/shared/constants.ts +2 -0
  201. package/src/solana/exec.ts +4 -4
  202. package/src/solana/index.ts +170 -82
  203. package/src/sui/hasher.ts +1 -1
  204. package/src/sui/index.ts +88 -56
  205. package/src/sui/manuallyExec/encoder.ts +2 -2
  206. package/src/sui/manuallyExec/index.ts +2 -2
  207. package/src/ton/exec.ts +2 -2
  208. package/src/ton/index.ts +220 -58
  209. package/src/ton/send.ts +222 -0
  210. package/src/ton/types.ts +2 -2
  211. package/src/types.ts +173 -30
  212. package/src/utils.ts +91 -7
  213. package/dist/aptos/utils.d.ts +0 -12
  214. package/dist/aptos/utils.d.ts.map +0 -1
  215. package/dist/aptos/utils.js +0 -15
  216. package/dist/aptos/utils.js.map +0 -1
  217. package/src/aptos/utils.ts +0 -24
package/src/evm/const.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { parseAbi } from 'abitype'
1
2
  import { type EventFragment, AbiCoder, Interface } from 'ethers'
2
3
 
3
4
  import Token_ABI from './abi/BurnMintERC677Token.ts'
@@ -10,24 +11,26 @@ import TokenPool_1_6_ABI from './abi/LockReleaseTokenPool_1_6_1.ts'
10
11
  import EVM2EVMOffRamp_1_2_ABI from './abi/OffRamp_1_2.ts'
11
12
  import EVM2EVMOffRamp_1_5_ABI from './abi/OffRamp_1_5.ts'
12
13
  import OffRamp_1_6_ABI from './abi/OffRamp_1_6.ts'
14
+ import OffRamp_2_0_ABI from './abi/OffRamp_2_0.ts'
13
15
  import EVM2EVMOnRamp_1_2_ABI from './abi/OnRamp_1_2.ts'
14
16
  import EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
15
17
  import OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
18
+ import OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
16
19
  import PriceRegistry_1_2_ABI from './abi/PriceRegistry_1_2.ts'
17
20
  import Router_ABI from './abi/Router.ts'
18
21
  import TokenAdminRegistry_ABI from './abi/TokenAdminRegistry_1_5.ts'
19
22
 
20
23
  export const defaultAbiCoder = AbiCoder.defaultAbiCoder()
21
24
 
22
- export const DEFAULT_GAS_LIMIT = 200_000n
23
- export const DEFAULT_APPROVE_GAS_LIMIT = 120_000n
24
-
25
25
  const customErrors = [
26
26
  'error NoContract()',
27
27
  'error NoGasForCallExactCheck()',
28
28
  'error NotEnoughGasForCall()',
29
+ 'error InvalidChain(uint64 chainSelector)',
29
30
  ] as const
30
31
 
32
+ export const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)'])
33
+
31
34
  export const interfaces = {
32
35
  Router: new Interface(Router_ABI),
33
36
  Token: new Interface(Token_ABI),
@@ -38,9 +41,11 @@ export const interfaces = {
38
41
  TokenPool_v1_6: new Interface(TokenPool_1_6_ABI),
39
42
  CommitStore_v1_5: new Interface(CommitStore_1_5_ABI),
40
43
  CommitStore_v1_2: new Interface(CommitStore_1_2_ABI),
44
+ OffRamp_v2_0: new Interface(OffRamp_2_0_ABI),
41
45
  OffRamp_v1_6: new Interface(OffRamp_1_6_ABI),
42
46
  EVM2EVMOffRamp_v1_5: new Interface(EVM2EVMOffRamp_1_5_ABI),
43
47
  EVM2EVMOffRamp_v1_2: new Interface(EVM2EVMOffRamp_1_2_ABI),
48
+ OnRamp_v2_0: new Interface(OnRamp_2_0_ABI),
44
49
  OnRamp_v1_6: new Interface(OnRamp_1_6_ABI),
45
50
  EVM2EVMOnRamp_v1_5: new Interface(EVM2EVMOnRamp_1_5_ABI),
46
51
  EVM2EVMOnRamp_v1_2: new Interface(EVM2EVMOnRamp_1_2_ABI),
@@ -74,3 +79,5 @@ export const commitsFragments = getAllFragmentsMatchingEvents([
74
79
  'CommitReportAccepted',
75
80
  ])
76
81
  export const receiptsFragments = getAllFragmentsMatchingEvents(['ExecutionStateChanged'])
82
+
83
+ export const CCV_INDEXER_URL = 'https://chainlink-ccv-indexer.ccip.stage.external.griddle.sh/all'
package/src/evm/errors.ts CHANGED
@@ -118,7 +118,7 @@ export function parseWithFragment(
118
118
 
119
119
  // join truthy property names, separated by a dot
120
120
  function j(...args: string[]): string {
121
- return args.filter(Boolean).join('.')
121
+ return args.reduce((acc, v) => (!v ? acc : acc ? acc + (v.match(/^\w/) ? '.' : '') + v : v), '')
122
122
  }
123
123
 
124
124
  /**
@@ -137,7 +137,11 @@ export function recursiveParseError(
137
137
  if (data.length === 0) return key ? [[key, data.toArray()]] : []
138
138
  let kv: ReturnType<typeof recursiveParseError>
139
139
  try {
140
- kv = Object.entries(data.toObject()).map(([k, v]) => [j(key, k), v])
140
+ const obj = data.toObject()
141
+ const keys = Object.keys(obj)
142
+ // eslint-disable-next-line no-restricted-syntax
143
+ if (keys.length > 0 && keys.every((k) => k.startsWith('_'))) throw new Error('not an obj')
144
+ kv = Object.entries(obj).map(([k, v]) => [j(key, k), v])
141
145
  } catch (_) {
142
146
  kv = data.toArray().map((v, i) => [j(key, `[${i}]`), v])
143
147
  }
@@ -0,0 +1,360 @@
1
+ import {
2
+ type BytesLike,
3
+ concat,
4
+ dataSlice,
5
+ encodeBase58,
6
+ getAddress,
7
+ hexlify,
8
+ toBeHex,
9
+ toBigInt,
10
+ toNumber,
11
+ zeroPadValue,
12
+ } from 'ethers'
13
+
14
+ import {
15
+ type EVMExtraArgsV1,
16
+ type EVMExtraArgsV2,
17
+ type ExtraArgs,
18
+ type GenericExtraArgsV3,
19
+ type SVMExtraArgsV1,
20
+ type SuiExtraArgsV1,
21
+ EVMExtraArgsV1Tag,
22
+ EVMExtraArgsV2Tag,
23
+ GenericExtraArgsV3Tag,
24
+ SVMExtraArgsV1Tag,
25
+ SuiExtraArgsV1Tag,
26
+ } from '../extra-args.ts'
27
+ import { DEFAULT_GAS_LIMIT } from '../shared/constants.ts'
28
+ import { getAddressBytes, getDataBytes } from '../utils.ts'
29
+ import { defaultAbiCoder } from './const.ts'
30
+ import { resultToObject } from './types.ts'
31
+
32
+ // ABI type strings for extra args encoding
33
+ const EVMExtraArgsV1ABI = 'tuple(uint256 gasLimit)'
34
+ const EVMExtraArgsV2ABI = 'tuple(uint256 gasLimit, bool allowOutOfOrderExecution)'
35
+ const SVMExtraArgsV1ABI =
36
+ 'tuple(uint32 computeUnits, uint64 accountIsWritableBitmap, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] accounts)'
37
+ const SuiExtraArgsV1ABI =
38
+ 'tuple(uint256 gasLimit, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] receiverObjectIds)'
39
+
40
+ /**
41
+ * Encodes GenericExtraArgsV3 using tightly packed binary format.
42
+ *
43
+ * Binary format:
44
+ * - tag (4 bytes): 0xa69dd4aa
45
+ * - gasLimit (4 bytes): uint32 big-endian
46
+ * - blockConfirmations (2 bytes): uint16 big-endian
47
+ * - ccvsLength (1 byte): uint8
48
+ * - For each CCV:
49
+ * - ccvAddressLength (1 byte): 0 or 20
50
+ * - ccvAddress (0 or 20 bytes)
51
+ * - ccvArgsLength (2 bytes): uint16 big-endian
52
+ * - ccvArgs (variable)
53
+ * - executorLength (1 byte): 0 or 20
54
+ * - executor (0 or 20 bytes)
55
+ * - executorArgsLength (2 bytes): uint16 big-endian
56
+ * - executorArgs (variable)
57
+ * - tokenReceiverLength (1 byte): uint8
58
+ * - tokenReceiver (variable)
59
+ * - tokenArgsLength (2 bytes): uint16 big-endian
60
+ * - tokenArgs (variable)
61
+ */
62
+ function encodeExtraArgsV3(args: GenericExtraArgsV3): string {
63
+ const parts: Uint8Array[] = []
64
+
65
+ // Tag (4 bytes)
66
+ parts.push(getDataBytes(GenericExtraArgsV3Tag))
67
+
68
+ // gasLimit (4 bytes, uint32 big-endian)
69
+ parts.push(getDataBytes(toBeHex(args.gasLimit, 4)))
70
+
71
+ // blockConfirmations (2 bytes, uint16 big-endian)
72
+ parts.push(getDataBytes(toBeHex(args.blockConfirmations, 2)))
73
+
74
+ // ccvsLength (1 byte)
75
+ parts.push(new Uint8Array([args.ccvs.length]))
76
+
77
+ // For each CCV
78
+ for (let i = 0; i < args.ccvs.length; i++) {
79
+ const ccvAddress = args.ccvs[i]!
80
+ const ccvArgsBytes = getDataBytes(args.ccvArgs[i] ?? '0x')
81
+
82
+ if (ccvAddress && ccvAddress !== '' && ccvAddress !== '0x') {
83
+ // ccvAddressLength = 20
84
+ parts.push(new Uint8Array([20]))
85
+ // ccvAddress (20 bytes)
86
+ parts.push(getDataBytes(ccvAddress))
87
+ } else {
88
+ // ccvAddressLength = 0
89
+ parts.push(new Uint8Array([0]))
90
+ }
91
+
92
+ // ccvArgsLength (2 bytes, uint16 big-endian)
93
+ parts.push(getDataBytes(toBeHex(ccvArgsBytes.length, 2)))
94
+
95
+ // ccvArgs (variable)
96
+ if (ccvArgsBytes.length > 0) {
97
+ parts.push(ccvArgsBytes)
98
+ }
99
+ }
100
+
101
+ // executorLength (1 byte)
102
+ if (args.executor && args.executor !== '' && args.executor !== '0x') {
103
+ parts.push(new Uint8Array([20]))
104
+ parts.push(getDataBytes(args.executor))
105
+ } else {
106
+ parts.push(new Uint8Array([0]))
107
+ }
108
+
109
+ // Convert BytesLike fields to Uint8Array
110
+ const executorArgsBytes = getDataBytes(args.executorArgs)
111
+ const tokenReceiverBytes = getDataBytes(args.tokenReceiver)
112
+ const tokenArgsBytes = getDataBytes(args.tokenArgs)
113
+
114
+ // executorArgsLength (2 bytes, uint16 big-endian)
115
+ parts.push(getDataBytes(toBeHex(executorArgsBytes.length, 2)))
116
+
117
+ // executorArgs (variable)
118
+ if (executorArgsBytes.length > 0) {
119
+ parts.push(executorArgsBytes)
120
+ }
121
+
122
+ // tokenReceiverLength (1 byte)
123
+ parts.push(new Uint8Array([tokenReceiverBytes.length]))
124
+
125
+ // tokenReceiver (variable)
126
+ if (tokenReceiverBytes.length > 0) {
127
+ parts.push(tokenReceiverBytes)
128
+ }
129
+
130
+ // tokenArgsLength (2 bytes, uint16 big-endian)
131
+ parts.push(getDataBytes(toBeHex(tokenArgsBytes.length, 2)))
132
+
133
+ // tokenArgs (variable)
134
+ if (tokenArgsBytes.length > 0) {
135
+ parts.push(tokenArgsBytes)
136
+ }
137
+
138
+ return hexlify(concat(parts))
139
+ }
140
+
141
+ /**
142
+ * Decodes GenericExtraArgsV3 from tightly packed binary format.
143
+ * @param data - Bytes to decode (without the tag prefix).
144
+ * @returns Decoded GenericExtraArgsV3 or undefined if parsing fails.
145
+ */
146
+ function decodeExtraArgsV3(data: Uint8Array): GenericExtraArgsV3 | undefined {
147
+ let offset = 0
148
+
149
+ // gasLimit (4 bytes, uint32 big-endian)
150
+ if (offset + 4 > data.length) return undefined
151
+ const gasLimit = toBigInt(data.subarray(offset, offset + 4))
152
+ offset += 4
153
+
154
+ // blockConfirmations (2 bytes, uint16 big-endian)
155
+ if (offset + 2 > data.length) return undefined
156
+ const blockConfirmations = toNumber(data.subarray(offset, offset + 2))
157
+ offset += 2
158
+
159
+ // ccvsLength (1 byte)
160
+ if (offset + 1 > data.length) return undefined
161
+ const ccvsLength = data[offset]!
162
+ offset += 1
163
+
164
+ const ccvs: string[] = []
165
+ const ccvArgs: string[] = []
166
+
167
+ // For each CCV
168
+ for (let i = 0; i < ccvsLength; i++) {
169
+ // ccvAddressLength (1 byte)
170
+ if (offset + 1 > data.length) return undefined
171
+ const ccvAddrLen = data[offset]!
172
+ offset += 1
173
+
174
+ // ccvAddress (0 or 20 bytes)
175
+ if (ccvAddrLen === 20) {
176
+ if (offset + 20 > data.length) return undefined
177
+ ccvs.push(getAddress(hexlify(data.slice(offset, offset + 20))))
178
+ offset += 20
179
+ } else if (ccvAddrLen === 0) {
180
+ ccvs.push('')
181
+ } else {
182
+ return undefined // Invalid address length
183
+ }
184
+
185
+ // ccvArgsLength (2 bytes, uint16 big-endian)
186
+ if (offset + 2 > data.length) return undefined
187
+ const ccvArgsLen = toNumber(data.subarray(offset, offset + 2))
188
+ offset += 2
189
+
190
+ // ccvArgs (variable)
191
+ if (offset + ccvArgsLen > data.length) return undefined
192
+ ccvArgs.push(hexlify(data.slice(offset, offset + ccvArgsLen)))
193
+ offset += ccvArgsLen
194
+ }
195
+
196
+ // executorLength (1 byte)
197
+ if (offset + 1 > data.length) return undefined
198
+ const executorLen = data[offset]!
199
+ offset += 1
200
+
201
+ // executor (0 or 20 bytes)
202
+ let executor = ''
203
+ if (executorLen === 20) {
204
+ if (offset + 20 > data.length) return undefined
205
+ executor = getAddress(hexlify(data.slice(offset, offset + 20)))
206
+ offset += 20
207
+ } else if (executorLen !== 0) {
208
+ return undefined // Invalid executor length
209
+ }
210
+
211
+ // executorArgsLength (2 bytes, uint16 big-endian)
212
+ if (offset + 2 > data.length) return undefined
213
+ const executorArgsLen = toNumber(data.subarray(offset, offset + 2))
214
+ offset += 2
215
+
216
+ // executorArgs (variable)
217
+ if (offset + executorArgsLen > data.length) return undefined
218
+ const executorArgs = hexlify(data.slice(offset, offset + executorArgsLen))
219
+ offset += executorArgsLen
220
+
221
+ // tokenReceiverLength (1 byte)
222
+ if (offset + 1 > data.length) return undefined
223
+ const tokenReceiverLen = data[offset]!
224
+ offset += 1
225
+
226
+ // tokenReceiver (variable)
227
+ if (offset + tokenReceiverLen > data.length) return undefined
228
+ const tokenReceiverBytes = data.slice(offset, offset + tokenReceiverLen)
229
+ offset += tokenReceiverLen
230
+
231
+ // Convert tokenReceiver bytes to string
232
+ let tokenReceiver: string
233
+ if (tokenReceiverLen === 0) {
234
+ tokenReceiver = ''
235
+ } else if (tokenReceiverLen === 20) {
236
+ // 20 bytes = EVM address, return checksummed
237
+ tokenReceiver = getAddress(hexlify(tokenReceiverBytes))
238
+ } else {
239
+ // Other lengths: return as hex string
240
+ tokenReceiver = hexlify(tokenReceiverBytes)
241
+ }
242
+
243
+ // tokenArgsLength (2 bytes, uint16 big-endian)
244
+ if (offset + 2 > data.length) return undefined
245
+ const tokenArgsLen = toNumber(data.subarray(offset, offset + 2))
246
+ offset += 2
247
+
248
+ // tokenArgs (variable)
249
+ if (offset + tokenArgsLen > data.length) return undefined
250
+ const tokenArgs = hexlify(data.slice(offset, offset + tokenArgsLen))
251
+ offset += tokenArgsLen
252
+
253
+ return {
254
+ gasLimit,
255
+ blockConfirmations,
256
+ ccvs,
257
+ ccvArgs,
258
+ executor,
259
+ executorArgs,
260
+ tokenReceiver,
261
+ tokenArgs,
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Decodes extra arguments from a CCIP message.
267
+ * @param extraArgs - Encoded extra arguments bytes.
268
+ * @returns Decoded extra arguments with tag, or undefined if unknown format.
269
+ */
270
+ export function decodeExtraArgs(
271
+ extraArgs: BytesLike,
272
+ ):
273
+ | (EVMExtraArgsV1 & { _tag: 'EVMExtraArgsV1' })
274
+ | (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
275
+ | (GenericExtraArgsV3 & { _tag: 'GenericExtraArgsV3' })
276
+ | (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
277
+ | (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' })
278
+ | undefined {
279
+ const data = getDataBytes(extraArgs),
280
+ tag = dataSlice(data, 0, 4)
281
+ switch (tag) {
282
+ case EVMExtraArgsV1Tag: {
283
+ const args = defaultAbiCoder.decode([EVMExtraArgsV1ABI], dataSlice(data, 4))
284
+ return { ...(resultToObject(args[0]) as EVMExtraArgsV1), _tag: 'EVMExtraArgsV1' }
285
+ }
286
+ case EVMExtraArgsV2Tag: {
287
+ const args = defaultAbiCoder.decode([EVMExtraArgsV2ABI], dataSlice(data, 4))
288
+ return { ...(resultToObject(args[0]) as EVMExtraArgsV2), _tag: 'EVMExtraArgsV2' }
289
+ }
290
+ case GenericExtraArgsV3Tag: {
291
+ const parsed = decodeExtraArgsV3(data.slice(4))
292
+ if (!parsed) return undefined
293
+ return { ...parsed, _tag: 'GenericExtraArgsV3' }
294
+ }
295
+ case SVMExtraArgsV1Tag: {
296
+ const args = defaultAbiCoder.decode([SVMExtraArgsV1ABI], dataSlice(data, 4))
297
+ const parsed = resultToObject(args[0]) as SVMExtraArgsV1
298
+ parsed.tokenReceiver = encodeBase58(parsed.tokenReceiver)
299
+ parsed.accounts = parsed.accounts.map((a: string) => encodeBase58(a))
300
+ return { ...parsed, _tag: 'SVMExtraArgsV1' }
301
+ }
302
+ case SuiExtraArgsV1Tag: {
303
+ const args = defaultAbiCoder.decode([SuiExtraArgsV1ABI], dataSlice(data, 4))
304
+ const parsed = resultToObject(args[0]) as SuiExtraArgsV1
305
+ return {
306
+ ...parsed,
307
+ _tag: 'SuiExtraArgsV1',
308
+ }
309
+ }
310
+ default:
311
+ return undefined
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Encodes extra arguments for a CCIP message.
317
+ * @param args - Extra arguments to encode.
318
+ * @returns Encoded extra arguments as hex string.
319
+ */
320
+ export function encodeExtraArgs(args: ExtraArgs | undefined): string {
321
+ if (!args) return '0x'
322
+ if ('blockConfirmations' in args) {
323
+ // GenericExtraArgsV3 - tightly packed binary encoding
324
+ return encodeExtraArgsV3(args)
325
+ } else if ('computeUnits' in args) {
326
+ return concat([
327
+ SVMExtraArgsV1Tag,
328
+ defaultAbiCoder.encode(
329
+ [SVMExtraArgsV1ABI],
330
+ [
331
+ {
332
+ ...args,
333
+ tokenReceiver: getAddressBytes(args.tokenReceiver),
334
+ accounts: args.accounts.map((a) => getAddressBytes(a)),
335
+ },
336
+ ],
337
+ ),
338
+ ])
339
+ } else if ('receiverObjectIds' in args) {
340
+ return concat([
341
+ SuiExtraArgsV1Tag,
342
+ defaultAbiCoder.encode(
343
+ [SuiExtraArgsV1ABI],
344
+ [
345
+ {
346
+ ...args,
347
+ tokenReceiver: zeroPadValue(getAddressBytes(args.tokenReceiver), 32),
348
+ receiverObjectIds: args.receiverObjectIds.map((a) => getDataBytes(a)),
349
+ },
350
+ ],
351
+ ),
352
+ ])
353
+ } else if ('allowOutOfOrderExecution' in args) {
354
+ if ((args as Partial<typeof args>).gasLimit == null) args.gasLimit = DEFAULT_GAS_LIMIT
355
+ return concat([EVMExtraArgsV2Tag, defaultAbiCoder.encode([EVMExtraArgsV2ABI], [args])])
356
+ } else if ((args as Partial<typeof args>).gasLimit != null) {
357
+ return concat([EVMExtraArgsV1Tag, defaultAbiCoder.encode([EVMExtraArgsV1ABI], [args])])
358
+ }
359
+ return '0x'
360
+ }
package/src/evm/gas.ts CHANGED
@@ -32,33 +32,34 @@ const ccipReceive = FunctionFragment.from({
32
32
  })
33
33
  type Any2EVMMessage = Parameters<TypedContract<typeof RouterABI>['routeMessage']>[0]
34
34
 
35
- const transferFragment = interfaces.Token.getFunction('transfer')!
36
-
37
35
  /**
38
36
  * Finds suitable token balance slot by simulating a fake transfer between 2 non-existent accounts,
39
37
  * with state overrides for the holders' balance, which reverts if override slot is wrong
40
38
  */
41
39
  const findBalancesSlot = memoize(
42
- async function findBalancesSlot_(token: string, provider: JsonRpcApiProvider): Promise<number> {
43
- const fakeHolder = getAddress(hexlify(randomBytes(20)))
44
- const fakeRecipient = getAddress(hexlify(randomBytes(20)))
45
- const fakeAmount = 1e7
40
+ async function findBalancesSlot_(
41
+ token: string,
42
+ provider: JsonRpcApiProvider,
43
+ holder: string = getAddress(hexlify(randomBytes(20))),
44
+ recipient: string = getAddress(hexlify(randomBytes(20))),
45
+ ): Promise<number> {
46
+ const contract = new Contract(token, interfaces.Token, provider) as unknown as TypedContract<
47
+ typeof TokenABI
48
+ >
49
+ const fakeAmount = (await contract.totalSupply()) + 1n
50
+ const calldata = interfaces.Token.encodeFunctionData('transfer', [recipient, fakeAmount])
46
51
 
47
- const calldata = concat([
48
- transferFragment.selector,
49
- defaultAbiCoder.encode(transferFragment.inputs, [fakeRecipient, fakeAmount]),
50
- ])
51
52
  let firstErr
52
53
  // try range(0..15), but start with most probable 0 (common ERC20) and 9 (USDC)
53
54
  for (const slot of [0, 9, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]) {
54
55
  try {
55
56
  await provider.send('eth_estimateGas', [
56
- { from: fakeHolder, to: token, data: calldata },
57
+ { from: holder, to: token, data: calldata },
57
58
  'latest',
58
59
  {
59
60
  [token]: {
60
61
  stateDiff: {
61
- [solidityPackedKeccak256(['uint256', 'uint256'], [fakeHolder, slot])]: toBeHex(
62
+ [solidityPackedKeccak256(['uint256', 'uint256'], [holder, slot])]: toBeHex(
62
63
  fakeAmount,
63
64
  32,
64
65
  ),
@@ -109,7 +110,7 @@ export async function estimateExecGas({
109
110
  destAmounts[token] = currentBalance
110
111
  }
111
112
  destAmounts[token]! += amount
112
- const balancesSlot = await findBalancesSlot(token, provider)
113
+ const balancesSlot = await findBalancesSlot(token, provider, receiver, router)
113
114
  stateOverrides[token] = {
114
115
  stateDiff: {
115
116
  [solidityPackedKeccak256(['uint256', 'uint256'], [receiver, balancesSlot])]: toBeHex(
package/src/evm/hasher.ts CHANGED
@@ -102,24 +102,39 @@ export function getV16LeafHasher(
102
102
  onRamp: string,
103
103
  { logger = console }: WithLogger = {},
104
104
  ): LeafHasher<typeof CCIPVersion.V1_6> {
105
+ const onRampBytes = getAddressBytes(onRamp)
106
+ // Addresses ≤32 bytes (EVM 20B, Aptos/Solana/Sui 32B) are zero-padded to 32 bytes;
107
+ // Addresses >32 bytes (e.g., TON 36B) are used as raw bytes without padding
108
+ const onRampForHash = onRampBytes.length <= 32 ? zeroPadValue(onRampBytes, 32) : onRampBytes
109
+
105
110
  const metadataInput = concat([
106
111
  ANY_2_EVM_MESSAGE_HASH,
107
112
  toBeHex(sourceChainSelector, 32),
108
113
  toBeHex(destChainSelector, 32),
109
- keccak256(zeroPadValue(getAddressBytes(onRamp), 32)),
114
+ keccak256(onRampForHash),
110
115
  ])
111
116
 
112
117
  return (message: ReadonlyDeep<CCIPMessage<typeof CCIPVersion.V1_6>>): string => {
113
118
  logger.debug('Message', message)
114
- const parsedArgs = decodeExtraArgs(
115
- message.extraArgs,
116
- networkInfo(message.sourceChainSelector).family,
117
- )
118
- if (
119
- !parsedArgs ||
120
- (parsedArgs._tag !== 'EVMExtraArgsV1' && parsedArgs._tag !== 'EVMExtraArgsV2')
121
- )
122
- throw new CCIPExtraArgsInvalidError('EVM', message.extraArgs)
119
+
120
+ // Non-EVM sources (e.g., TON) embed gasLimit on the message during parsing,
121
+ // since their extraArgs format differs. EVM sources decode from extraArgs.
122
+ let gasLimit: bigint
123
+ if ('gasLimit' in message) {
124
+ gasLimit = message.gasLimit
125
+ } else {
126
+ const parsedArgs = decodeExtraArgs(
127
+ message.extraArgs,
128
+ networkInfo(message.sourceChainSelector).family,
129
+ )
130
+ if (
131
+ !parsedArgs ||
132
+ (parsedArgs._tag !== 'EVMExtraArgsV1' && parsedArgs._tag !== 'EVMExtraArgsV2')
133
+ )
134
+ throw new CCIPExtraArgsInvalidError('EVM', message.extraArgs)
135
+ gasLimit = parsedArgs.gasLimit
136
+ }
137
+
123
138
  const tokenAmounts = message.tokenAmounts.map((ta) => ({
124
139
  ...ta,
125
140
  sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
@@ -140,16 +155,13 @@ export function getV16LeafHasher(
140
155
  'uint256 gasLimit',
141
156
  'uint64 nonce',
142
157
  ],
143
- [
144
- message.messageId,
145
- message.receiver,
146
- message.sequenceNumber,
147
- parsedArgs.gasLimit,
148
- message.nonce,
149
- ],
158
+ [message.messageId, message.receiver, message.sequenceNumber, gasLimit, message.nonce],
150
159
  )
151
160
 
152
- const sender = zeroPadValue(getAddressBytes(message.sender), 32)
161
+ const senderBytes = getAddressBytes(message.sender)
162
+ // Addresses ≤32 bytes (EVM 20B, Aptos/Solana/Sui 32B) are zero-padded to 32 bytes;
163
+ // Addresses >32 bytes (e.g., TON 36B) are used as raw bytes without padding
164
+ const sender = senderBytes.length <= 32 ? zeroPadValue(senderBytes, 32) : senderBytes
153
165
 
154
166
  const packedValues = defaultAbiCoder.encode(
155
167
  [