@chainlink/ccip-sdk 0.97.0 → 1.0.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 (114) hide show
  1. package/README.md +12 -9
  2. package/dist/api/index.d.ts +4 -0
  3. package/dist/api/index.d.ts.map +1 -1
  4. package/dist/api/index.js +16 -4
  5. package/dist/api/index.js.map +1 -1
  6. package/dist/aptos/index.d.ts +4 -6
  7. package/dist/aptos/index.d.ts.map +1 -1
  8. package/dist/aptos/index.js +0 -5
  9. package/dist/aptos/index.js.map +1 -1
  10. package/dist/aptos/logs.d.ts +2 -2
  11. package/dist/aptos/logs.d.ts.map +1 -1
  12. package/dist/chain.d.ts +11 -12
  13. package/dist/chain.d.ts.map +1 -1
  14. package/dist/chain.js +19 -1
  15. package/dist/chain.js.map +1 -1
  16. package/dist/errors/index.d.ts +3 -3
  17. package/dist/errors/index.d.ts.map +1 -1
  18. package/dist/errors/index.js +3 -3
  19. package/dist/errors/index.js.map +1 -1
  20. package/dist/evm/abi/OnRamp_2_0.d.ts +1 -1
  21. package/dist/evm/abi/OnRamp_2_0.js +1 -1
  22. package/dist/evm/abi/OnRamp_2_0.js.map +1 -1
  23. package/dist/evm/index.d.ts +1 -3
  24. package/dist/evm/index.d.ts.map +1 -1
  25. package/dist/evm/index.js +1 -5
  26. package/dist/evm/index.js.map +1 -1
  27. package/dist/evm/logs.d.ts +1 -1
  28. package/dist/evm/logs.js +1 -1
  29. package/dist/evm/offchain.d.ts +1 -14
  30. package/dist/evm/offchain.d.ts.map +1 -1
  31. package/dist/evm/offchain.js +1 -133
  32. package/dist/evm/offchain.js.map +1 -1
  33. package/dist/index.d.ts +3 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/offchain.d.ts +23 -6
  38. package/dist/offchain.d.ts.map +1 -1
  39. package/dist/offchain.js +92 -17
  40. package/dist/offchain.js.map +1 -1
  41. package/dist/requests.d.ts.map +1 -1
  42. package/dist/requests.js +0 -1
  43. package/dist/requests.js.map +1 -1
  44. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts +1 -1
  45. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js +1 -1
  46. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts +1 -1
  47. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js +1 -1
  48. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts +1 -1
  49. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js +1 -1
  50. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts +16 -1
  51. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts.map +1 -1
  52. package/dist/solana/idl/1.6.0/CCIP_COMMON.js +16 -1
  53. package/dist/solana/idl/1.6.0/CCIP_COMMON.js.map +1 -1
  54. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts +1 -1
  55. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js +1 -1
  56. package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts +1 -1
  57. package/dist/solana/idl/1.6.0/CCIP_ROUTER.js +1 -1
  58. package/dist/solana/index.d.ts +6 -8
  59. package/dist/solana/index.d.ts.map +1 -1
  60. package/dist/solana/index.js +2 -7
  61. package/dist/solana/index.js.map +1 -1
  62. package/dist/solana/offchain.d.ts +1 -13
  63. package/dist/solana/offchain.d.ts.map +1 -1
  64. package/dist/solana/offchain.js +1 -66
  65. package/dist/solana/offchain.js.map +1 -1
  66. package/dist/solana/utils.d.ts +4 -4
  67. package/dist/solana/utils.d.ts.map +1 -1
  68. package/dist/solana/utils.js +1 -1
  69. package/dist/solana/utils.js.map +1 -1
  70. package/dist/sui/index.d.ts +4 -6
  71. package/dist/sui/index.d.ts.map +1 -1
  72. package/dist/sui/index.js +0 -5
  73. package/dist/sui/index.js.map +1 -1
  74. package/dist/ton/exec.d.ts +2 -2
  75. package/dist/ton/exec.d.ts.map +1 -1
  76. package/dist/ton/exec.js +1 -1
  77. package/dist/ton/exec.js.map +1 -1
  78. package/dist/ton/index.d.ts +5 -6
  79. package/dist/ton/index.d.ts.map +1 -1
  80. package/dist/ton/index.js +0 -4
  81. package/dist/ton/index.js.map +1 -1
  82. package/dist/ton/types.d.ts +3 -5
  83. package/dist/ton/types.d.ts.map +1 -1
  84. package/dist/ton/types.js.map +1 -1
  85. package/dist/types.d.ts +10 -10
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/types.js.map +1 -1
  88. package/package.json +9 -5
  89. package/src/api/index.ts +21 -8
  90. package/src/aptos/index.ts +4 -11
  91. package/src/aptos/logs.ts +2 -2
  92. package/src/chain.ts +17 -12
  93. package/src/errors/index.ts +3 -0
  94. package/src/evm/abi/OnRamp_2_0.ts +1 -1
  95. package/src/evm/index.ts +4 -10
  96. package/src/evm/logs.ts +1 -1
  97. package/src/evm/offchain.ts +2 -191
  98. package/src/index.ts +8 -1
  99. package/src/offchain.ts +125 -28
  100. package/src/requests.ts +2 -3
  101. package/src/solana/idl/1.6.0/BASE_TOKEN_POOL.ts +2 -2
  102. package/src/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.ts +2 -2
  103. package/src/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts +2 -2
  104. package/src/solana/idl/1.6.0/CCIP_COMMON.ts +32 -2
  105. package/src/solana/idl/1.6.0/CCIP_OFFRAMP.ts +2 -2
  106. package/src/solana/idl/1.6.0/CCIP_ROUTER.ts +2 -2
  107. package/src/solana/index.ts +10 -17
  108. package/src/solana/offchain.ts +3 -100
  109. package/src/solana/utils.ts +8 -5
  110. package/src/sui/index.ts +5 -12
  111. package/src/ton/exec.ts +3 -6
  112. package/src/ton/index.ts +10 -15
  113. package/src/ton/types.ts +3 -6
  114. package/src/types.ts +13 -10
