@aptos-labs/cross-chain-core 5.9.0 → 6.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 (35) hide show
  1. package/dist/CrossChainCore.d.ts +75 -0
  2. package/dist/CrossChainCore.d.ts.map +1 -1
  3. package/dist/index.js +360 -161
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +361 -165
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/providers/wormhole/signers/AptosLocalSigner.d.ts +8 -6
  8. package/dist/providers/wormhole/signers/AptosLocalSigner.d.ts.map +1 -1
  9. package/dist/providers/wormhole/signers/AptosSigner.d.ts +2 -1
  10. package/dist/providers/wormhole/signers/AptosSigner.d.ts.map +1 -1
  11. package/dist/providers/wormhole/signers/EthereumSigner.d.ts +1 -1
  12. package/dist/providers/wormhole/signers/EthereumSigner.d.ts.map +1 -1
  13. package/dist/providers/wormhole/signers/Signer.d.ts +10 -2
  14. package/dist/providers/wormhole/signers/Signer.d.ts.map +1 -1
  15. package/dist/providers/wormhole/signers/SolanaLocalSigner.d.ts +4 -0
  16. package/dist/providers/wormhole/signers/SolanaLocalSigner.d.ts.map +1 -1
  17. package/dist/providers/wormhole/signers/SolanaSigner.d.ts.map +1 -1
  18. package/dist/providers/wormhole/signers/solanaUtils.d.ts.map +1 -1
  19. package/dist/providers/wormhole/types.d.ts +78 -1
  20. package/dist/providers/wormhole/types.d.ts.map +1 -1
  21. package/dist/providers/wormhole/wormhole.d.ts +27 -1
  22. package/dist/providers/wormhole/wormhole.d.ts.map +1 -1
  23. package/dist/version.d.ts +1 -1
  24. package/package.json +3 -3
  25. package/src/CrossChainCore.ts +91 -4
  26. package/src/providers/wormhole/signers/AptosLocalSigner.ts +27 -14
  27. package/src/providers/wormhole/signers/AptosSigner.ts +10 -1
  28. package/src/providers/wormhole/signers/EthereumSigner.ts +57 -6
  29. package/src/providers/wormhole/signers/Signer.ts +19 -2
  30. package/src/providers/wormhole/signers/SolanaLocalSigner.ts +7 -0
  31. package/src/providers/wormhole/signers/SolanaSigner.ts +4 -1
  32. package/src/providers/wormhole/signers/solanaUtils.ts +43 -19
  33. package/src/providers/wormhole/types.ts +100 -1
  34. package/src/providers/wormhole/wormhole.ts +138 -28
  35. package/src/version.ts +1 -1
@@ -15,10 +15,12 @@ import {
15
15
  Chain,
16
16
  CrossChainProvider,
17
17
  CrossChainCore,
18
+ EVM_CHAIN_NAMES,
18
19
  } from "../../CrossChainCore";
19
20
  import { logger } from "../../utils/logger";
20
21
  import { serializeReceipt } from "../../utils/receiptSerialization";
21
22
  import { AptosLocalSigner } from "./signers/AptosLocalSigner";
23
+ import { isAccount } from "./signers/AptosSigner";
22
24
  import { Signer } from "./signers/Signer";
23
25
  import { ChainConfig } from "../../config";
24
26
  import { createCCTPRoute } from "./utils";
@@ -38,7 +40,10 @@ import {
38
40
  WormholeInitiateWithdrawResponse,
39
41
  WormholeClaimWithdrawRequest,
40
42
  WormholeClaimWithdrawResponse,
43
+ RetryWithdrawClaimRequest,
44
+ RetryWithdrawClaimResponse,
41
45
  WithdrawError,
46
+ TransferError,
42
47
  } from "./types";
43
48
  import { SolanaDerivedWallet } from "@aptos-labs/derived-wallet-solana";
44
49
  import { EIP1193DerivedWallet } from "@aptos-labs/derived-wallet-ethereum";
@@ -80,16 +85,31 @@ export class WormholeProvider implements CrossChainProvider<
80
85
  const isMainnet = dappNetwork === Network.MAINNET;
81
86
  const platforms: PlatformLoader<any>[] = [aptos, solana, evm, sui];
82
87
 
