@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
package/dist/index.mjs CHANGED
@@ -124,14 +124,43 @@ import {
124
124
  Aptos,
125
125
  AptosConfig
126
126
  } from "@aptos-labs/ts-sdk";
127
+
128
+ // src/providers/wormhole/types.ts
129
+ function validateExpireTimestamp(value) {
130
+ if (!Number.isInteger(value) || value < 0) {
131
+ throw new Error(
132
+ `getExpireTimestamp returned an invalid value (${value}). Expected a non-negative integer (epoch seconds).`
133
+ );
134
+ }
135
+ }
136
+ var TransferError = class extends Error {
137
+ constructor(message, originChainTxnId, cause) {
138
+ super(message);
139
+ this.name = "TransferError";
140
+ this.originChainTxnId = originChainTxnId;
141
+ this.cause = cause;
142
+ }
143
+ };
144
+ var WithdrawError = class extends Error {
145
+ constructor(message, originChainTxnId, phase, cause) {
146
+ super(message);
147
+ this.name = "WithdrawError";
148
+ this.originChainTxnId = originChainTxnId;
149
+ this.phase = phase;
150
+ this.cause = cause;
151
+ }
152
+ };
153
+
154
+ // src/providers/wormhole/signers/AptosLocalSigner.ts
127
155
  var AptosLocalSigner = class {
128
- constructor(chain, options, wallet, feePayerAccount, dappNetwork) {
156
+ constructor(chain, options, wallet, feePayerAccount, crossChainCore, onTransactionSigned) {
129
157
  this._claimedTransactionHashes = [];
130
158
  this._chain = chain;
131
159
  this._options = options;
132
160
  this._wallet = wallet;
133
161
  this._sponsorAccount = feePayerAccount;
134
- this._dappNetwork = dappNetwork;
162
+ this._crossChainCore = crossChainCore;
163
+ this._onTransactionSigned = onTransactionSigned;
135
164
  }
136
165
  chain() {
137
166
  return this._chain;
@@ -146,19 +175,21 @@ var AptosLocalSigner = class {
146
175
  const txHashes = [];
147
176
  this._claimedTransactionHashes = [];
148
177
  for (const tx of txs) {
178
+ this._onTransactionSigned?.(tx.description, null);
149
179
  const txId = await signAndSendTransaction(
150
180
  tx,
151
181
  this._wallet,
152
182
  this._sponsorAccount,
153
- this._dappNetwork
183
+ this._crossChainCore
154
184
  );
185
+ this._onTransactionSigned?.(tx.description, txId);
155
186
  txHashes.push(txId);
156
187
  this._claimedTransactionHashes.push(txId);
157
188
  }
158
189
  return txHashes;
159
190
  }
160
191
  };
161
- async function signAndSendTransaction(request, wallet, sponsorAccount, dappNetwork) {
192
+ async function signAndSendTransaction(request, wallet, sponsorAccount, crossChainCore) {
162
193
  if (!wallet) {
163
194
  throw new Error("Wallet is undefined");
164
195
  }
@@ -172,14 +203,20 @@ async function signAndSendTransaction(request, wallet, sponsorAccount, dappNetwo
172
203
  return a;
173
204
  }
174
205
  });
206
+ const dappNetwork = crossChainCore._dappConfig.aptosNetwork;
175
207
  const aptosConfig = new AptosConfig({
176
208
  network: dappNetwork
177
209
  });
178
210
  const aptos2 = new Aptos(aptosConfig);
211
+ const expireTimestamp = crossChainCore._dappConfig.getExpireTimestamp?.();
212
+ if (typeof expireTimestamp !== "undefined") {
213
+ validateExpireTimestamp(expireTimestamp);
214
+ }
179
215
  const txnToSign = await aptos2.transaction.build.simple({
180
216
  data: payload,
181
217
  sender: wallet.accountAddress.toString(),
182
- withFeePayer: sponsorAccount ? true : false
218
+ withFeePayer: sponsorAccount ? true : false,
219
+ ...typeof expireTimestamp !== "undefined" ? { options: { expireTimestamp } } : {}
183
220
  });
184
221
  const senderAuthenticator = await aptos2.transaction.sign({
185
222
  signer: wallet,
@@ -203,6 +240,101 @@ async function signAndSendTransaction(request, wallet, sponsorAccount, dappNetwo
203
240
  return tx.hash;
204
241
  }
205
242
 
243
+ // src/providers/wormhole/signers/AptosSigner.ts
244
+ import {
245
+ AccountAddress,
246
+ Aptos as Aptos2,
247
+ AptosConfig as AptosConfig2,
248
+ Deserializer
249
+ } from "@aptos-labs/ts-sdk";
250
+ import { UserResponseStatus } from "@aptos-labs/wallet-standard";
251
+ import { GasStationClient, GasStationTransactionSubmitter } from "@aptos-labs/gas-station-client";
252
+ async function signAndSendTransaction2(request, wallet, sponsorAccount, dappNetwork, crossChainCore) {
253
+ if (!wallet) {
254
+ throw new Error("wallet.sendTransaction is undefined");
255
+ }
256
+ const payload = request.transaction;
257
+ payload.functionArguments = payload.functionArguments.map((a) => {
258
+ if (a instanceof Uint8Array) {
259
+ return Array.from(a);
260
+ } else if (typeof a === "bigint") {
261
+ return a.toString();
262
+ } else {
263
+ return a;
264
+ }
265
+ });
266
+ let aptosConfig;
267
+ const useGasStation = sponsorAccount && !isAccount(sponsorAccount);
268
+ if (useGasStation) {
269
+ const gasStationClient = new GasStationClient({
270
+ network: dappNetwork,
271
+ apiKey: sponsorAccount[dappNetwork]
272
+ });
273
+ const transactionSubmitter = new GasStationTransactionSubmitter(gasStationClient);
274
+ aptosConfig = new AptosConfig2({
275
+ network: dappNetwork,
276
+ pluginSettings: {
277
+ TRANSACTION_SUBMITTER: transactionSubmitter
278
+ }
279
+ });
280
+ } else {
281
+ aptosConfig = new AptosConfig2({
282
+ network: dappNetwork
283
+ });
284
+ }
285
+ const aptos2 = new Aptos2(aptosConfig);
286
+ const functionArguments = extractFunctionArguments(
287
+ payload.functionArguments
288
+ );
289
+ const withdrawFunction = "0x5e2d961f06cd27aa07554a39d55f5ce1e58dff35d803c3529b1cd5c4fa3ab584::withdraw::deposit_for_burn";
290
+ const transactionData = {
291
+ function: withdrawFunction,
292
+ functionArguments
293
+ };
294
+ const expireTimestamp = crossChainCore?._dappConfig?.getExpireTimestamp?.();
295
+ if (typeof expireTimestamp !== "undefined") {
296
+ validateExpireTimestamp(expireTimestamp);
297
+ }
298
+ const txnToSign = await aptos2.transaction.build.simple({
299
+ data: transactionData,
300
+ sender: (await wallet.features["aptos:account"]?.account()).address.toString(),
301
+ withFeePayer: sponsorAccount ? true : false,
302
+ ...typeof expireTimestamp !== "undefined" ? { options: { expireTimestamp } } : {}
303
+ });
304
+ const response = await wallet.features["aptos:signTransaction"]?.signTransaction(txnToSign);
305
+ if (response?.status === UserResponseStatus.REJECTED) {
306
+ throw new Error("User has rejected the request");
307
+ }
308
+ const txnToSubmit = {
309
+ transaction: txnToSign,
310
+ senderAuthenticator: response.args
311
+ };
312
+ if (sponsorAccount && isAccount(sponsorAccount)) {
313
+ const feePayerSignerAuthenticator = aptos2.transaction.signAsFeePayer({
314
+ signer: sponsorAccount,
315
+ transaction: txnToSign
316
+ });
317
+ txnToSubmit.feePayerAuthenticator = feePayerSignerAuthenticator;
318
+ }
319
+ const txnSubmitted = await aptos2.transaction.submit.simple(txnToSubmit);
320
+ const tx = await aptos2.waitForTransaction({
321
+ transactionHash: txnSubmitted.hash
322
+ });
323
+ return tx.hash;
324
+ }
325
+ function extractFunctionArguments(functionArguments) {
326
+ const deserializer1 = new Deserializer(functionArguments[0].bcsToBytes());
327
+ const amount = deserializer1.deserializeU64();
328
+ const deserializer2 = new Deserializer(functionArguments[1].bcsToBytes());
329
+ const destination_domain = deserializer2.deserializeU32();
330
+ const mint_recipient = new AccountAddress(functionArguments[2].bcsToBytes());
331
+ const burn_token = new AccountAddress(functionArguments[3].bcsToBytes());
332
+ return [amount, destination_domain, mint_recipient, burn_token];
333
+ }
334
+ function isAccount(obj) {
335
+ return "accountAddress" in obj;
336
+ }
337
+
206
338
  // src/providers/wormhole/signers/SolanaSigner.ts
207
339
  import {
208
340
  Transaction as Transaction2
@@ -233,26 +365,40 @@ async function sendAndConfirmTransaction(serializedTx, blockhash, lastValidBlock
233
365
  );
234
366
  let confirmedTx = null;
235
367
  let txSendAttempts = 1;
236
- while (!confirmedTx) {
237
- confirmedTx = await Promise.race([
238
- confirmTransactionPromise,
239
- new Promise(
240
- (resolve) => setTimeout(() => resolve(null), retryIntervalMs)
241
- )
242
- ]);
243
- if (confirmedTx) break;
244
- if (verbose) {
245
- console.log(
246
- `Tx not confirmed after ${retryIntervalMs * txSendAttempts++}ms, resending`
247
- );
368
+ try {
369
+ while (!confirmedTx) {
370
+ confirmedTx = await Promise.race([
371
+ confirmTransactionPromise,
372
+ new Promise(
373
+ (resolve) => setTimeout(() => resolve(null), retryIntervalMs)
374
+ )
375
+ ]);
376
+ if (confirmedTx) break;
377
+ if (verbose) {
378
+ console.log(
379
+ `Tx not confirmed after ${retryIntervalMs * txSendAttempts++}ms, resending`
380
+ );
381
+ }
382
+ try {
383
+ await connection.sendRawTransaction(serializedTx, sendOptions);
384
+ } catch (e) {
385
+ if (verbose) {
386
+ console.error("Failed to resend transaction:", e);
387
+ }
388
+ }
248
389
  }
249
- try {
250
- await connection.sendRawTransaction(serializedTx, sendOptions);
251
- } catch (e) {
390
+ } catch (e) {
391
+ const message = e instanceof Error ? e.message.toLowerCase() : "";
392
+ if (message.includes("block height exceeded") || message.includes("blockheightexceeded")) {
252
393
  if (verbose) {
253
- console.error("Failed to resend transaction:", e);
394
+ console.warn(
395
+ "Block height exceeded but tx was already sent, returning signature:",
396
+ signature
397
+ );
254
398
  }
399
+ return signature;
255
400
  }
401
+ throw e;
256
402
  }
257
403
  if (confirmedTx.value.err) {
258
404
  const errorMessage = formatTransactionError(confirmedTx.value.err);
@@ -433,11 +579,11 @@ async function sleep(timeout) {
433
579
  }
434
580
 
435
581
  // src/providers/wormhole/signers/SolanaSigner.ts
436
- async function signAndSendTransaction2(request, wallet, options, crossChainCore) {
582
+ async function signAndSendTransaction3(request, wallet, options, crossChainCore) {
437
583
  if (!wallet || !(wallet instanceof SolanaDerivedWallet)) {
438
584
  throw new Error("Invalid wallet type or missing Solana wallet");
439
585
  }
440
- const commitment = options?.commitment ?? "finalized";
586
+ const commitment = options?.commitment ?? crossChainCore?._dappConfig?.solanaConfig?.commitment ?? "finalized";
441
587
  const connection = new Connection2(
442
588
  crossChainCore?._dappConfig?.solanaConfig?.rpc ?? crossChainCore?.CHAINS["Solana"]?.defaultRpc ?? "https://api.devnet.solana.com"
443
589
  // Last resort fallback
@@ -487,7 +633,7 @@ async function setPriorityFeeInstructions(connection, blockhash, lastValidBlockH
487
633
 
488
634
  // src/providers/wormhole/signers/EthereumSigner.ts
489
635
  import { ethers, getBigInt } from "ethers";
490
- async function signAndSendTransaction3(request, wallet, chainName, options) {
636
+ async function signAndSendTransaction4(request, wallet, chainName) {
491
637
  if (!wallet) {
492
638
  throw new Error("wallet.sendTransaction is undefined");
493
639
  }
@@ -495,8 +641,7 @@ async function signAndSendTransaction3(request, wallet, chainName, options) {
495
641
  method: "eth_chainId"
496
642
  });
497
643
  const actualChainId = parseInt(chainId, 16);
498
- if (!actualChainId)
499
- throw new Error("No signer found for chain" + chainName);
644
+ if (!actualChainId) throw new Error("No signer found for chain" + chainName);
500
645
  const expectedChainId = request.transaction.chainId ? getBigInt(request.transaction.chainId) : void 0;
501
646
  if (!actualChainId || !expectedChainId || BigInt(actualChainId) !== expectedChainId) {
502
647
  throw new Error(
@@ -507,99 +652,44 @@ async function signAndSendTransaction3(request, wallet, chainName, options) {
507
652
  wallet.eip1193Provider
508
653
  );
509
654
  const signer = await provider.getSigner();
510
- const response = await signer.sendTransaction(request.transaction);
511
- const receipt = await response.wait();
512
- return receipt?.hash || "";
513
- }
514
-
515
- // src/providers/wormhole/signers/AptosSigner.ts
516
- import {
517
- AccountAddress,
518
- Aptos as Aptos2,
519
- AptosConfig as AptosConfig2,
520
- Deserializer
521
- } from "@aptos-labs/ts-sdk";
522
- import { UserResponseStatus } from "@aptos-labs/wallet-standard";
523
- import { GasStationClient, GasStationTransactionSubmitter } from "@aptos-labs/gas-station-client";
524
- async function signAndSendTransaction4(request, wallet, sponsorAccount, dappNetwork) {
525
- if (!wallet) {
526
- throw new Error("wallet.sendTransaction is undefined");
527
- }
528
- const payload = request.transaction;
529
- payload.functionArguments = payload.functionArguments.map((a) => {
530
- if (a instanceof Uint8Array) {
531
- return Array.from(a);
532
- } else if (typeof a === "bigint") {
533
- return a.toString();
534
- } else {
535
- return a;
655
+ let response;
656
+ try {
657
+ response = await signer.sendTransaction(request.transaction);
658
+ } catch (e) {
659
+ const message = e instanceof Error ? e.message : String(e);
660
+ const hashMatch = message.match(/"hash":\s*"(0x[a-fA-F0-9]{64})"/);
661
+ if (hashMatch) {
662
+ console.warn("Extracted EVM tx hash from error:", hashMatch[1]);
663
+ return hashMatch[1];
536
664
  }
537
- });
538
- let aptosConfig;
539
- const useGasStation = sponsorAccount && !isAccount(sponsorAccount);
540
- if (useGasStation) {
541
- const gasStationClient = new GasStationClient({
542
- network: dappNetwork,
543
- apiKey: sponsorAccount[dappNetwork]
544
- });
545
- const transactionSubmitter = new GasStationTransactionSubmitter(gasStationClient);
546
- aptosConfig = new AptosConfig2({
547
- network: dappNetwork,
548
- pluginSettings: {
549
- TRANSACTION_SUBMITTER: transactionSubmitter
550
- }
551
- });
552
- } else {
553
- aptosConfig = new AptosConfig2({
554
- network: dappNetwork
555
- });
665
+ throw e;
556
666
  }
557
- const aptos2 = new Aptos2(aptosConfig);
558
- const functionArguments = extractFunctionArguments(
559
- payload.functionArguments
560
- );
561
- const withdrawFunction = "0x5e2d961f06cd27aa07554a39d55f5ce1e58dff35d803c3529b1cd5c4fa3ab584::withdraw::deposit_for_burn";
562
- const transactionData = {
563
- function: withdrawFunction,
564
- functionArguments
565
- };
566
- const txnToSign = await aptos2.transaction.build.simple({
567
- data: transactionData,
568
- sender: (await wallet.features["aptos:account"]?.account()).address.toString(),
569
- withFeePayer: sponsorAccount ? true : false
570
- });
571
- const response = await wallet.features["aptos:signTransaction"]?.signTransaction(txnToSign);
572
- if (response?.status === UserResponseStatus.REJECTED) {
573
- throw new Error("User has rejected the request");
574
- }
575
- const txnToSubmit = {
576
- transaction: txnToSign,
577
- senderAuthenticator: response.args
578
- };
579
- if (sponsorAccount && isAccount(sponsorAccount)) {
580
- const feePayerSignerAuthenticator = aptos2.transaction.signAsFeePayer({
581
- signer: sponsorAccount,
582
- transaction: txnToSign
583
- });
584
- txnToSubmit.feePayerAuthenticator = feePayerSignerAuthenticator;
667
+ try {
668
+ const receipt = await response.wait();
669
+ return receipt?.hash || response.hash || "";
670
+ } catch (e) {
671
+ if (e?.code === "TRANSACTION_REPLACED") {
672
+ if (e.reason === "repriced") {
673
+ const replacementHash = e.receipt?.hash || e.replacement?.hash;
674
+ if (replacementHash) {
675
+ console.warn(
676
+ "EVM transaction was repriced. Using replacement hash:",
677
+ replacementHash
678
+ );
679
+ return replacementHash;
680
+ }
681
+ }
682
+ throw e;
683
+ }
684
+ if (response.hash) {
685
+ console.warn(
686
+ "EVM transaction wait failed but tx was submitted:",
687
+ response.hash
688
+ );
689
+ return response.hash;
690
+ }
691
+ throw e;
585
692
  }
586
- const txnSubmitted = await aptos2.transaction.submit.simple(txnToSubmit);
587
- const tx = await aptos2.waitForTransaction({
588
- transactionHash: txnSubmitted.hash
589
- });
590
- return tx.hash;
591
- }
592
- function extractFunctionArguments(functionArguments) {
593
- const deserializer1 = new Deserializer(functionArguments[0].bcsToBytes());
594
- const amount = deserializer1.deserializeU64();
595
- const deserializer2 = new Deserializer(functionArguments[1].bcsToBytes());
596
- const destination_domain = deserializer2.deserializeU32();
597
- const mint_recipient = new AccountAddress(functionArguments[2].bcsToBytes());
598
- const burn_token = new AccountAddress(functionArguments[3].bcsToBytes());
599
- return [amount, destination_domain, mint_recipient, burn_token];
600
- }
601
- function isAccount(obj) {
602
- return "accountAddress" in obj;
603
693
  }
604
694
 
605
695
  // src/providers/wormhole/signers/SuiSigner.ts
@@ -622,7 +712,7 @@ async function signAndSendTransaction5(request, wallet) {
622
712
 
623
713
  // src/providers/wormhole/signers/Signer.ts
624
714
  var Signer = class {
625
- constructor(chain, address, options, wallet, crossChainCore, sponsorAccount) {
715
+ constructor(chain, address, options, wallet, crossChainCore, sponsorAccount, onTransactionSigned, trackAsSourceChain = true) {
626
716
  this._claimedTransactionHashes = [];
627
717
  this._chain = chain;
628
718
  this._address = address;
@@ -630,6 +720,8 @@ var Signer = class {
630
720
  this._wallet = wallet;
631
721
  this._crossChainCore = crossChainCore;
632
722
  this._sponsorAccount = sponsorAccount;
723
+ this._onTransactionSigned = onTransactionSigned;
724
+ this._trackAsSourceChain = trackAsSourceChain;
633
725
  }
634
726
  chain() {
635
727
  return this._chain.key;
@@ -644,6 +736,7 @@ var Signer = class {
644
736
  const txHashes = [];
645
737
  this._claimedTransactionHashes = [];
646
738
  for (const tx of txs) {
739
+ this._onTransactionSigned?.(tx.description, null);
647
740
  const txId = await signAndSendTransaction6(
648
741
  this._chain,
649
742
  tx,
@@ -652,6 +745,10 @@ var Signer = class {
652
745
  this._crossChainCore,
653
746
  this._sponsorAccount
654
747
  );
748
+ if (this._trackAsSourceChain) {
749
+ this._crossChainCore._lastSourceChainTxId = txId;
750
+ }
751
+ this._onTransactionSigned?.(tx.description, txId);
655
752
  txHashes.push(txId);
656
753
  this._claimedTransactionHashes.push(txId);
657
754
  }
@@ -664,7 +761,7 @@ var signAndSendTransaction6 = async (chain, request, wallet, options = {}, cross
664
761
  }
665
762
  const dappNetwork = crossChainCore._dappConfig.aptosNetwork;
666
763
  if (chain.context === "Solana") {
667
- const signature = await signAndSendTransaction2(
764
+ const signature = await signAndSendTransaction3(
668
765
  request,
669
766
  wallet,
670
767
  options,
@@ -672,11 +769,10 @@ var signAndSendTransaction6 = async (chain, request, wallet, options = {}, cross
672
769
  );
673
770
  return signature;
674
771
  } else if (chain.context === "Ethereum") {
675
- const tx = await signAndSendTransaction3(
772
+ const tx = await signAndSendTransaction4(
676
773
  request,
677
774
  wallet,
678
- chain.displayName,
679
- options
775
+ chain.displayName
680
776
  );
681
777
  return tx;
682
778
  } else if (chain.context === "Sui") {
@@ -686,11 +782,12 @@ var signAndSendTransaction6 = async (chain, request, wallet, options = {}, cross
686
782
  );
687
783
  return tx;
688
784
  } else if (chain.context === "Aptos") {
689
- const tx = await signAndSendTransaction4(
785
+ const tx = await signAndSendTransaction2(
690
786
  request,
691
787
  wallet,
692
788
  sponsorAccount,
693
- dappNetwork
789
+ dappNetwork,
790
+ crossChainCore
694
791
  );
695
792
  return tx;
696
793
  } else {
@@ -730,17 +827,6 @@ async function createCCTPRoute(wh, sourceChain, destChain, tokens) {
730
827
  return { route: cctpRoute, request };
731
828
  }
732
829
 
733
- // src/providers/wormhole/types.ts
734
- var WithdrawError = class extends Error {
735
- constructor(message, originChainTxnId, phase, cause) {
736
- super(message);
737
- this.name = "WithdrawError";
738
- this.originChainTxnId = originChainTxnId;
739
- this.phase = phase;
740
- this.cause = cause;
741
- }
742
- };
743
-
744
830
  // src/providers/wormhole/wormhole.ts
745
831
  var WormholeProvider = class {
746
832
  constructor(core) {
@@ -759,12 +845,22 @@ var WormholeProvider = class {
759
845
  }
760
846
  const isMainnet = dappNetwork === Network.MAINNET;
761
847
  const platforms = [aptos, solana, evm, sui];
762
- const solanaRpc = this.crossChainCore._dappConfig?.solanaConfig?.rpc ?? this.crossChainCore.CHAINS["Solana"]?.defaultRpc;
848
+ const dappConfig = this.crossChainCore._dappConfig;
849
+ const chains = this.crossChainCore.CHAINS;
850
+ const solanaRpc = dappConfig?.solanaConfig?.rpc ?? chains["Solana"]?.defaultRpc;
851
+ const suiRpc = dappConfig?.suiConfig?.rpc ?? chains["Sui"]?.defaultRpc;
852
+ const evmChainsConfig = {};
853
+ for (const name of EVM_CHAIN_NAMES) {
854
+ const rpc = dappConfig?.evmConfig?.[name]?.rpc ?? chains[name]?.defaultRpc;
855
+ if (rpc) {
856
+ evmChainsConfig[name] = { rpc };
857
+ }
858
+ }
763
859
  const wh = await wormhole(isMainnet ? "Mainnet" : "Testnet", platforms, {
764
860
  chains: {
765
- Solana: {
766
- rpc: solanaRpc
767
- }
861
+ ...solanaRpc ? { Solana: { rpc: solanaRpc } } : {},
862
+ ...suiRpc ? { Sui: { rpc: suiRpc } } : {},
863
+ ...evmChainsConfig
768
864
  }
769
865
  });
770
866
  this._wormholeContext = wh;
@@ -815,7 +911,7 @@ var WormholeProvider = class {
815
911
  return quote;
816
912
  }
817
913
  async submitCCTPTransfer(input) {
818
- const { sourceChain, wallet, destinationAddress } = input;
914
+ const { sourceChain, wallet, destinationAddress, onTransactionSigned } = input;
819
915
  if (!this._wormholeContext) {
820
916
  await this.setWormholeContext(sourceChain);
821
917
  }
@@ -841,7 +937,9 @@ var WormholeProvider = class {
841
937
  signerAddress,
842
938
  {},
843
939
  wallet,
844
- this.crossChainCore
940
+ this.crossChainCore,
941
+ void 0,
942
+ onTransactionSigned
845
943
  );
846
944
  logger.log("signer", signer);
847
945
  logger.log("wormholeRequest", this.wormholeRequest);
@@ -856,10 +954,15 @@ var WormholeProvider = class {
856
954
  return { originChainTxnId: originChainTxnId || "", receipt };
857
955
  }
858
956
  async claimCCTPTransfer(input) {
859
- let { receipt, mainSigner, sponsorAccount } = input;
957
+ let { receipt, mainSigner, sponsorAccount, onTransactionSigned } = input;
860
958
  if (!this.wormholeRoute) {
861
959
  throw new Error("Wormhole route not initialized");
862
960
  }
961
+ if (sponsorAccount && !isAccount(sponsorAccount)) {
962
+ throw new Error(
963
+ "AptosLocalSigner does not support GasStationApiKey as a sponsor account. Wormhole claim transactions are script-based and cannot be submitted via the gas station. Please provide an Account instance as the sponsor, or omit the sponsor account."
964
+ );
965
+ }
863
966
  logger.log("mainSigner", mainSigner.accountAddress.toString());
864
967
  let retries = 0;
865
968
  const maxRetries = 5;
@@ -877,7 +980,8 @@ var WormholeProvider = class {
877
980
  // the account that signs the "claim" transaction
878
981
  sponsorAccount,
879
982
  // the fee payer account
880
- this.crossChainCore._dappConfig?.aptosNetwork
983
+ this.crossChainCore,
984
+ onTransactionSigned
881
985
  );
882
986
  if (routes3.isManual(this.wormholeRoute)) {
883
987
  const circleAttestationReceipt = await this.wormholeRoute.complete(signer, receipt);
@@ -922,12 +1026,21 @@ var WormholeProvider = class {
922
1026
  });
923
1027
  }
924
1028
  let { originChainTxnId, receipt } = await this.submitCCTPTransfer(input);
925
- const { destinationChainTxnId } = await this.claimCCTPTransfer({
926
- receipt,
927
- mainSigner: input.mainSigner,
928
- sponsorAccount: input.sponsorAccount
929
- });
930
- return { originChainTxnId, destinationChainTxnId };
1029
+ try {
1030
+ const { destinationChainTxnId } = await this.claimCCTPTransfer({
1031
+ receipt,
1032
+ mainSigner: input.mainSigner,
1033
+ sponsorAccount: input.sponsorAccount,
1034
+ onTransactionSigned: input.onTransactionSigned
1035
+ });
1036
+ return { originChainTxnId, destinationChainTxnId };
1037
+ } catch (error) {
1038
+ throw new TransferError(
1039
+ error?.message ?? "Transfer claim failed after source-chain burn",
1040
+ originChainTxnId,
1041
+ error
1042
+ );
1043
+ }
931
1044
  }
932
1045
  // --- Split withdraw flow: initiateWithdraw + trackWithdraw + claimWithdraw ---
933
1046
  /**
@@ -936,7 +1049,7 @@ var WormholeProvider = class {
936
1049
  * Returns a receipt that can be tracked and later claimed.
937
1050
  */
938
1051
  async initiateWithdraw(input) {
939
- const { wallet, destinationAddress, sponsorAccount } = input;
1052
+ const { wallet, destinationAddress, sponsorAccount, onTransactionSigned } = input;
940
1053
  if (!this._wormholeContext) {
941
1054
  throw new Error("Wormhole context not initialized");
942
1055
  }
@@ -949,7 +1062,8 @@ var WormholeProvider = class {
949
1062
  {},
950
1063
  wallet,
951
1064
  this.crossChainCore,
952
- sponsorAccount
1065
+ sponsorAccount,
1066
+ onTransactionSigned
953
1067
  );
954
1068
  const wormholeDestAddress = Wormhole2.chainAddress(
955
1069
  this.destinationChain,
@@ -1007,9 +1121,9 @@ var WormholeProvider = class {
1007
1121
  * Otherwise falls back to the wallet-based Signer (triggers wallet popup).
1008
1122
  */
1009
1123
  async claimWithdraw(input) {
1010
- const { sourceChain, destinationAddress, receipt } = input;
1124
+ const { claimChain, destinationAddress, receipt } = input;
1011
1125
  const serverClaimUrl = this.crossChainCore._dappConfig?.solanaConfig?.serverClaimUrl;
1012
- if (sourceChain === "Solana" && serverClaimUrl) {
1126
+ if (claimChain === "Solana" && serverClaimUrl) {
1013
1127
  logger.log("claimWithdraw: using server-side claim via", serverClaimUrl);
1014
1128
  const response = await fetch(serverClaimUrl, {
1015
1129
  method: "POST",
@@ -1017,7 +1131,7 @@ var WormholeProvider = class {
1017
1131
  body: JSON.stringify({
1018
1132
  receipt: serializeReceipt(receipt),
1019
1133
  destinationAddress,
1020
- sourceChain
1134
+ claimChain
1021
1135
  })
1022
1136
  });
1023
1137
  if (!response.ok) {
@@ -1038,11 +1152,14 @@ var WormholeProvider = class {
1038
1152
  );
1039
1153
  }
1040
1154
  const claimSigner = new Signer(
1041
- this.getChainConfig(sourceChain),
1155
+ this.getChainConfig(claimChain),
1042
1156
  destinationAddress,
1043
1157
  {},
1044
1158
  input.wallet,
1045
- this.crossChainCore
1159
+ this.crossChainCore,
1160
+ void 0,
1161
+ input.onTransactionSigned,
1162
+ false
1046
1163
  );
1047
1164
  if (routes3.isManual(this.wormholeRoute)) {
1048
1165
  const circleAttestationReceipt = await this.wormholeRoute.complete(
@@ -1068,12 +1185,20 @@ var WormholeProvider = class {
1068
1185
  * as the flow progresses.
1069
1186
  */
1070
1187
  async withdraw(input) {
1071
- const { sourceChain, wallet, destinationAddress, sponsorAccount, onPhaseChange } = input;
1188
+ const {
1189
+ sourceChain,
1190
+ wallet,
1191
+ destinationAddress,
1192
+ sponsorAccount,
1193
+ onPhaseChange,
1194
+ onTransactionSigned
1195
+ } = input;
1072
1196
  onPhaseChange?.("initiating");
1073
1197
  const { originChainTxnId, receipt } = await this.initiateWithdraw({
1074
1198
  wallet,
1075
1199
  destinationAddress,
1076
- sponsorAccount
1200
+ sponsorAccount,
1201
+ onTransactionSigned
1077
1202
  });
1078
1203
  let currentPhase = "tracking";
1079
1204
  try {
@@ -1082,10 +1207,11 @@ var WormholeProvider = class {
1082
1207
  currentPhase = "claiming";
1083
1208
  onPhaseChange?.("claiming");
1084
1209
  const { destinationChainTxnId } = await this.claimWithdraw({
1085
- sourceChain,
1210
+ claimChain: sourceChain,
1086
1211
  destinationAddress: destinationAddress.toString(),
1087
1212
  receipt: attestedReceipt,
1088
- wallet
1213
+ wallet,
1214
+ onTransactionSigned
1089
1215
  });
1090
1216
  return { originChainTxnId, destinationChainTxnId };
1091
1217
  } catch (error) {
@@ -1097,6 +1223,59 @@ var WormholeProvider = class {
1097
1223
  );
1098
1224
  }
1099
1225
  }
1226
+ /**
1227
+ * Retries a failed `claimWithdraw` call with configurable exponential backoff.
1228
+ *
1229
+ * Use this when the claim phase of a withdrawal fails (e.g., RPC instability,
1230
+ * network congestion) but the Aptos burn transaction has already been submitted.
1231
+ * The attested receipt can be recovered from the `WithdrawError` thrown by
1232
+ * `withdraw()` and passed directly to this method.
1233
+ *
1234
+ * @example
1235
+ * ```ts
1236
+ * try {
1237
+ * await provider.withdraw({ ... });
1238
+ * } catch (error) {
1239
+ * if (error instanceof WithdrawError && error.phase === "claiming") {
1240
+ * const result = await provider.retryWithdrawClaim({
1241
+ * sourceChain,
1242
+ * destinationAddress,
1243
+ * receipt: attestedReceipt,
1244
+ * wallet,
1245
+ * maxRetries: 5,
1246
+ * });
1247
+ * }
1248
+ * }
1249
+ * ```
1250
+ */
1251
+ async retryWithdrawClaim(input) {
1252
+ const {
1253
+ maxRetries = 5,
1254
+ initialDelayMs = 2e3,
1255
+ backoffMultiplier = 2,
1256
+ ...claimInput
1257
+ } = input;
1258
+ let lastError;
1259
+ let delay = initialDelayMs;
1260
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1261
+ try {
1262
+ const result = await this.claimWithdraw(claimInput);
1263
+ return { ...result, retriesUsed: attempt };
1264
+ } catch (error) {
1265
+ lastError = error;
1266
+ logger.log(
1267
+ `retryWithdrawClaim: attempt ${attempt + 1}/${maxRetries + 1} failed: ${lastError.message}`
1268
+ );
1269
+ if (attempt < maxRetries) {
1270
+ await sleep2(delay);
1271
+ delay *= backoffMultiplier;
1272
+ }
1273
+ }
1274
+ }
1275
+ throw new Error(
1276
+ `Claim failed after ${maxRetries + 1} attempts: ${lastError?.message}`
1277
+ );
1278
+ }
1100
1279
  getChainConfig(chain) {
1101
1280
  const chainConfig = this.crossChainCore.CHAINS[chain];
1102
1281
  if (!chainConfig) {
@@ -1119,6 +1298,7 @@ var SolanaLocalSigner = class {
1119
1298
  this.retryIntervalMs = config.retryIntervalMs ?? 5e3;
1120
1299
  this.priorityFeeConfig = config.priorityFeeConfig;
1121
1300
  this.verbose = config.verbose ?? false;
1301
+ this._onTransactionSigned = config.onTransactionSigned;
1122
1302
  }
1123
1303
  chain() {
1124
1304
  return "Solana";
@@ -1137,7 +1317,9 @@ var SolanaLocalSigner = class {
1137
1317
  const txHashes = [];
1138
1318
  this._claimedTransactionHashes = [];
1139
1319
  for (const tx of txs) {
1320
+ this._onTransactionSigned?.(tx.description, null);
1140
1321
  const txId = await this.signAndSendTransaction(tx);
1322
+ this._onTransactionSigned?.(tx.description, txId);
1141
1323
  txHashes.push(txId);
1142
1324
  this._claimedTransactionHashes.push(txId);
1143
1325
  }
@@ -1628,6 +1810,18 @@ var getSuiWalletUSDCBalance = async (walletAddress, aptosNetwork, rpc) => {
1628
1810
 
1629
1811
  // src/CrossChainCore.ts
1630
1812
  import { NetworkToChainId, NetworkToNodeAPI } from "@aptos-labs/ts-sdk";
1813
+ var _evmChainRecord = {
1814
+ Ethereum: true,
1815
+ Sepolia: true,
1816
+ BaseSepolia: true,
1817
+ ArbitrumSepolia: true,
1818
+ Avalanche: true,
1819
+ Base: true,
1820
+ Arbitrum: true,
1821
+ PolygonSepolia: true,
1822
+ Polygon: true
1823
+ };
1824
+ var EVM_CHAIN_NAMES = Object.keys(_evmChainRecord);
1631
1825
  var EthereumChainIdToTestnetChain = {
1632
1826
  11155111: testnetChains.Sepolia,
1633
1827
  84532: testnetChains.BaseSepolia,
@@ -1642,7 +1836,7 @@ var EthereumChainIdToMainnetChain = {
1642
1836
  43114: mainnetChains.Avalanche,
1643
1837
  137: mainnetChains.Polygon
1644
1838
  };
1645
- var CrossChainCore = class {
1839
+ var CrossChainCore2 = class {
1646
1840
  constructor(args) {
1647
1841
  this._dappConfig = {
1648
1842
  aptosNetwork: Network3.TESTNET
@@ -1696,14 +1890,13 @@ var CrossChainCore = class {
1696
1890
  walletAddress,
1697
1891
  this._dappConfig.aptosNetwork,
1698
1892
  sourceChain,
1699
- // TODO: maybe let the user config it
1700
- this.CHAINS[sourceChain].defaultRpc
1893
+ this._dappConfig?.evmConfig?.[sourceChain]?.rpc ?? this.CHAINS[sourceChain].defaultRpc
1701
1894
  );
1702
1895
  case "Sui":
1703
1896
  return await getSuiWalletUSDCBalance(
1704
1897
  walletAddress,
1705
1898
  this._dappConfig.aptosNetwork,
1706
- this.CHAINS[sourceChain].defaultRpc
1899
+ this._dappConfig?.suiConfig?.rpc ?? this.CHAINS[sourceChain].defaultRpc
1707
1900
  );
1708
1901
  default:
1709
1902
  throw new Error(`Unsupported chain: ${sourceChain}`);
@@ -1716,13 +1909,15 @@ import { Network as Network4 } from "@aptos-labs/ts-sdk";
1716
1909
  export {
1717
1910
  AptosLocalSigner,
1718
1911
  Context,
1719
- CrossChainCore,
1912
+ CrossChainCore2 as CrossChainCore,
1913
+ EVM_CHAIN_NAMES,
1720
1914
  EthereumChainIdToMainnetChain,
1721
1915
  EthereumChainIdToTestnetChain,
1722
1916
  Network4 as Network,
1723
1917
  NetworkToChainId,
1724
1918
  NetworkToNodeAPI,
1725
1919
  SolanaLocalSigner,
1920
+ TransferError,
1726
1921
  WithdrawError,
1727
1922
  WormholeProvider,
1728
1923
  createCCTPRoute,
@@ -1732,6 +1927,7 @@ export {
1732
1927
  serializeReceipt,
1733
1928
  signAndSendTransaction,
1734
1929
  testnetChains,
1735
- testnetTokens
1930
+ testnetTokens,
1931
+ validateExpireTimestamp
1736
1932
  };
1737
1933
  //# sourceMappingURL=index.mjs.map