package/src/offchain.ts CHANGED
@@ -1,56 +1,79 @@
1
- import { keccak256 } from 'ethers'
1
+ import { type BytesLike, dataLength, dataSlice, getBytes, toNumber } from 'ethers'
2
+ import type { PickDeep } from 'type-fest'
2
3
 
3
4
  import {
4
5
  CCIPLbtcAttestationNotApprovedError,
5
6
  CCIPLbtcAttestationNotFoundError,
6
7
  CCIPUsdcAttestationError,
7
8
  } from './errors/index.ts'
8
- import { NetworkType } from './types.ts'
9
+ import { parseSourceTokenData } from './evm/messages.ts'
10
+ import { type CCIPRequest, type OffchainTokenData, type WithLogger, NetworkType } from './types.ts'
11
+ import { networkInfo } from './utils.ts'
9
12
 
10
13
  const CIRCLE_API_URL = {
11
- mainnet: 'https://iris-api.circle.com/v1',
12
- testnet: 'https://iris-api-sandbox.circle.com/v1',
14
+ mainnet: 'https://iris-api.circle.com',
15
+ testnet: 'https://iris-api-sandbox.circle.com',
13
16
  }
14
17
 
15
18
  type CctpAttestationResponse =
16
19
  | { error: 'string' }
17
- | { status: 'pending_confirmations' }
18
- | { status: 'complete'; attestation: string }
19
-
20
- const LOMBARD_API_URL = {
21
- mainnet: 'https://mainnet.prod.lombard.finance',
22
- testnet: 'https://gastald-testnet.prod.lombard.finance',
23
- }
24
-
25
- type LombardAttestation =
26
- | { status: 'NOTARIZATION_STATUS_SESSION_APPROVED'; message_hash: string; attestation: string }
27
- | { status: string; message_hash: string }
28
- type LombardAttestationsResponse = { attestations: Array<LombardAttestation> }
20
+ | {
21
+ messages: {
22
+ status: 'pending_confirmations' | 'complete'
23
+ eventNonce?: string
24
+ attestation: string
25
+ message: string
26
+ }[]
27
+ }
29
28
 
30
29
  /**
31
- * Returns the USDC attestation for a given MessageSent Log
32
- * https://developers.circle.com/stablecoins/reference/getattestation
30
+ * Returns the USDC attestation for a given tokenAmount.extraData and txHash
31
+ * https://developers.circle.com/cctp/quickstarts/transfer-usdc-ethereum-to-arc#3-3-retrieve-attestation
33
32
  *
34
- * @param message - payload of USDC MessageSent(bytes message) event
33
+ * @param opts - CCTPv2 options
35
34
  * @param networkType - network type (mainnet or testnet)
36
- * @returns USDC/CCTP attestation bytes
35
+ * @returns USDC/CCTP attestation and message
37
36
  */