83
- // Get custom RPC endpoints from config
88
+ // RPC resolution order per chain:
89
+ // 1. User-provided config (solanaConfig / suiConfig / evmConfig)
90
+ // 2. Built-in defaultRpc from CHAINS config
91
+ const dappConfig = this.crossChainCore._dappConfig;
92
+ const chains = this.crossChainCore.CHAINS;
93
+
84
94
  const solanaRpc =
85
- this.crossChainCore._dappConfig?.solanaConfig?.rpc ??
86
- this.crossChainCore.CHAINS["Solana"]?.defaultRpc;
95
+ dappConfig?.solanaConfig?.rpc ?? chains["Solana"]?.defaultRpc;
96
+
97
+ const suiRpc = dappConfig?.suiConfig?.rpc ?? chains["Sui"]?.defaultRpc;
98
+
99
+ const evmChainsConfig: Record<string, { rpc: string }> = {};
100
+ for (const name of EVM_CHAIN_NAMES) {
101
+ const rpc =
102
+ dappConfig?.evmConfig?.[name]?.rpc ?? chains[name]?.defaultRpc;
103
+ if (rpc) {
104
+ evmChainsConfig[name] = { rpc };
105
+ }
106
+ }
87
107
 
88
108
  const wh = await wormhole(isMainnet ? "Mainnet" : "Testnet", platforms, {
89
109
  chains: {
90
- Solana: {
91
- rpc: solanaRpc,
92
- },
110
+ ...(solanaRpc ? { Solana: { rpc: solanaRpc } } : {}),
111
+ ...(suiRpc ? { Sui: { rpc: suiRpc } } : {}),
112
+ ...evmChainsConfig,
93
113
  },
94
114
  });
95
115
  this._wormholeContext = wh;
