@xswap-link/sdk 0.10.8 → 0.10.10

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 (69) hide show
  1. package/.eslintrc.json +10 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/index.global.js +331 -331
  4. package/dist/index.js +9 -9
  5. package/dist/index.mjs +9 -9
  6. package/package.json +6 -3
  7. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +106 -9
  8. package/src/components/Swap/SwapView/SwapButton/index.tsx +11 -11
  9. package/src/constants/index.ts +18 -0
  10. package/src/context/SwapProvider.tsx +7 -3
  11. package/src/services/api.ts +1 -0
  12. package/src/services/svm/README.md +483 -0
  13. package/src/services/svm/bindings/accounts/AllowedOfframp.ts +73 -0
  14. package/src/services/svm/bindings/accounts/Config.ts +153 -0
  15. package/src/services/svm/bindings/accounts/DestChain.ts +113 -0
  16. package/src/services/svm/bindings/accounts/Nonce.ts +97 -0
  17. package/src/services/svm/bindings/accounts/index.ts +15 -0
  18. package/src/services/svm/bindings/accounts/tokenAdminRegistry.ts +128 -0
  19. package/src/services/svm/bindings/errors/anchor.ts +773 -0
  20. package/src/services/svm/bindings/errors/custom.ts +375 -0
  21. package/src/services/svm/bindings/errors/index.ts +62 -0
  22. package/src/services/svm/bindings/instructions/ccipSend.ts +112 -0
  23. package/src/services/svm/bindings/instructions/getFee.ts +73 -0
  24. package/src/services/svm/bindings/instructions/index.ts +4 -0
  25. package/src/services/svm/bindings/programId.ts +6 -0
  26. package/src/services/svm/bindings/types/BaseChain.ts +92 -0
  27. package/src/services/svm/bindings/types/BaseConfig.ts +184 -0
  28. package/src/services/svm/bindings/types/CodeVersion.ts +88 -0
  29. package/src/services/svm/bindings/types/CrossChainAmount.ts +53 -0
  30. package/src/services/svm/bindings/types/DestChainConfig.ts +76 -0
  31. package/src/services/svm/bindings/types/DestChainState.ts +76 -0
  32. package/src/services/svm/bindings/types/GetFeeResult.ts +72 -0
  33. package/src/services/svm/bindings/types/LockOrBurnInV1.ts +102 -0
  34. package/src/services/svm/bindings/types/LockOrBurnOutV1.ts +79 -0
  35. package/src/services/svm/bindings/types/RampMessageHeader.ts +94 -0
  36. package/src/services/svm/bindings/types/RateLimitConfig.ts +72 -0
  37. package/src/services/svm/bindings/types/RateLimitTokenBucket.ts +76 -0
  38. package/src/services/svm/bindings/types/ReleaseOrMintInV1.ts +156 -0
  39. package/src/services/svm/bindings/types/ReleaseOrMintOutV1.ts +53 -0
  40. package/src/services/svm/bindings/types/RemoteAddress.ts +61 -0
  41. package/src/services/svm/bindings/types/RemoteConfig.ts +86 -0
  42. package/src/services/svm/bindings/types/RestoreOnAction.ts +120 -0
  43. package/src/services/svm/bindings/types/SVM2AnyMessage.ts +128 -0
  44. package/src/services/svm/bindings/types/SVM2AnyRampMessage.ts +166 -0
  45. package/src/services/svm/bindings/types/SVM2AnyTokenTransfer.ts +118 -0
  46. package/src/services/svm/bindings/types/SVMTokenAmount.ts +64 -0
  47. package/src/services/svm/bindings/types/index.ts +78 -0
  48. package/src/services/svm/core/client/accounts.ts +97 -0
  49. package/src/services/svm/core/client/events.ts +95 -0
  50. package/src/services/svm/core/client/fee.ts +279 -0
  51. package/src/services/svm/core/client/index.ts +150 -0
  52. package/src/services/svm/core/client/send.ts +607 -0
  53. package/src/services/svm/core/client/utils.ts +131 -0
  54. package/src/services/svm/core/models.ts +236 -0
  55. package/src/services/svm/index.ts +32 -0
  56. package/src/services/svm/utils/conversion.ts +62 -0
  57. package/src/services/svm/utils/errors.ts +51 -0
  58. package/src/services/svm/utils/keypair.ts +19 -0
  59. package/src/services/svm/utils/logger.ts +171 -0
  60. package/src/services/svm/utils/pdas/common.ts +15 -0
  61. package/src/services/svm/utils/pdas/feeQuoter.ts +68 -0
  62. package/src/services/svm/utils/pdas/index.ts +12 -0
  63. package/src/services/svm/utils/pdas/receiver.ts +58 -0
  64. package/src/services/svm/utils/pdas/rmnRemote.ts +23 -0
  65. package/src/services/svm/utils/pdas/router.ts +328 -0
  66. package/src/services/svm/utils/pdas/tokenpool.ts +161 -0
  67. package/src/services/svm/utils/transaction.ts +132 -0
  68. package/src/utils/validation.ts +7 -3
  69. package/tsconfig.json +2 -1