38
37
  export async function getUsdcAttestation(
39
- message: string,
38
+ opts: {
39
+ /** CCTP sourceDomain */
40
+ sourceDomain: number
41
+ /** CCTP burn eventNonce */
42
+ nonce: number
43
+ /** burn txHash, same as CCIP request */
44
+ txHash: string
45
+ },
40
46
  networkType: NetworkType,
41
- ): Promise<string> {
42
- const msgHash = keccak256(message)
43
-
47
+ ): Promise<{ attestation: string; message: string }> {
48
+ const { sourceDomain, nonce, txHash } = opts
44
49
  const circleApiBaseUrl =
45
50
  networkType === NetworkType.Mainnet ? CIRCLE_API_URL.mainnet : CIRCLE_API_URL.testnet
46
- const res = await fetch(`${circleApiBaseUrl}/attestations/${msgHash}`)
51
+ const res = await fetch(
52
+ `${circleApiBaseUrl}/v2/messages/${sourceDomain}?transactionHash=${txHash}`,
53
+ )
47
54
  const json = (await res.json()) as CctpAttestationResponse
48
- if (!('status' in json) || json.status !== 'complete' || !json.attestation) {
49
- throw new CCIPUsdcAttestationError(msgHash, json)
55
+ let att
56
+ if ('messages' in json) {
57
+ att = json.messages.find((m) => m.status === 'complete' && m.eventNonce === nonce.toString())
50
58
  }
51
- return json.attestation
59
+ if (!att?.message) throw new CCIPUsdcAttestationError(txHash, json, { context: opts })
60
+ return att
61
+ }
62
+
63
+ const LOMBARD_API_URL = {
64
+ mainnet: 'https://mainnet.prod.lombard.finance',
65
+ testnet: 'https://gastald-testnet.prod.lombard.finance',
52
66
  }
53
67
 
68
+ type LombardAttestation =
69
+ | {
70
+ status: 'NOTARIZATION_STATUS_SESSION_APPROVED'
71
+ message_hash: string
72
+ attestation: string
73
+ }
74
+ | { status: string; message_hash: string }
75
+ type LombardAttestationsResponse = { attestations: Array<LombardAttestation> }
76
+
54
77
  /**
55
78
  * Returns the LBTC attestation for a given payload hash
56
79
  *
@@ -86,3 +109,77 @@ export async function getLbtcAttestation(
86
109
  }
87
110
  return attestation
88
111
  }
112
+
113
+ /**
114
+ * Fetch CCIPv1 offchain token data for USDC and LBTC tokenAmounts
115
+ * @param request - CCIPRequest containing tx.hash and message
116
+ * @returns Promise resolving to an OffchainTokenData for each tokenAmount
117
+ */
118
+ export async function getOffchainTokenData(
119
+ request: PickDeep<CCIPRequest, 'tx.hash' | `message`>,
120
+ { logger = console }: WithLogger = {},
121
+ ): Promise<OffchainTokenData[]> {
122
+ const { networkType } = networkInfo(request.message.sourceChainSelector)
123
+
124
+ function looksUsdcData(extraData: BytesLike) {
125
+ if (dataLength(extraData) !== 64) return
126
+ // USDCTokenPool's extraData is a packed `SourceTokenDataPayloadV1{uint64 nonce, uint32 sourceDomain}`,
127
+ // which we need to query CCTPv2 (by sourceDomain and txHash) and to filter by nonce among messages,
128
+ // if more than one in tx
129
+ let nonce, sourceDomain
130
+ try {
131
+ // those toNumber conversions throw early in case the bytearray don't look like small numbers
132
+ nonce = toNumber(dataSlice(extraData, 0, 32))
133
+ sourceDomain = toNumber(dataSlice(extraData, 32, 32 + 32))
134
+ return { nonce, sourceDomain } // maybe USDC
135
+ } catch {
136
+ // not USDC
137
+ }
138
+ }
139
+
140
+ function looksLbtcData(extraData: BytesLike) {
141
+ // LBTC returns `message_hash`/`payloadHash` directly as `bytes32 extraData`
142
+ if (
143
+ dataLength(extraData) === 32 &&
144
+ getBytes(extraData, 'extraData').filter(Boolean).length > 20 // looks like a hash
145
+ )
146
+ return true
147
+ }
148
+
149
+ return Promise.all(
150
+ request.message.tokenAmounts.map(async (tokenAmount, i) => {
151
+ let extraData
152
+ if ('extraData' in tokenAmount) {
153
+ extraData = tokenAmount.extraData
154
+ } else if ('sourceTokenData' in request.message) {
155
+ // v1.2..v1.5
156
+ if (dataLength(request.message.sourceTokenData[i]!) === 64) {
157
+ extraData = request.message.sourceTokenData[i]
158
+ } else {
159
+ ;({ extraData } = parseSourceTokenData(request.message.sourceTokenData[i]!))
160
+ }
161
+ }
162
+ if (!extraData) return
163
+ const usdcOpts = looksUsdcData(extraData)
164
+ if (usdcOpts) {
165
+ try {
166
+ const usdcAttestation = await getUsdcAttestation(
167
+ { ...usdcOpts, txHash: request.tx.hash },
168
+ networkType,
169
+ )
170
+ return { _tag: 'usdc', extraData, ...usdcAttestation }
171
+ } catch (err) {
172
+ // maybe not a USDC transfer, or not ready
173
+ logger.warn(`❌ CCTP: Failed to fetch attestation for message:`, request.message, err)
174
+ }
175
+ } else if (looksLbtcData(extraData)) {
176
+ try {
177
+ const lbtcAttestation = await getLbtcAttestation(extraData, networkType)
178
+ return { _tag: 'lbtc', extraData, ...lbtcAttestation }
179
+ } catch (err) {
180
+ logger.warn(`❌ LBTC: Failed to fetch attestation for message:`, extraData, err)
181
+ }
182
+ }
183
+ }),
184
+ )
185
+ }
package/src/requests.ts CHANGED
@@ -19,8 +19,8 @@ import {
19
19
  type CCIPMessage,
20
20
  type CCIPRequest,
21
21
  type CCIPVersion,
22
+ type ChainLog,
22
23
  type ChainTransaction,
23
- type Log_,
24
24
  type MessageInput,
25
25
  ChainFamily,
26
26
  } from './types.ts'
@@ -78,7 +78,6 @@ function decodeJsonMessage(data: Record<string, unknown> | undefined) {
78
78
  data_.sourceNetworkInfo?.chainSelector
79
79
  if (!sourceChainSelector) throw new CCIPMessageInvalidError(data)
80
80
  data_.sourceChainSelector ??= sourceChainSelector
81
- data_.nonce ??= 0n
82
81
  const sourceFamily = networkInfo(sourceChainSelector).family
83
82
 
84
83
  const destChainSelector =
@@ -181,7 +180,7 @@ export function decodeMessage(data: string | Uint8Array | Record<string, unknown
181
180
  // try bytearray decoding on each supported chain
182
181
  for (const chain of Object.values(supportedChains)) {
183
182
  try {
184
- const decoded = chain.decodeMessage({ data } as unknown as Log_)
183
+ const decoded = chain.decodeMessage({ data } as unknown as ChainLog)
185
184
  if (decoded) return decoded
186
185
  } catch (_) {
187
186
  // continue
@@ -3,7 +3,7 @@
3
3
  // .then((res) => res.text())
4
4
  // .then((text) => text.trim())
5
5
  export type BaseTokenPool = {
6
- version: '1.6.0'
6
+ version: '1.6.1'
7
7
  name: 'base_token_pool'
8
8
  instructions: []
9
9
  types: [
@@ -868,7 +868,7 @@ export type BaseTokenPool = {
868
868
  }
869
869
 
870
870
  export const IDL: BaseTokenPool = {
871
- version: '1.6.0',
871
+ version: '1.6.1',
872
872
  name: 'base_token_pool',
873
873
  instructions: [],
874
874
  types: [
@@ -3,7 +3,7 @@
3
3
  // .then((res) => res.text())
4
4
  // .then((text) => text.trim())
5
5
  export type BurnmintTokenPool = {
6
- version: '1.6.0'
6
+ version: '1.6.1'
7
7
  name: 'burnmint_token_pool'
8
8
  instructions: [
9
9
  {
@@ -951,7 +951,7 @@ export type BurnmintTokenPool = {
951
951
  }
952
952
 
953
953
  export const IDL: BurnmintTokenPool = {
954
- version: '1.6.0',
954
+ version: '1.6.1',
955
955
  name: 'burnmint_token_pool',
956
956
  instructions: [
957
957
  {
@@ -3,7 +3,7 @@
3
3
  // .then((res) => res.text())
4
4
  // .then((text) => text.trim())
5
5
  export type CctpTokenPool = {
6
- version: '1.6.0'
6
+ version: '1.6.1'
7
7
  name: 'cctp_token_pool'
8
8
  instructions: [
9
9
  {
@@ -1376,7 +1376,7 @@ export type CctpTokenPool = {
1376
1376
  }
1377
1377
 
1378
1378
  export const IDL: CctpTokenPool = {
1379
- version: '1.6.0',
1379
+ version: '1.6.1',
1380
1380
  name: 'cctp_token_pool',
1381
1381
  instructions: [
1382
1382
  {
@@ -3,7 +3,7 @@
3
3
  // .then((res) => res.text())
4
4
  // .then((text) => text.trim())
5
5
  export type CcipCommon = {
6
- version: '0.1.1'
6
+ version: '1.6.1'
7
7
  name: 'ccip_common'
8
8
  instructions: []
9
9
  accounts: [
@@ -102,11 +102,26 @@ export type CcipCommon = {
102
102
  name: 'InvalidSVMAddress'
103
103
  msg: 'Invalid SVM address'
104
104
  },
105
+ {
106
+ code: 10011
107
+ name: 'InvalidTVMAddress'
108
+ msg: 'Invalid TVM address'
109
+ },
110
+ {
111
+ code: 10012
112
+ name: 'InvalidAptosAddress'
113
+ msg: 'Invalid Aptos address'
114
+ },
115
+ {
116
+ code: 10013
117
+ name: 'InvalidSuiAddress'
118
+ msg: 'Invalid Sui address'
119
+ },
105
120
  ]
106
121
  }
107
122
 
108
123
  export const IDL: CcipCommon = {
109
- version: '0.1.1',
124
+ version: '1.6.1',
110
125
  name: 'ccip_common',
111
126
  instructions: [],
112
127
  accounts: [
@@ -205,6 +220,21 @@ export const IDL: CcipCommon = {
205
220
  name: 'InvalidSVMAddress',
206
221
  msg: 'Invalid SVM address',
207
222
  },
223
+ {
224
+ code: 10011,
225
+ name: 'InvalidTVMAddress',
226
+ msg: 'Invalid TVM address',
227
+ },
228
+ {
229
+ code: 10012,
230
+ name: 'InvalidAptosAddress',
231
+ msg: 'Invalid Aptos address',
232
+ },
233
+ {
234
+ code: 10013,
235
+ name: 'InvalidSuiAddress',
236
+ msg: 'Invalid Sui address',
237
+ },
208
238
  ],
209
239
  }
210
240
  // generate:end
@@ -3,7 +3,7 @@
3
3
  // .then((res) => res.text())
4
4
  // .then((text) => text.trim())
5
5
  export type CcipOfframp = {
6
- version: '0.1.1'
6
+ version: '1.6.1'
7
7
  name: 'ccip_offramp'
8
8
  constants: [
9
9
  {
@@ -2748,7 +2748,7 @@ export type CcipOfframp = {
2748
2748
  }
2749
2749
 
2750
2750
  export const IDL: CcipOfframp = {
2751
- version: '0.1.1',
2751
+ version: '1.6.1',
2752
2752
  name: 'ccip_offramp',
2753
2753
  constants: [
2754
2754
  {
@@ -8,7 +8,7 @@
8
8
  // else throw new Error('isMut is already true, no need for this workaround anymore');
9
9
  // }))
10
10
  export type CcipRouter = {
11
- version: '0.1.1'
11
+ version: '1.6.1'
12
12
  name: 'ccip_router'
13
13
  docs: [
14
14
  'The `ccip_router` module contains the implementation of the Cross-Chain Interoperability Protocol (CCIP) Router.',
@@ -2339,7 +2339,7 @@ export type CcipRouter = {
2339
2339
  }
2340
2340
 
2341
2341
  export const IDL: CcipRouter = {
2342
- version: '0.1.1',
2342
+ version: '1.6.1',
2343
2343
  name: 'ccip_router',
2344
2344
  docs: [
2345
2345
  'The `ccip_router` module contains the implementation of the Cross-Chain Interoperability Protocol (CCIP) Router.',
@@ -77,15 +77,14 @@ import {
77
77
  type CCIPMessage,
78
78
  type CCIPRequest,
79
79
  type CCIPVerifications,
80
+ type ChainLog,
80
81
  type ChainTransaction,
81
82
  type CommitReport,
82
83
  type ExecutionInput,
83
84
  type ExecutionReceipt,
84
85
  type Lane,
85
- type Log_,
86
86
  type MergeArrayElements,
87
87
  type NetworkInfo,
88
- type OffchainTokenData,
89
88
  type WithLogger,
90
89
  CCIPVersion,
91
90
  ChainFamily,
@@ -113,7 +112,6 @@ import { IDL as CCIP_CCTP_TOKEN_POOL } from './idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts
113
112
  import { IDL as CCIP_OFFRAMP_IDL } from './idl/1.6.0/CCIP_OFFRAMP.ts'
114
113
  import { IDL as CCIP_ROUTER_IDL } from './idl/1.6.0/CCIP_ROUTER.ts'
115
114
  import { getTransactionsForAddress } from './logs.ts'
116
- import { fetchSolanaOffchainTokenData } from './offchain.ts'
117
115
  import { generateUnsignedCcipSend, getFee } from './send.ts'
118
116
  import { type CCIPMessage_V1_6_Solana, type UnsignedSolanaTx, isWallet } from './types.ts'
119
117
  import {
@@ -160,7 +158,7 @@ const unknownTokens: { [mint: string]: string } = {
160
158
  }
161
159
 
162
160
  /** Solana-specific log structure with transaction reference and log level. */
163
- export type SolanaLog = Log_ & { tx: SolanaTransaction; data: string; level: number }
161
+ export type SolanaLog = ChainLog & { tx: SolanaTransaction; data: string; level: number }
164
162
  /** Solana-specific transaction structure with versioned transaction response. */
165
163
  export type SolanaTransaction = MergeArrayElements<
166
164
  ChainTransaction,
@@ -430,13 +428,13 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
430
428
  * - `watch`: Watch for new logs
431
429
  * - `programs`: Special option to allow querying by address of interest, but yielding matching
432
430
  * logs from specific (string address) program or any (true)
433
- * @returns AsyncIterableIterator of parsed Log_ objects.
431
+ * @returns AsyncIterableIterator of parsed ChainLog objects.
434
432
  * @throws {@link CCIPLogsAddressRequiredError} if address is not provided
435
433
  * @throws {@link CCIPTopicsInvalidError} if topics contain invalid values
436
434
  */
437
435
  async *getLogs(
438
436
  opts: LogFilter & { programs?: string[] | true },
439
- ): AsyncGenerator<Log_ & { tx: SolanaTransaction }> {
437
+ ): AsyncGenerator<ChainLog & { tx: SolanaTransaction }> {
440
438
  let programs: true | string[]
441
439
  if (!opts.address) {
442
440
  throw new CCIPLogsAddressRequiredError()
@@ -895,7 +893,7 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
895
893
  * @throws {@link CCIPLogDataMissingError} if log data is missing
896
894
  */
897
895
  static decodeCommits(
898
- log: Pick<Log_, 'data'>,
896
+ log: Pick<ChainLog, 'data'>,
899
897
  lane?: Omit<Lane, 'destChainSelector'>,
900
898
  ): CommitReport[] | undefined {
901
899
  // Check if this is a CommitReportAccepted event by looking at the discriminant
@@ -950,7 +948,7 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
950
948
  * @throws {@link CCIPLogDataMissingError} if log data is missing
951
949
  * @throws {@link CCIPExecutionStateInvalidError} if execution state is invalid
952
950
  */
953
- static decodeReceipt(log: Pick<Log_, 'data' | 'tx' | 'index'>): ExecutionReceipt | undefined {
951
+ static decodeReceipt(log: Pick<ChainLog, 'data' | 'tx' | 'index'>): ExecutionReceipt | undefined {
954
952
  // Check if this is a ExecutionStateChanged event by looking at the discriminant
955
953
  if (!log.data || typeof log.data !== 'string') {
956
954
  throw new CCIPLogDataMissingError()
@@ -983,7 +981,7 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
983
981
  } else throw new CCIPExecutionStateInvalidError(util.inspect(decoded.data.state))
984
982
 
985
983
  let returnData
986
- if (log.tx) {
984
+ if (log.tx?.logs) {
987
985
  // use only last receipt per tx+message (i.e. skip intermediary InProgress=1 states for Solana)
988
986
  const laterReceiptLog = log.tx.logs
989
987
  .filter((l) => l.index > log.index)
@@ -1107,11 +1105,6 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
1107
1105
  return (await this.getMessagesInTx(await this.getTransaction(hash)))[0]!
1108
1106
  }
1109
1107
 
1110
- /** {@inheritDoc Chain.getOffchainTokenData} */
1111
- async getOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
1112
- return fetchSolanaOffchainTokenData(request, this)
1113
- }
1114
-
1115
1108
  /**
1116
1109
  * {@inheritDoc Chain.generateUnsignedExecute}
1117
1110
  * @returns instructions - array of instructions to execute the report
@@ -1209,13 +1202,13 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
1209
1202
  if (Array.isArray(data)) {
1210
1203
  if (data.every((e) => typeof e === 'string')) return getErrorFromLogs(data)
1211
1204
  else if (data.every((e) => typeof e === 'object' && 'data' in e && 'address' in e))
1212
- return getErrorFromLogs(data as Log_[])
1205
+ return getErrorFromLogs(data as ChainLog[])
1213
1206
  } else if (typeof data === 'object') {
1214
1207
  if ('transactionLogs' in data && 'transactionMessage' in data) {
1215
- const parsed = getErrorFromLogs(data.transactionLogs as Log_[] | string[])
1208
+ const parsed = getErrorFromLogs(data.transactionLogs as ChainLog[] | string[])
1216
1209
  if (parsed) return { message: data.transactionMessage, ...parsed }
1217
1210
  }
1218
- if ('logs' in data) return getErrorFromLogs(data.logs as Log_[] | string[])
1211
+ if ('logs' in data) return getErrorFromLogs(data.logs as ChainLog[] | string[])
1219
1212
  } else if (typeof data === 'string') {
1220
1213
  const parsedExtraArgs = this.decodeExtraArgs(getDataBytes(data))
1221
1214
  if (parsedExtraArgs) return parsedExtraArgs
@@ -1,29 +1,10 @@
1
- import { type BN, BorshCoder } from '@coral-xyz/anchor'
2
- import type { PublicKey } from '@solana/web3.js'
1
+ import { BorshCoder } from '@coral-xyz/anchor'
3
2
  import { hexlify } from 'ethers'
4
3
 
5
- import {
6
- CCIPCctpDecodeError,
7
- CCIPCctpMultipleEventsError,
8
- CCIPDataFormatUnsupportedError,
9
- } from '../errors/index.ts'
10
- import { getUsdcAttestation } from '../offchain.ts'
11
- import type { CCIPMessage, CCIPRequest, OffchainTokenData, WithLogger } from '../types.ts'
12
- import { bytesToBuffer, networkInfo, util } from '../utils.ts'
4
+ import type { OffchainTokenData } from '../types.ts'
5
+ import { bytesToBuffer } from '../utils.ts'
13
6
  import { IDL as BASE_TOKEN_POOL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
14
7
  import { IDL as CCTP_TOKEN_POOL } from './idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts'
15
- import type { SolanaLog, SolanaTransaction } from './index.ts'
16
- import { hexDiscriminator } from './utils.ts'
17
-
18
- interface CcipCctpMessageSentEvent {
19
- originalSender: PublicKey
20
- remoteChainSelector: BN
21
- msgTotalNonce: BN
22
- eventAddress: PublicKey
23
- sourceDomain: number
24
- cctpNonce: BN
25
- messageSentBytes: Uint8Array
26
- }
27
8
 
28
9
  interface CcipCctpMessageAndAttestation {
29
10
  message: {
@@ -38,84 +19,6 @@ const cctpTokenPoolCoder = new BorshCoder({
38
19
  errors: [...BASE_TOKEN_POOL.errors, ...CCTP_TOKEN_POOL.errors],
39
20
  })
40
21
 
41
- /**
42
- * Analyzes a Solana transaction to extract CcipCctpMessageSentEvent, fetch Circle attestation,
43
- * and encode the data in the format required by the destination chain.
44
- * @param request - CCIP request containing transaction data and chain routing info.
45
- * @param logger - Logger instance for logging messages.
46
- * @returns Array of encoded offchain token data (only one supported for Solana right now).
47
- * @throws Error if transaction hash is missing or CcipCctpMessageSentEvent parsing fails.
48
- */
49
- export async function fetchSolanaOffchainTokenData(
50
- request: Pick<CCIPRequest, 'tx' | 'lane'> & {
51
- message: CCIPMessage
52
- log: Pick<CCIPRequest['log'], 'topics' | 'index' | 'transactionHash' | 'address'>
53
- },
54
- { logger = console }: WithLogger = {},
55
- ): Promise<OffchainTokenData[]> {
56
- if (!request.message.tokenAmounts.length) {
57
- return []
58
- }
59
-
60
- if (request.message.tokenAmounts.length > 1) {
61
- throw new CCIPDataFormatUnsupportedError(
62
- `Expected at most 1 token transfer, found ${request.message.tokenAmounts.length}`,
63
- )
64
- }
65
-
66
- const { networkType } = networkInfo(request.lane.sourceChainSelector)
67
- const txSignature = request.log.transactionHash
68
-
69
- // Parse Solana transaction to find CCTP event
70
- const tx = request.tx as SolanaTransaction
71
- const log = request.log as SolanaLog
72
- const logMessages = tx.tx.meta!.logMessages!
73
- // there may have multiple ccipSend calls in same tx;
74
- // use `invoke [level]` to filter only logs inside this call
75
- const requestInvokeIdx = logMessages.findLastIndex(
76
- (l, i) => i < log.index && l === `Program ${request.log.address} invoke [${log.level}]`,
77
- )
78
- const cctpEvents = []
79
- for (const l of tx.logs) {
80
- if (requestInvokeIdx >= l.index || l.index >= log.index) continue
81
- if (l.topics[0] !== hexDiscriminator('CcipCctpMessageSentEvent')) continue
82
- const decoded = cctpTokenPoolCoder.events.decode(l.data)
83
- if (!decoded) throw new CCIPCctpDecodeError(util.inspect(l))
84
- cctpEvents.push(decoded.data as unknown as CcipCctpMessageSentEvent)
85
- }
86
- const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(() => undefined)
87
-
88
- // If no CcipCctpMessageSentEvent found, return defaults so we don't block execution
89
- if (cctpEvents.length === 0) {
90
- logger.debug('No USDC/CCTP events found')
91
- return offchainTokenData
92
- }
93
-
94
- // Currently, we only support ONE token per transfer
95
- if (cctpEvents.length > 1) {
96
- throw new CCIPCctpMultipleEventsError(cctpEvents.length, txSignature)
97
- }
98
-
99
- // NOTE: assuming USDC token is the first (and only) token in the CCIP message, we will process the CCTP event.
100
- // If later multi-token transfers support is added, we need to add more info in order to match each token with it's event and offchainTokenData.
101
- const cctpEvent = cctpEvents[0]
102
- if (cctpEvent) {
103
- const message = hexlify(cctpEvent.messageSentBytes)
104
- try {
105
- // Extract message bytes to fetch circle's attestation and then encode offchainTokenData.
106
- const attestation = await getUsdcAttestation(message, networkType)
107
-
108
- offchainTokenData[0] = { _tag: 'usdc', message, attestation }
109
- } catch (error) {
110
- logger.warn(`❌ Solana CCTP: Failed to fetch attestation for ${txSignature}:`, message, error)
111
- }
112
- }
113
-
114
- logger.debug('Got Solana offchain token data', offchainTokenData)
115
-
116
- return offchainTokenData
117
- }
118
-
119
22
  /**
120
23
  * Encodes CCTP message and attestation
121
24
  *
@@ -26,7 +26,7 @@ import {
26
26
  CCIPTokenMintNotFoundError,
27
27
  CCIPTransactionNotFinalizedError,
28
28
  } from '../errors/index.ts'
29
- import type { Log_, WithLogger } from '../types.ts'
29
+ import type { ChainLog, WithLogger } from '../types.ts'
30
30
  import { 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'
@@ -143,7 +143,7 @@ export function camelToSnakeCase(str: string): string {
143
143
  .replace(/^_/, '')
144
144
  }
145
145
 
146
- type ParsedLog = Pick<Log_, 'topics' | 'index' | 'address' | 'data'> & {
146
+ type ParsedLog = Pick<ChainLog, 'topics' | 'index' | 'address' | 'data'> & {
147
147
  data: string
148
148
  level: number
149
149
  }
@@ -160,7 +160,7 @@ type ParsedLog = Pick<Log_, 'topics' | 'index' | 'address' | 'data'> & {
160
160
  * This function:
161
161
  * 1. Tracks the program call stack to determine which program emitted each log
162
162
  * 2. Extracts the first 8 bytes from base64 "Program data:" logs as topics (event discriminants)
163
- * 3. Converts logs to EVM-compatible Log_ format for CCIP compatibility
163
+ * 3. Converts logs to EVM-compatible ChainLog format for CCIP compatibility
164
164
  * 4. Returns ALL logs from the transaction - filtering should be done by the caller
165
165
  *
166
166
  * @param logs - Array of logMessages from Solana transaction
@@ -215,7 +215,10 @@ export function parseSolanaLogs(logs: readonly string[]): ParsedLog[] {
215
215
  * @returns Parsed error info with program and error details.
216
216
  */
217
217
  export function getErrorFromLogs(
218
- logs_: readonly string[] | readonly Pick<Log_, 'address' | 'index' | 'data' | 'topics'>[] | null,
218
+ logs_:
219
+ | readonly string[]
220
+ | readonly Pick<ChainLog, 'address' | 'index' | 'data' | 'topics'>[]
221
+ | null,
219
222
  ): { program: string; [k: string]: string } | undefined {
220
223
  if (!logs_?.length) return
221
224
  let logs
@@ -229,7 +232,7 @@ export function getErrorFromLogs(
229
232
  (acc, l) =>
230
233
  // if acc is empty (i.e. on last log), or it is emitted by the same program and not a Program data:
231
234
  !acc.length || (l.address === acc[0]!.address && !l.topics.length) ? [l, ...acc] : acc,
232
- [] as Pick<Log_, 'address' | 'index' | 'data'>[],
235
+ [] as Pick<ChainLog, 'address' | 'index' | 'data'>[],
233
236
  )
234
237
  .map(({ data }) => data as string)
235
238
  .reduceRight(