@@ -162,7 +182,8 @@ export class WormholeProvider implements CrossChainProvider<
162
182
  async submitCCTPTransfer(
163
183
  input: WormholeSubmitTransferRequest,
164
184
  ): Promise<WormholeStartTransferResponse> {
165
- const { sourceChain, wallet, destinationAddress } = input;
185
+ const { sourceChain, wallet, destinationAddress, onTransactionSigned } =
186
+ input;
166
187
 
167
188
  if (!this._wormholeContext) {
168
189
  await this.setWormholeContext(sourceChain);
@@ -201,6 +222,8 @@ export class WormholeProvider implements CrossChainProvider<
201
222
  {},
202
223
  wallet,
203
224
  this.crossChainCore,
225
+ undefined,
226
+ onTransactionSigned,
204
227
  );
205
228
 
206
229
  logger.log("signer", signer);
@@ -225,10 +248,18 @@ export class WormholeProvider implements CrossChainProvider<
225
248
  async claimCCTPTransfer(
226
249
  input: WormholeClaimTransferRequest,
227
250
  ): Promise<{ destinationChainTxnId: string }> {
228
- let { receipt, mainSigner, sponsorAccount } = input;
251
+ let { receipt, mainSigner, sponsorAccount, onTransactionSigned } = input;
229
252
  if (!this.wormholeRoute) {
230
253
  throw new Error("Wormhole route not initialized");
231
254
  }
255
+ if (sponsorAccount && !isAccount(sponsorAccount)) {
256
+ throw new Error(
257
+ "AptosLocalSigner does not support GasStationApiKey as a sponsor account. " +
258
+ "Wormhole claim transactions are script-based and cannot be submitted " +
259
+ "via the gas station. Please provide an Account instance as the sponsor, " +
260
+ "or omit the sponsor account.",
261
+ );
262
+ }
232
263
 
233
264
  logger.log("mainSigner", mainSigner.accountAddress.toString());
234
265
 
@@ -248,7 +279,8 @@ export class WormholeProvider implements CrossChainProvider<
248
279
  {},
249
280
  mainSigner, // the account that signs the "claim" transaction
250
281
  sponsorAccount, // the fee payer account
251
- this.crossChainCore._dappConfig?.aptosNetwork,
282
+ this.crossChainCore,
283
+ onTransactionSigned,
252
284
  );
253
285
 
254
286
  if (routes.isManual(this.wormholeRoute)) {
@@ -304,12 +336,21 @@ export class WormholeProvider implements CrossChainProvider<
304
336
  // Submit transfer transaction from origin chain
305
337
  let { originChainTxnId, receipt } = await this.submitCCTPTransfer(input);
306
338
  // Claim transfer transaction on destination chain
307
- const { destinationChainTxnId } = await this.claimCCTPTransfer({
308
- receipt,
309
- mainSigner: input.mainSigner,
310
- sponsorAccount: input.sponsorAccount,
311
- });
312
- return { originChainTxnId, destinationChainTxnId };
339
+ try {
340
+ const { destinationChainTxnId } = await this.claimCCTPTransfer({
341
+ receipt,
342
+ mainSigner: input.mainSigner,
343
+ sponsorAccount: input.sponsorAccount,
344
+ onTransactionSigned: input.onTransactionSigned,
345
+ });
346
+ return { originChainTxnId, destinationChainTxnId };
347
+ } catch (error: any) {
348
+ throw new TransferError(
349
+ error?.message ?? "Transfer claim failed after source-chain burn",
350
+ originChainTxnId,
351
+ error,
352
+ );
353
+ }
313
354
  }
314
355
 
315
356
  // --- Split withdraw flow: initiateWithdraw + trackWithdraw + claimWithdraw ---
@@ -322,7 +363,8 @@ export class WormholeProvider implements CrossChainProvider<
322
363
  async initiateWithdraw(
323
364
  input: WormholeInitiateWithdrawRequest,
324
365
  ): Promise<WormholeInitiateWithdrawResponse> {
325
- const { wallet, destinationAddress, sponsorAccount } = input;
366
+ const { wallet, destinationAddress, sponsorAccount, onTransactionSigned } =
367
+ input;
326
368
 
327
369
  if (!this._wormholeContext) {
328
370
  throw new Error("Wormhole context not initialized");
@@ -333,13 +375,12 @@ export class WormholeProvider implements CrossChainProvider<
333
375
 
334
376
  const signer = new Signer(
335
377
  this.getChainConfig("Aptos"),
336
- (
337
- await wallet.features["aptos:account"].account()
338
- ).address.toString(),
378
+ (await wallet.features["aptos:account"].account()).address.toString(),
339
379
  {},
340
380
  wallet,
341
381
  this.crossChainCore,
342
382
  sponsorAccount,
383
+ onTransactionSigned,
343
384
  );
344
385
 
345
386
  const wormholeDestAddress = Wormhole.chainAddress(
@@ -367,9 +408,7 @@ export class WormholeProvider implements CrossChainProvider<
367
408
  * Phase 2: Tracks a withdraw receipt until attestation is ready.
368
409
  * This polls Wormhole and returns once the receipt reaches the Attested state.
369
410
  */
370
- async trackWithdraw(
371
- receipt: routes.Receipt,
372
- ): Promise<routes.Receipt> {
411
+ async trackWithdraw(receipt: routes.Receipt): Promise<routes.Receipt> {
373
412
  if (!this.wormholeRoute) {
374
413
  throw new Error("Wormhole route not initialized");
375
414
  }
@@ -412,13 +451,13 @@ export class WormholeProvider implements CrossChainProvider<
412
451
  async claimWithdraw(
413
452
  input: WormholeClaimWithdrawRequest,
414
453
  ): Promise<WormholeClaimWithdrawResponse> {
415
- const { sourceChain, destinationAddress, receipt } = input;
454
+ const { claimChain, destinationAddress, receipt } = input;
416
455
 
417
456
  // Server-side claim path: Solana destination with configured serverClaimUrl
418
457
  const serverClaimUrl =
419
458
  this.crossChainCore._dappConfig?.solanaConfig?.serverClaimUrl;
420
459
 
421
- if (sourceChain === "Solana" && serverClaimUrl) {
460
+ if (claimChain === "Solana" && serverClaimUrl) {
422
461
  logger.log("claimWithdraw: using server-side claim via", serverClaimUrl);
423
462
 
424
463
  const response = await fetch(serverClaimUrl, {
@@ -427,7 +466,7 @@ export class WormholeProvider implements CrossChainProvider<
427
466
  body: JSON.stringify({
428
467
  receipt: serializeReceipt(receipt),
429
468
  destinationAddress,
430
- sourceChain,
469
+ claimChain,
431
470
  }),
432
471
  });
433
472
 
@@ -455,11 +494,14 @@ export class WormholeProvider implements CrossChainProvider<
455
494
  }
456
495
 
457
496
  const claimSigner = new Signer(
458
- this.getChainConfig(sourceChain),
497
+ this.getChainConfig(claimChain),
459
498
  destinationAddress,
460
499
  {},
461
500
  input.wallet,
462
501
  this.crossChainCore,
502
+ undefined,
503
+ input.onTransactionSigned,
504
+ false,
463
505
  );
464
506
 
465
507
  if (routes.isManual(this.wormholeRoute)) {
@@ -489,7 +531,14 @@ export class WormholeProvider implements CrossChainProvider<
489
531
  async withdraw(
490
532
  input: WormholeWithdrawRequest,
491
533
  ): Promise<WormholeWithdrawResponse> {
492
- const { sourceChain, wallet, destinationAddress, sponsorAccount, onPhaseChange } = input;
534
+ const {
535
+ sourceChain,
536
+ wallet,
537
+ destinationAddress,
538
+ sponsorAccount,
539
+ onPhaseChange,
540
+ onTransactionSigned,
541
+ } = input;
493
542
 
494
543
  // Phase 1: Initiate — user signs Aptos burn
495
544
  onPhaseChange?.("initiating");
@@ -497,6 +546,7 @@ export class WormholeProvider implements CrossChainProvider<
497
546
  wallet,
498
547
  destinationAddress,
499
548
  sponsorAccount,
549
+ onTransactionSigned,
500
550
  });
501
551
 
502
552
  // Phases 2 & 3 are wrapped so that, if they fail, the caller still
@@ -511,10 +561,11 @@ export class WormholeProvider implements CrossChainProvider<
511
561
  currentPhase = "claiming";
512
562
  onPhaseChange?.("claiming");
513
563
  const { destinationChainTxnId } = await this.claimWithdraw({
514
- sourceChain,
564
+ claimChain: sourceChain,
515
565
  destinationAddress: destinationAddress.toString(),
516
566
  receipt: attestedReceipt,
517
567
  wallet,
568
+ onTransactionSigned,
518
569
  });
519
570
 
520
571
  return { originChainTxnId, destinationChainTxnId };
@@ -528,6 +579,65 @@ export class WormholeProvider implements CrossChainProvider<
528
579
  }
529
580
  }
530
581
 
582
+ /**
583
+ * Retries a failed `claimWithdraw` call with configurable exponential backoff.
584
+ *
585
+ * Use this when the claim phase of a withdrawal fails (e.g., RPC instability,
586
+ * network congestion) but the Aptos burn transaction has already been submitted.
587
+ * The attested receipt can be recovered from the `WithdrawError` thrown by
588
+ * `withdraw()` and passed directly to this method.
589
+ *
590
+ * @example
591
+ * ```ts
592
+ * try {
593
+ * await provider.withdraw({ ... });
594
+ * } catch (error) {
595
+ * if (error instanceof WithdrawError && error.phase === "claiming") {
596
+ * const result = await provider.retryWithdrawClaim({
597
+ * sourceChain,
598
+ * destinationAddress,
599
+ * receipt: attestedReceipt,
600
+ * wallet,
601
+ * maxRetries: 5,
602
+ * });
603
+ * }
604
+ * }
605
+ * ```
606
+ */
607
+ async retryWithdrawClaim(
608
+ input: RetryWithdrawClaimRequest,
609
+ ): Promise<RetryWithdrawClaimResponse> {
610
+ const {
611
+ maxRetries = 5,
612
+ initialDelayMs = 2000,
613
+ backoffMultiplier = 2,
614
+ ...claimInput
615
+ } = input;
616
+
617
+ let lastError: Error | undefined;
618
+ let delay = initialDelayMs;
619
+
620
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
621
+ try {
622
+ const result = await this.claimWithdraw(claimInput);
623
+ return { ...result, retriesUsed: attempt };
624
+ } catch (error) {
625
+ lastError = error as Error;
626
+ logger.log(
627
+ `retryWithdrawClaim: attempt ${attempt + 1}/${maxRetries + 1} failed: ${lastError.message}`,
628
+ );
629
+ if (attempt < maxRetries) {
630
+ await sleep(delay);
631
+ delay *= backoffMultiplier;
632
+ }
633
+ }
634
+ }
635
+
636
+ throw new Error(
637
+ `Claim failed after ${maxRetries + 1} attempts: ${lastError?.message}`,
638
+ );
639
+ }
640
+
531
641
  getChainConfig(chain: Chain): ChainConfig {
532
642
  const chainConfig =
533
643
  this.crossChainCore.CHAINS[
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const CROSS_CHAIN_CORE_VERSION = "5.9.0";
1
+ export const CROSS_CHAIN_CORE_VERSION = "6.0.0";