@@ -0,0 +1,78 @@
1
+ import * as CodeVersion from "./CodeVersion";
2
+ import * as RestoreOnAction from "./RestoreOnAction";
3
+
4
+ export { RampMessageHeader } from "./RampMessageHeader";
5
+ export type {
6
+ RampMessageHeaderFields,
7
+ RampMessageHeaderJSON,
8
+ } from "./RampMessageHeader";
9
+ export { SVM2AnyRampMessage } from "./SVM2AnyRampMessage";
10
+ export type {
11
+ SVM2AnyRampMessageFields,
12
+ SVM2AnyRampMessageJSON,
13
+ } from "./SVM2AnyRampMessage";
14
+ export { SVM2AnyTokenTransfer } from "./SVM2AnyTokenTransfer";
15
+ export type {
16
+ SVM2AnyTokenTransferFields,
17
+ SVM2AnyTokenTransferJSON,
18
+ } from "./SVM2AnyTokenTransfer";
19
+ export { SVM2AnyMessage } from "./SVM2AnyMessage";
20
+ export type {
21
+ SVM2AnyMessageFields,
22
+ SVM2AnyMessageJSON,
23
+ } from "./SVM2AnyMessage";
24
+ export { SVMTokenAmount } from "./SVMTokenAmount";
25
+ export type {
26
+ SVMTokenAmountFields,
27
+ SVMTokenAmountJSON,
28
+ } from "./SVMTokenAmount";
29
+ export { CrossChainAmount } from "./CrossChainAmount";
30
+ export type {
31
+ CrossChainAmountFields,
32
+ CrossChainAmountJSON,
33
+ } from "./CrossChainAmount";
34
+ export { GetFeeResult } from "./GetFeeResult";
35
+ export type { GetFeeResultFields, GetFeeResultJSON } from "./GetFeeResult";
36
+ export { DestChainState } from "./DestChainState";
37
+ export type {
38
+ DestChainStateFields,
39
+ DestChainStateJSON,
40
+ } from "./DestChainState";
41
+ export { DestChainConfig } from "./DestChainConfig";
42
+ export type {
43
+ DestChainConfigFields,
44
+ DestChainConfigJSON,
45
+ } from "./DestChainConfig";
46
+ export { CodeVersion };
47
+
48
+ export type CodeVersionKind = CodeVersion.Default | CodeVersion.V1;
49
+ export type CodeVersionJSON = CodeVersion.DefaultJSON | CodeVersion.V1JSON;
50
+
51
+ export { RestoreOnAction };
52
+
53
+ export type RestoreOnActionKind =
54
+ | RestoreOnAction.None
55
+ | RestoreOnAction.Upgrade
56
+ | RestoreOnAction.Rollback;
57
+ export type RestoreOnActionJSON =
58
+ | RestoreOnAction.NoneJSON
59
+ | RestoreOnAction.UpgradeJSON
60
+ | RestoreOnAction.RollbackJSON;
61
+
62
+ export {
63
+ RemoteAddress,
64
+ RemoteAddressFields,
65
+ RemoteAddressJSON,
66
+ } from "./RemoteAddress";
67
+ export {
68
+ RemoteConfigFields,
69
+ RemoteConfigJSON,
70
+ RemoteConfig,
71
+ } from "./RemoteConfig";
72
+ export {
73
+ RateLimitTokenBucketFields,
74
+ RateLimitTokenBucketJSON,
75
+ RateLimitTokenBucket,
76
+ } from "./RateLimitTokenBucket";
77
+
78
+ export { RateLimitConfig, RateLimitConfigFields, RateLimitConfigJSON } from "./RateLimitConfig";
@@ -0,0 +1,97 @@
1
+ import * as anchor from "@coral-xyz/anchor";
2
+ import { AnchorProvider } from "@coral-xyz/anchor";
3
+ import { PublicKey } from "@solana/web3.js";
4
+ import { CCIPContext } from "../models";
5
+ import {
6
+ tokenAdminRegistry,
7
+ tokenAdminRegistryFields,
8
+ } from "../../bindings/accounts";
9
+ import { findTokenAdminRegistryPDA } from "../../utils/pdas";
10
+ import { createLogger, Logger, LogLevel } from "../../utils/logger";
11
+ import { createErrorEnhancer } from "../../utils/errors";
12
+
13
+ /**
14
+ * Token Admin Registry account type
15
+ */
16
+ export type TokenAdminRegistry = tokenAdminRegistryFields;
17
+
18
+ /**
19
+ * Client for reading CCIP-related accounts
20
+ */
21
+ export class CCIPAccountReader {
22
+ readonly provider: AnchorProvider;
23
+ readonly programId: PublicKey;
24
+ private readonly logger: Logger;
25
+
26
+ /**
27
+ * Creates a new CCIPAccountReader using a context
28
+ * @param context SDK context with provider, config and logger
29
+ */
30
+ constructor(readonly context: CCIPContext) {
31
+ this.logger =
32
+ context.logger ??
33
+ createLogger("account-reader", { level: LogLevel.INFO });
34
+
35
+ // Use the provider from the context to create an AnchorProvider
36
+ this.provider = new AnchorProvider(
37
+ context.provider.connection,
38
+ context.provider.wallet as any, // Cast to any to satisfy AnchorProvider
39
+ {}
40
+ );
41
+
42
+ // Set Anchor provider globally
43
+ anchor.setProvider(this.provider);
44
+
45
+ // Use router from config
46
+ this.programId = context.config.ccipRouterProgramId;
47
+
48
+ this.logger.debug(
49
+ `CCIPAccountReader initialized: programId=${this.programId.toString()}`
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Fetches a token admin registry account
55
+ * @param mint Token mint
56
+ * @returns Token admin registry account
57
+ */
58
+ async getTokenAdminRegistry(mint: PublicKey): Promise<TokenAdminRegistry> {
59
+ const enhanceError = createErrorEnhancer(this.logger);
60
+
61
+ try {
62
+ this.logger.debug(
63
+ `Fetching token admin registry for mint: ${mint.toString()}`
64
+ );
65
+ this.logger.trace(`Program ID: ${this.programId.toString()}`);
66
+ const [pda] = findTokenAdminRegistryPDA(mint, this.programId);
67
+ this.logger.trace(`Token admin registry PDA: ${pda.toString()}`);
68
+
69
+ // Use the generated tokenAdminRegistry.fetch method
70
+ const tokenRegistry = await tokenAdminRegistry.fetch(
71
+ this.context.provider.connection,
72
+ pda,
73
+ this.programId
74
+ );
75
+
76
+ if (!tokenRegistry) {
77
+ throw new Error(
78
+ `Token admin registry not found for mint: ${mint.toString()}`
79
+ );
80
+ }
81
+
82
+ this.logger.trace("Retrieved token admin registry:", {
83
+ mint: tokenRegistry.mint.toString(),
84
+ administrator: tokenRegistry.administrator.toString(),
85
+ lookupTable: tokenRegistry.lookupTable.toString(),
86
+ });
87
+
88
+ return tokenRegistry;
89
+ } catch (error) {
90
+ throw enhanceError(error, {
91
+ operation: "getTokenAdminRegistry",
92
+ mint: mint.toString(),
93
+ programId: this.programId.toString(),
94
+ });
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,95 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ import { createErrorEnhancer } from "../../utils/errors";
4
+ import { CCIPContext } from "../models";
5
+
6
+ /**
7
+ * Parses a CCIP message sent event from a transaction
8
+ *
9
+ * @param context SDK context with provider, config and logger
10
+ * @param txSignature Transaction signature
11
+ * @returns Parsed event data with messageId if available
12
+ */
13
+ export async function parseCCIPMessageSentEvent(
14
+ context: CCIPContext,
15
+ txSignature: string,
16
+ ): Promise<{
17
+ messageId?: string;
18
+ }> {
19
+ if (!context.logger) {
20
+ throw new Error("Logger is required for parseCCIPMessageSentEvent");
21
+ }
22
+
23
+ const logger = context.logger;
24
+ const config = context.config;
25
+ const connection = context.provider.connection;
26
+
27
+ const enhanceError = createErrorEnhancer(logger);
28
+
29
+ try {
30
+ logger.info(
31
+ `Parsing CCIP message sent event for transaction: ${txSignature}`,
32
+ );
33
+
34
+ // Get transaction details with logs
35
+ logger.debug(`Fetching transaction details with logs`);
36
+ const tx = await connection.getParsedTransaction(txSignature, {
37
+ commitment: "confirmed",
38
+ maxSupportedTransactionVersion: 0,
39
+ });
40
+
41
+ if (!tx || !tx.meta || !tx.meta.logMessages) {
42
+ logger.warn(`No transaction logs found for ${txSignature}`);
43
+ return { messageId: undefined };
44
+ }
45
+
46
+ // Get the router program ID as string for comparison
47
+ const routerProgramId = config.ccipRouterProgramId.toString();
48
+ logger.debug(
49
+ `Looking for program return log from CCIP Router: ${routerProgramId}`,
50
+ );
51
+
52
+ // Log messages in TRACE mode
53
+ logger.trace("Transaction logs:", tx.meta.logMessages);
54
+
55
+ // Look for the program return log from the CCIP Router program
56
+ const programReturnLog = tx.meta.logMessages.find((log) =>
57
+ log.includes(`Program return: ${routerProgramId}`),
58
+ );
59
+
60
+ if (programReturnLog) {
61
+ logger.debug(`Found CCIP program return log`);
62
+
63
+ // Extract the base64 data after the program ID
64
+ const parts = programReturnLog.split(
65
+ `Program return: ${routerProgramId} `,
66
+ );
67
+ if (parts.length > 1) {
68
+ const base64Data = parts[1].trim();
69
+ logger.trace(`Extracted base64 data: ${base64Data}`);
70
+
71
+ const buffer = Buffer.from(base64Data, "base64");
72
+
73
+ // The buffer should contain the messageId (32 bytes)
74
+ const messageIdHex = "0x" + buffer.toString("hex");
75
+ logger.info(`Successfully extracted messageId: ${messageIdHex}`);
76
+
77
+ return {
78
+ messageId: messageIdHex,
79
+ };
80
+ }
81
+ }
82
+
83
+ logger.warn(
84
+ `Could not find CCIP Router program return log in transaction logs`,
85
+ );
86
+ return { messageId: undefined };
87
+ } catch (error) {
88
+ enhanceError(new Error(`Failed to parse message ID from transaction`), {
89
+ operation: "parseCCIPMessageSentEvent",
90
+ txSignature,
91
+ error: error instanceof Error ? error.message : String(error),
92
+ });
93
+ return { messageId: undefined };
94
+ }
95
+ }
@@ -0,0 +1,279 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ import {
4
+ PublicKey,
5
+ TransactionMessage,
6
+ VersionedTransaction,
7
+ } from "@solana/web3.js";
8
+ import { NATIVE_MINT } from "@solana/spl-token";
9
+ import { AccountMeta } from "@solana/web3.js";
10
+ import { createErrorEnhancer } from "../../utils/errors";
11
+ import { CCIPFeeRequest, CCIPContext, CCIPCoreConfig } from "../models";
12
+ import * as types from "../../bindings/types";
13
+ import { GetFeeResult } from "../../bindings/types/GetFeeResult";
14
+ import {
15
+ findFqConfigPDA,
16
+ findFqDestChainPDA,
17
+ findFqBillingTokenConfigPDA,
18
+ findFqPerChainPerTokenConfigPDA,
19
+ findConfigPDA,
20
+ findDestChainStatePDA,
21
+ } from "../../utils/pdas";
22
+ import {
23
+ getFee,
24
+ GetFeeAccounts,
25
+ GetFeeArgs,
26
+ } from "../../bindings/instructions/getFee";
27
+
28
+ /**
29
+ * Calculates the fee for a CCIP message
30
+ *
31
+ * @param context SDK context with provider, config and logger
32
+ * @param request Fee request parameters
33
+ * @returns Fee result
34
+ */
35
+ export async function calculateFee(
36
+ context: CCIPContext,
37
+ request: CCIPFeeRequest,
38
+ ): Promise<types.GetFeeResult> {
39
+ const logger = context.logger;
40
+ const config = context.config;
41
+ const connection = context.provider.connection;
42
+ const signerPublicKey = context.provider.getAddress();
43
+
44
+ if (!logger) {
45
+ throw new Error("Logger is required for calculateFee");
46
+ }
47
+
48
+ const enhanceError = createErrorEnhancer(logger);
49
+ const selectorBigInt = BigInt(request.destChainSelector.toString());
50
+
51
+ logger.info(
52
+ `Calculating fee for destination chain ${request.destChainSelector.toString()}`,
53
+ );
54
+
55
+ const feeTokenMint = request.message.feeToken.equals(PublicKey.default)
56
+ ? NATIVE_MINT
57
+ : request.message.feeToken;
58
+
59
+ logger.debug(
60
+ `Using fee token: ${feeTokenMint.toString()} (${
61
+ request.message.feeToken.equals(PublicKey.default)
62
+ ? "Native SOL"
63
+ : "SPL Token"
64
+ })`,
65
+ );
66
+
67
+ // Build the accounts needed for the getFee instruction
68
+ logger.debug(`Building accounts for getFee instruction`);
69
+ const accounts = await buildGetFeeAccounts(
70
+ config,
71
+ selectorBigInt,
72
+ feeTokenMint,
73
+ );
74
+
75
+ logger.trace("Fee accounts:", {
76
+ config: accounts.config.toString(),
77
+ destChainState: accounts.destChainState.toString(),
78
+ feeQuoter: accounts.feeQuoter.toString(),
79
+ feeQuoterConfig: accounts.feeQuoterConfig.toString(),
80
+ feeQuoterDestChain: accounts.feeQuoterDestChain.toString(),
81
+ feeQuoterBillingTokenConfig:
82
+ accounts.feeQuoterBillingTokenConfig.toString(),
83
+ feeQuoterLinkTokenConfig: accounts.feeQuoterLinkTokenConfig.toString(),
84
+ });
85
+
86
+ // Create the getFee instruction arguments
87
+ logger.debug(`Creating getFee instruction arguments`);
88
+ const args: GetFeeArgs = {
89
+ destChainSelector: request.destChainSelector,
90
+ message: {
91
+ receiver: request.message.receiver,
92
+ data: request.message.data,
93
+ tokenAmounts: request.message.tokenAmounts,
94
+ feeToken: request.message.feeToken,
95
+ extraArgs: request.message.extraArgs,
96
+ },
97
+ };
98
+
99
+ // Create instruction
100
+ logger.debug(`Creating getFee instruction`);
101
+ const instruction = getFee(args, accounts, config.ccipRouterProgramId);
102
+
103
+ // Build and add token-specific remaining accounts for each token in tokenAmounts
104
+ const remainingAccounts: AccountMeta[] = [];
105
+
106
+ // Process each token in tokenAmounts
107
+ logger.debug(
108
+ `Processing ${request.message.tokenAmounts.length} token amounts for remaining accounts`,
109
+ );
110
+ for (const tokenAmount of request.message.tokenAmounts) {
111
+ try {
112
+ logger.trace(
113
+ `Processing token: ${tokenAmount.token.toString()}, amount: ${tokenAmount.amount.toString()}`,
114
+ );
115
+
116
+ // Find the token billing config PDA
117
+ const [tokenBillingConfig] = findFqBillingTokenConfigPDA(
118
+ tokenAmount.token,
119
+ config.feeQuoterProgramId,
120
+ );
121
+
122
+ // Find the per chain per token config PDA
123
+ const [perChainPerTokenConfig] = findFqPerChainPerTokenConfigPDA(
124
+ selectorBigInt,
125
+ tokenAmount.token,
126
+ config.feeQuoterProgramId,
127
+ );
128
+
129
+ logger.trace(`Found token configs:`, {
130
+ tokenBillingConfig: tokenBillingConfig.toString(),
131
+ perChainPerTokenConfig: perChainPerTokenConfig.toString(),
132
+ });
133
+
134
+ // Add these accounts to the remaining accounts
135
+ remainingAccounts.push(
136
+ { pubkey: tokenBillingConfig, isWritable: false, isSigner: false },
137
+ { pubkey: perChainPerTokenConfig, isWritable: false, isSigner: false },
138
+ );
139
+ } catch (error) {
140
+ // Log the error with context but continue with other tokens
141
+ enhanceError(error, {
142
+ operation: "getFee:processToken",
143
+ token: tokenAmount.token.toString(),
144
+ amount: tokenAmount.amount.toString(),
145
+ destChainSelector: selectorBigInt.toString(),
146
+ });
147
+ // Continue with other tokens if one fails
148
+ }
149
+ }
150
+
151
+ // Add remaining accounts to the instruction
152
+ if (remainingAccounts.length > 0) {
153
+ logger.debug(
154
+ `Adding ${remainingAccounts.length} remaining accounts to the instruction`,
155
+ );
156
+ instruction.keys.push(...remainingAccounts);
157
+ }
158
+
159
+ // Log complete instruction accounts in TRACE mode
160
+ logger.trace(
161
+ "Complete instruction accounts:",
162
+ instruction.keys.map((key, index) => ({
163
+ index,
164
+ pubkey: key.pubkey.toString(),
165
+ isSigner: key.isSigner,
166
+ isWritable: key.isWritable,
167
+ })),
168
+ );
169
+
170
+ // Get recent blockhash
171
+ logger.debug(`Getting recent blockhash for transaction`);
172
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
173
+
174
+ // Create transaction
175
+ logger.debug(`Creating versioned transaction for simulation`);
176
+ const messageV0 = new TransactionMessage({
177
+ payerKey: signerPublicKey,
178
+ recentBlockhash: blockhash,
179
+ instructions: [instruction],
180
+ }).compileToV0Message();
181
+
182
+ const tx = new VersionedTransaction(messageV0);
183
+ await context.provider.signTransaction(tx);
184
+
185
+ // Simulate transaction to get the return data
186
+ logger.debug(`Simulating transaction to get fee result`);
187
+ const simulation = await connection.simulateTransaction(tx, {
188
+ commitment: "confirmed",
189
+ sigVerify: false,
190
+ });
191
+
192
+ // Parse the return data
193
+ if (simulation.value.logs) {
194
+ logger.trace(`Simulation logs:`, simulation.value.logs);
195
+
196
+ const ccipReturnLog = simulation.value.logs.find((log) =>
197
+ log.includes(`Program return: ${config.ccipRouterProgramId.toString()}`),
198
+ );
199
+
200
+ if (ccipReturnLog) {
201
+ logger.debug(`Found CCIP program return log`);
202
+ const parts = ccipReturnLog.split(
203
+ `Program return: ${config.ccipRouterProgramId.toString()} `,
204
+ );
205
+ if (parts.length > 1) {
206
+ const base64Data = parts[1].trim();
207
+ const buffer = Buffer.from(base64Data, "base64");
208
+
209
+ // Use the proper bindings to decode the result
210
+ logger.debug(`Decoding fee result data`);
211
+ const feeResultData = GetFeeResult.layout().decode(buffer);
212
+ const result = GetFeeResult.fromDecoded(feeResultData);
213
+
214
+ logger.info(
215
+ `Fee calculation complete: ${result.amount.toString()} tokens`,
216
+ );
217
+ return result;
218
+ }
219
+ }
220
+
221
+ logger.error(`Could not find CCIP program return log in simulation logs`);
222
+ } else {
223
+ logger.error(`Simulation did not return any logs`);
224
+ }
225
+
226
+ throw enhanceError(
227
+ new Error("Could not parse fee from transaction return data"),
228
+ {
229
+ operation: "getFee",
230
+ destChainSelector: request.destChainSelector.toString(),
231
+ feeToken: request.message.feeToken.toString(),
232
+ simulationStatus: simulation?.value?.err || "No specific error",
233
+ hasLogs: !!simulation?.value?.logs,
234
+ logCount: simulation?.value?.logs?.length || 0,
235
+ },
236
+ );
237
+ }
238
+
239
+ /**
240
+ * Build accounts required for the getFee instruction
241
+ * @param config SDK configuration
242
+ * @param selectorBigInt Chain selector as BigInt
243
+ * @param feeTokenMint Fee token mint address
244
+ * @returns GetFeeAccounts object with all required accounts
245
+ */
246
+ async function buildGetFeeAccounts(
247
+ config: CCIPCoreConfig,
248
+ selectorBigInt: bigint,
249
+ feeTokenMint: PublicKey,
250
+ ): Promise<GetFeeAccounts> {
251
+ const [configPDA] = findConfigPDA(config.ccipRouterProgramId);
252
+ const [destChainState] = findDestChainStatePDA(
253
+ selectorBigInt,
254
+ config.ccipRouterProgramId,
255
+ );
256
+ const [feeQuoterConfig] = findFqConfigPDA(config.feeQuoterProgramId);
257
+ const [fqDestChain] = findFqDestChainPDA(
258
+ selectorBigInt,
259
+ config.feeQuoterProgramId,
260
+ );
261
+ const [fqBillingTokenConfig] = findFqBillingTokenConfigPDA(
262
+ feeTokenMint,
263
+ config.feeQuoterProgramId,
264
+ );
265
+ const [fqLinkBillingTokenConfig] = findFqBillingTokenConfigPDA(
266
+ config.linkTokenMint,
267
+ config.feeQuoterProgramId,
268
+ );
269
+
270
+ return {
271
+ config: configPDA,
272
+ destChainState: destChainState,
273
+ feeQuoter: config.feeQuoterProgramId,
274
+ feeQuoterConfig: feeQuoterConfig,
275
+ feeQuoterDestChain: fqDestChain,
276
+ feeQuoterBillingTokenConfig: fqBillingTokenConfig,
277
+ feeQuoterLinkTokenConfig: fqLinkBillingTokenConfig,
278
+ };
279
+ }
@@ -0,0 +1,150 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ import { TransactionInstruction } from "@solana/web3.js";
4
+ import { createLogger, Logger, LogLevel } from "../../utils/logger";
5
+ import {
6
+ CCIPContext,
7
+ CCIPSendRequest,
8
+ CCIPSendOptions,
9
+ CCIPFeeRequest,
10
+ CCIPSendResult,
11
+ ExtraArgsOptions,
12
+ CCIPCoreConfig,
13
+ CCIPProvider,
14
+ } from "../models";
15
+ import * as types from "../../bindings/types";
16
+
17
+ // Import functionality from separate modules
18
+ import { calculateFee } from "./fee";
19
+ import { parseCCIPMessageSentEvent } from "./events";
20
+ import { createExtraArgs } from "./utils";
21
+ import { sendCCIPMessage } from "./send";
22
+ import { CCIPAccountReader } from "./accounts";
23
+
24
+ /**
25
+ * Main client class for interacting with CCIP on Solana
26
+ *
27
+ * Features:
28
+ * - Message sending with optional skipPreflight for low compute limit transactions
29
+ * - Fee calculation for CCIP messages
30
+ * - Message ID parsing from transaction results
31
+ * - ExtraArgs generation for cross-chain messages
32
+ */
33
+ export class CCIPClient {
34
+ private readonly context: CCIPContext;
35
+ private readonly accountReader: CCIPAccountReader;
36
+
37
+ /**
38
+ * Creates a new CCIP client with context
39
+ * @param context CCIPContext containing provider, config, and logger
40
+ */
41
+ constructor(context: CCIPContext) {
42
+ // Initialize context
43
+ this.context = {
44
+ provider: context.provider,
45
+ config: context.config,
46
+ logger:
47
+ context.logger ?? createLogger("client", { level: LogLevel.INFO }),
48
+ };
49
+
50
+ // Initialize account reader with the same context
51
+ this.accountReader = new CCIPAccountReader(this.context);
52
+ }
53
+
54
+ /**
55
+ * Get the provider for this client
56
+ */
57
+ get provider(): CCIPProvider {
58
+ return this.context.provider;
59
+ }
60
+
61
+ /**
62
+ * Get the configuration for this client
63
+ */
64
+ get config(): CCIPCoreConfig {
65
+ return this.context.config;
66
+ }
67
+
68
+ /**
69
+ * Get the logger for this client
70
+ */
71
+ get logger(): Logger {
72
+ return this.context.logger;
73
+ }
74
+
75
+ /**
76
+ * Get the account reader for this client
77
+ */
78
+ getAccountReader(): CCIPAccountReader {
79
+ return this.accountReader;
80
+ }
81
+
82
+ /**
83
+ * Calculates the fee for a CCIP message
84
+ * @param request Fee request
85
+ * @returns Fee result
86
+ */
87
+ async getFee(request: CCIPFeeRequest): Promise<types.GetFeeResult> {
88
+ return calculateFee(this.context, request);
89
+ }
90
+
91
+ /**
92
+ * Sends a CCIP message
93
+ * @param request Send request
94
+ * @param computeBudgetInstruction Optional compute budget instruction
95
+ * @param sendOptions Optional send options (skipPreflight, etc.)
96
+ * @returns Transaction signature
97
+ */
98
+ async send(
99
+ request: CCIPSendRequest,
100
+ computeBudgetInstruction?: TransactionInstruction,
101
+ sendOptions?: CCIPSendOptions,
102
+ ): Promise<string> {
103
+ return sendCCIPMessage(
104
+ this.context,
105
+ request,
106
+ this.accountReader,
107
+ computeBudgetInstruction,
108
+ sendOptions,
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Sends a CCIP message and returns the message ID
114
+ * @param request Send request
115
+ * @param computeBudgetInstruction Optional compute budget instruction
116
+ * @param sendOptions Optional send options (skipPreflight, etc.)
117
+ * @returns Send result with transaction signature and message ID
118
+ */
119
+ async sendWithMessageId(
120
+ request: CCIPSendRequest,
121
+ computeBudgetInstruction?: TransactionInstruction,
122
+ sendOptions?: CCIPSendOptions,
123
+ ): Promise<CCIPSendResult> {
124
+ const txSignature = await this.send(
125
+ request,
126
+ computeBudgetInstruction,
127
+ sendOptions,
128
+ );
129
+
130
+ // Parse the CCIPMessageSent event to get the messageId
131
+ const eventData = await parseCCIPMessageSentEvent(
132
+ this.context,
133
+ txSignature,
134
+ );
135
+
136
+ return {
137
+ txSignature,
138
+ messageId: eventData.messageId,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Creates the extra arguments for a CCIP message
144
+ * @param options Options for creating extra arguments
145
+ * @returns Extra arguments buffer
146
+ */
147
+ createExtraArgs(options?: ExtraArgsOptions): Buffer {
148
+ return createExtraArgs(options, this.context.logger);
149
+ }
150
+ }