@txnlab/haystack-router 2.0.4 → 3.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.
package/dist/index.mjs CHANGED
@@ -1,18 +1,17 @@
1
- import { t as Logger } from "./logger-DUVOA2Oo.mjs";
2
- import { Algodv2, AtomicTransactionComposer, LogicSigAccount, Transaction, decodeUnsignedTransaction, isValidAddress, makeApplicationOptInTxnFromObject, makeAssetTransferTxnWithSuggestedParamsFromObject, msgpackRawDecode, signLogicSigTransactionObject, signTransaction } from "algosdk";
3
-
1
+ import { t as Logger } from "./logger-BelK6H0R.mjs";
2
+ import { Algodv2, AtomicTransactionComposer, LogicSigAccount, TransactionType, decodeUint64, decodeUnsignedTransaction, getApplicationAddress, isValidAddress, makeApplicationOptInTxnFromObject, makeAssetTransferTxnWithSuggestedParamsFromObject, msgpackRawDecode, signLogicSigTransactionObject, signTransaction } from "algosdk";
4
3
  //#region src/constants.ts
5
4
  /**
6
5
  * Supported DEX protocols for swap routing
7
6
  */
8
- let Protocol = /* @__PURE__ */ function(Protocol$1) {
9
- Protocol$1["TinymanV2"] = "TinymanV2";
10
- Protocol$1["Algofi"] = "Algofi";
11
- Protocol$1["Algomint"] = "Algomint";
12
- Protocol$1["Pact"] = "Pact";
13
- Protocol$1["Folks"] = "Folks";
14
- Protocol$1["TAlgo"] = "TAlgo";
15
- return Protocol$1;
7
+ let Protocol = /* @__PURE__ */ function(Protocol) {
8
+ Protocol["TinymanV2"] = "TinymanV2";
9
+ Protocol["Algofi"] = "Algofi";
10
+ Protocol["Algomint"] = "Algomint";
11
+ Protocol["Pact"] = "Pact";
12
+ Protocol["Folks"] = "Folks";
13
+ Protocol["TAlgo"] = "TAlgo";
14
+ return Protocol;
16
15
  }({});
17
16
  /**
18
17
  * Deprecated protocols that are automatically excluded from routing
@@ -42,24 +41,65 @@ const DEFAULT_AUTO_OPT_IN = false;
42
41
  const DEFAULT_DEBUG_LEVEL = "none";
43
42
  /** Default number of rounds to wait for transaction confirmation */
44
43
  const DEFAULT_CONFIRMATION_ROUNDS = 10;
45
-
44
+ //#endregion
45
+ //#region src/launch.ts
46
+ /**
47
+ * Normalize a quote's raw launch object, coercing amounts to bigint and
48
+ * defaulting the fields the API omits when they don't apply
49
+ */
50
+ function toSwapLaunchQuote(launch) {
51
+ return {
52
+ side: launch.side,
53
+ tokenNum: launch.tokenNum,
54
+ bondingTokenId: launch.bondingTokenId,
55
+ bondingSurplus: BigInt(launch.bondingSurplus),
56
+ refund: BigInt(launch.refund ?? 0),
57
+ graduates: launch.graduates ?? false
58
+ };
59
+ }
46
60
  //#endregion
47
61
  //#region src/composer.ts
62
+ /** ABI selectors of the HayLaunch bonding-curve buy methods (buyWithLimit, buyWithAlgoWithLimit) */
63
+ const LAUNCH_BUY_SELECTORS = /* @__PURE__ */ new Set(["9068a8d9", "b0e30517"]);
64
+ /** ABI selector of the HayLaunch bonding-curve sell method (sellWithLimit) */
65
+ const LAUNCH_SELL_SELECTOR = "028a0647";
66
+ /** Prefix of the log that carries an ARC-4 method's return value */
67
+ const ARC4_RETURN_PREFIX = "151f7c75";
68
+ /**
69
+ * Returns which side of a HayLaunch bonding-curve trade an app call is, or
70
+ * undefined if it isn't a curve buy or sell call
71
+ */
72
+ function launchCurveSide(txn) {
73
+ const selector = txn.applicationCall?.appArgs[0];
74
+ if (txn.type !== TransactionType.appl || !selector) return void 0;
75
+ const hex = Buffer.from(selector).toString("hex");
76
+ if (LAUNCH_BUY_SELECTORS.has(hex)) return "buy";
77
+ if (hex === LAUNCH_SELL_SELECTOR) return "sell";
78
+ }
79
+ /**
80
+ * Reads a uint64 ARC-4 return value from an app call's logs (the last log,
81
+ * prefixed with 151f7c75)
82
+ */
83
+ function readUint64Return(logs) {
84
+ const log = logs?.[logs.length - 1];
85
+ if (!log || log.length !== 12 || Buffer.from(log.subarray(0, 4)).toString("hex") !== ARC4_RETURN_PREFIX) return;
86
+ return decodeUint64(log.subarray(4), "bigint");
87
+ }
48
88
  /**
49
89
  * Status of the SwapComposer transaction group lifecycle
50
90
  */
51
- let SwapComposerStatus = /* @__PURE__ */ function(SwapComposerStatus$1) {
91
+ let SwapComposerStatus = /* @__PURE__ */ function(SwapComposerStatus) {
52
92
  /** The atomic group is still under construction. */
53
- SwapComposerStatus$1[SwapComposerStatus$1["BUILDING"] = 0] = "BUILDING";
93
+ SwapComposerStatus[SwapComposerStatus["BUILDING"] = 0] = "BUILDING";
54
94
  /** The atomic group has been finalized, but not yet signed. */
55
- SwapComposerStatus$1[SwapComposerStatus$1["BUILT"] = 1] = "BUILT";
95
+ SwapComposerStatus[SwapComposerStatus["BUILT"] = 1] = "BUILT";
56
96
  /** The atomic group has been finalized and signed, but not yet submitted to the network. */
57
- SwapComposerStatus$1[SwapComposerStatus$1["SIGNED"] = 2] = "SIGNED";
97
+ SwapComposerStatus[SwapComposerStatus["SIGNED"] = 2] = "SIGNED";
58
98
  /** The atomic group has been finalized, signed, and submitted to the network. */
59
- SwapComposerStatus$1[SwapComposerStatus$1["SUBMITTED"] = 3] = "SUBMITTED";
99
+ SwapComposerStatus[SwapComposerStatus["SUBMITTED"] = 3] = "SUBMITTED";
60
100
  /** The atomic group has been finalized, signed, submitted, and successfully committed to a block. */
61
- SwapComposerStatus$1[SwapComposerStatus$1["COMMITTED"] = 4] = "COMMITTED";
62
- return SwapComposerStatus$1;
101
+ SwapComposerStatus[SwapComposerStatus["COMMITTED"] = 4] = "COMMITTED";
102
+ return SwapComposerStatus;
63
103
  }({});
64
104
  /**
65
105
  * Composer for building and executing atomic swap transaction groups
@@ -96,6 +136,7 @@ var SwapComposer = class SwapComposer {
96
136
  note;
97
137
  inputTransactionIndex;
98
138
  outputTransactionIndex;
139
+ curveTransactionIndex;
99
140
  /** Summary data built incrementally during swap composition */
100
141
  summaryData;
101
142
  /**
@@ -207,15 +248,16 @@ var SwapComposer = class SwapComposer {
207
248
  */
208
249
  async addSwapTransactions() {
209
250
  if (this.swapTransactionsAdded) throw new Error("Swap transactions have already been added");
210
- if (this.getStatus() !== SwapComposerStatus.BUILDING) throw new Error("Cannot add swap transactions when composer status is not BUILDING");
251
+ if (this.getStatus() !== 0) throw new Error("Cannot add swap transactions when composer status is not BUILDING");
211
252
  const beforeTxns = await this.executeMiddlewareHooks("beforeSwap");
212
253
  if (this.atc.count() + beforeTxns.length > SwapComposer.MAX_GROUP_SIZE) throw new Error(`Adding beforeSwap transactions exceeds the maximum atomic group size of ${SwapComposer.MAX_GROUP_SIZE}`);
213
254
  for (const txnWithSigner of beforeTxns) this.atc.addTransaction(txnWithSigner);
214
- const { txns: processedTxns, inputTxnRelativeIndex, outputTxnRelativeIndex } = await this.processSwapTransactions();
255
+ const { txns: processedTxns, inputTxnRelativeIndex, outputTxnRelativeIndex, curveTxnRelativeIndex } = await this.processSwapTransactions();
215
256
  const afterTxns = await this.executeMiddlewareHooks("afterSwap");
216
257
  if (this.atc.count() + processedTxns.length + afterTxns.length > SwapComposer.MAX_GROUP_SIZE) throw new Error(`Adding swap transactions exceeds the maximum atomic group size of ${SwapComposer.MAX_GROUP_SIZE}`);
217
258
  if (inputTxnRelativeIndex !== void 0) this.inputTransactionIndex = this.atc.count() + inputTxnRelativeIndex;
218
259
  if (outputTxnRelativeIndex !== void 0) this.outputTransactionIndex = this.atc.count() + outputTxnRelativeIndex;
260
+ if (curveTxnRelativeIndex !== void 0) this.curveTransactionIndex = this.atc.count() + curveTxnRelativeIndex;
219
261
  for (const txnWithSigner of processedTxns) this.atc.addTransaction(txnWithSigner);
220
262
  for (const txnWithSigner of afterTxns) this.atc.addTransaction(txnWithSigner);
221
263
  this.swapTransactionsAdded = true;
@@ -261,7 +303,7 @@ var SwapComposer = class SwapComposer {
261
303
  * ```
262
304
  */
263
305
  async sign() {
264
- if (this.getStatus() >= SwapComposerStatus.SIGNED) return this.atc.gatherSignatures();
306
+ if (this.getStatus() >= 2) return this.atc.gatherSignatures();
265
307
  if (!this.swapTransactionsAdded) await this.addSwapTransactions();
266
308
  return await this.atc.gatherSignatures();
267
309
  }
@@ -302,13 +344,14 @@ var SwapComposer = class SwapComposer {
302
344
  * console.log('Transaction IDs:', result.txIds)
303
345
  * ```
304
346
  */
305
- async execute(waitRounds = DEFAULT_CONFIRMATION_ROUNDS) {
347
+ async execute(waitRounds = 10) {
306
348
  try {
307
349
  if (!this.swapTransactionsAdded) await this.addSwapTransactions();
308
350
  const { txIDs, ...result } = await this.atc.execute(this.algodClient, waitRounds);
309
351
  if (this.summaryData) {
310
352
  if (this.inputTransactionIndex !== void 0) this.summaryData.inputTxnId = txIDs[this.inputTransactionIndex];
311
353
  if (this.outputTransactionIndex !== void 0) this.summaryData.outputTxnId = txIDs[this.outputTransactionIndex];
354
+ if (this.summaryData.launch && this.curveTransactionIndex !== void 0) this.summaryData.launch.curveTxnId = txIDs[this.curveTransactionIndex];
312
355
  }
313
356
  await this.extractActualOutputAmount();
314
357
  return {
@@ -316,7 +359,7 @@ var SwapComposer = class SwapComposer {
316
359
  txIds: txIDs
317
360
  };
318
361
  } catch (error) {
319
- const { logSwapExecutionFailure } = await import("./debug-8aRsrfhx.mjs");
362
+ const { logSwapExecutionFailure } = await import("./debug-CU-Ps4nm.mjs");
320
363
  logSwapExecutionFailure({
321
364
  quote: this.quote,
322
365
  address: this.address,
@@ -357,7 +400,7 @@ var SwapComposer = class SwapComposer {
357
400
  * ```
358
401
  */
359
402
  getInputTransactionId() {
360
- if (this.getStatus() < SwapComposerStatus.BUILT) return;
403
+ if (this.getStatus() < 1) return;
361
404
  if (this.inputTransactionIndex === void 0) return;
362
405
  return (this.atc.buildGroup()[this.inputTransactionIndex]?.txn)?.txID();
363
406
  }
@@ -388,6 +431,8 @@ var SwapComposer = class SwapComposer {
388
431
  if (!this.summaryData || this.summaryData.outputAmount === void 0 || this.summaryData.inputTxnId === void 0 || this.summaryData.outputTxnId === void 0 || this.summaryData.outputSender === void 0) return;
389
432
  const txns = this.atc.buildGroup();
390
433
  const totalFees = txns.reduce((sum, tws) => sum + tws.txn.fee, 0n);
434
+ const launch = this.summaryData.launch;
435
+ const curveTxnId = launch?.curveTxnId;
391
436
  return {
392
437
  inputAssetId: this.summaryData.inputAssetId,
393
438
  outputAssetId: this.summaryData.outputAssetId,
@@ -396,10 +441,17 @@ var SwapComposer = class SwapComposer {
396
441
  type: this.quote.type,
397
442
  totalFees,
398
443
  transactionCount: txns.length,
399
- inputTxnId: this.summaryData.inputTxnId,
444
+ inputTxnId: launch?.side === "sell" && curveTxnId ? curveTxnId : this.summaryData.inputTxnId,
400
445
  outputTxnId: this.summaryData.outputTxnId,
401
446
  inputSender: this.summaryData.inputSender,
402
- outputSender: this.summaryData.outputSender
447
+ outputSender: this.summaryData.outputSender,
448
+ ...launch && curveTxnId && { launch: {
449
+ side: launch.side,
450
+ tokenNum: launch.tokenNum,
451
+ curveTxnId,
452
+ bondingAmount: launch.bondingAmount,
453
+ refund: launch.refund
454
+ } }
403
455
  };
404
456
  }
405
457
  /**
@@ -416,32 +468,25 @@ var SwapComposer = class SwapComposer {
416
468
  */
417
469
  async processSwapTransactions() {
418
470
  const appOptIns = await this.processRequiredAppOptIns();
419
- const swapTxns = [];
420
- let inputTxnRelativeIndex;
421
- let inputTxn;
422
- for (let i = 0; i < this.swapTxns.length; i++) {
423
- const swapTxn = this.swapTxns[i];
424
- if (!swapTxn) continue;
471
+ const decoded = this.swapTxns.map((swapTxn, i) => {
425
472
  try {
426
473
  const txn = decodeUnsignedTransaction(Buffer.from(swapTxn.data, "base64"));
427
474
  delete txn.group;
428
- if (swapTxn.signature !== false) swapTxns.push({
475
+ return {
429
476
  txn,
430
- signer: this.createSwapSigner(swapTxn.signature)
431
- });
432
- else {
433
- if (this.note !== void 0) txn.note = this.note;
434
- inputTxnRelativeIndex = appOptIns.length + swapTxns.length;
435
- inputTxn = txn;
436
- swapTxns.push({
437
- txn,
438
- signer: this.defaultSigner
439
- });
440
- }
477
+ signature: swapTxn.signature
478
+ };
441
479
  } catch (error) {
442
- throw new Error(`Failed to process swap transaction at index ${i}: ${error instanceof Error ? error.message : String(error)}`);
480
+ throw new Error(`Failed to process swap transaction at index ${i}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
443
481
  }
444
- }
482
+ });
483
+ const inputIndex = this.findInputTransactionIndex(decoded);
484
+ const inputTxn = inputIndex !== void 0 ? decoded[inputIndex]?.txn : void 0;
485
+ if (inputTxn && this.note !== void 0) inputTxn.note = this.note;
486
+ const swapTxns = decoded.map(({ txn, signature }) => ({
487
+ txn,
488
+ signer: signature === false ? this.defaultSigner : this.createSwapSigner(signature)
489
+ }));
445
490
  if (inputTxn) {
446
491
  const paymentAmount = inputTxn.payment?.amount;
447
492
  const assetTransferAmount = inputTxn.assetTransfer?.amount;
@@ -453,12 +498,63 @@ var SwapComposer = class SwapComposer {
453
498
  inputSender: this.address
454
499
  };
455
500
  }
456
- const outputTxnRelativeIndex = swapTxns.length > 0 ? appOptIns.length + swapTxns.length - 1 : void 0;
501
+ const curveIndex = this.initLaunchSummary(decoded);
502
+ const outputIndex = this.summaryData?.launch?.side === "buy" && curveIndex !== void 0 ? curveIndex : swapTxns.length - 1;
503
+ const outputTxnRelativeIndex = outputIndex >= 0 ? appOptIns.length + outputIndex : void 0;
457
504
  return {
458
505
  txns: [...appOptIns, ...swapTxns],
459
- inputTxnRelativeIndex,
460
- outputTxnRelativeIndex
506
+ inputTxnRelativeIndex: inputIndex !== void 0 ? appOptIns.length + inputIndex : void 0,
507
+ outputTxnRelativeIndex,
508
+ curveTxnRelativeIndex: curveIndex !== void 0 ? appOptIns.length + curveIndex : void 0
509
+ };
510
+ }
511
+ /**
512
+ * Finds the swap's input transaction: the user's payment or asset transfer
513
+ * to the router app, whose address comes from the router's pre-signed app
514
+ * calls. Falls back to the last user-signed transaction for groups without
515
+ * a router app call.
516
+ */
517
+ findInputTransactionIndex(txns) {
518
+ const routerAppId = txns.find(({ txn, signature }) => signature !== false && txn.type === TransactionType.appl)?.txn.applicationCall?.appIndex;
519
+ if (routerAppId !== void 0) {
520
+ const routerAddress = getApplicationAddress(routerAppId).toString();
521
+ const index = txns.findIndex(({ txn, signature }) => signature === false && (txn.payment?.receiver ?? txn.assetTransfer?.receiver)?.toString() === routerAddress);
522
+ if (index !== -1) return index;
523
+ }
524
+ for (let i = txns.length - 1; i >= 0; i--) if (txns[i]?.signature === false) return i;
525
+ }
526
+ /**
527
+ * For a launch quote, finds the user-signed curve buy or sell call and
528
+ * records the curve leg in summaryData. On a sell, the input amount becomes
529
+ * the launch tokens sold.
530
+ *
531
+ * @returns The curve call's index within the swap transactions, if found
532
+ */
533
+ initLaunchSummary(txns) {
534
+ const side = this.quote.launch?.side;
535
+ if (!side || !this.summaryData) return void 0;
536
+ const curveIndex = txns.findIndex(({ txn, signature }) => signature === false && launchCurveSide(txn) === side);
537
+ const curveCall = txns[curveIndex]?.txn.applicationCall;
538
+ const tokenNumArg = curveCall?.appArgs[1];
539
+ if (!curveCall || !tokenNumArg) return void 0;
540
+ const appAddress = getApplicationAddress(curveCall.appIndex).toString();
541
+ let bondingAmount;
542
+ if (side === "buy") {
543
+ const payment = txns[curveIndex - 1]?.txn;
544
+ if ((payment?.payment?.receiver ?? payment?.assetTransfer?.receiver)?.toString() === appAddress) bondingAmount = payment?.payment?.amount ?? payment?.assetTransfer?.amount;
545
+ } else {
546
+ const tokensToSell = curveCall.appArgs[2];
547
+ if (tokensToSell) this.summaryData.inputAmount = decodeUint64(tokensToSell, "bigint");
548
+ }
549
+ this.summaryData.launch = {
550
+ side,
551
+ tokenNum: decodeUint64(tokenNumArg, "bigint"),
552
+ bondingTokenId: BigInt(this.quote.launch?.bondingTokenId ?? 0),
553
+ appAddress,
554
+ bondingAmount,
555
+ refund: 0n
461
556
  };
557
+ return curveIndex;
462
558
  }
463
559
  /**
464
560
  * Creates opt-in transactions for apps the user hasn't opted into yet
@@ -502,16 +598,19 @@ var SwapComposer = class SwapComposer {
502
598
  try {
503
599
  if (signature.type === "logic_signature") {
504
600
  const valueArray = signature.value;
505
- const decoded = msgpackRawDecode(new Uint8Array(Object.values(valueArray)));
601
+ const valueBytes = new Uint8Array(Object.values(valueArray));
602
+ const decoded = msgpackRawDecode(valueBytes);
506
603
  if (!decoded.lsig) throw new Error("Logic signature structure missing lsig field");
507
604
  const lsig = decoded.lsig;
508
- return signLogicSigTransactionObject(transaction, new LogicSigAccount(lsig.l, lsig.arg)).blob;
605
+ const logicSigAccount = new LogicSigAccount(lsig.l, lsig.arg);
606
+ return signLogicSigTransactionObject(transaction, logicSigAccount).blob;
509
607
  } else if (signature.type === "secret_key") {
510
608
  const valueArray = signature.value;
511
- return signTransaction(transaction, new Uint8Array(Object.values(valueArray))).blob;
609
+ const secretKey = new Uint8Array(Object.values(valueArray));
610
+ return signTransaction(transaction, secretKey).blob;
512
611
  } else throw new Error(`Unsupported signature type: ${signature.type}`);
513
612
  } catch (error) {
514
- throw new Error(`Failed to re-sign transaction: ${error instanceof Error ? error.message : String(error)}`);
613
+ throw new Error(`Failed to re-sign transaction: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
515
614
  }
516
615
  }
517
616
  /**
@@ -523,6 +622,7 @@ var SwapComposer = class SwapComposer {
523
622
  ...this.quote,
524
623
  quote: this.quote.quote === "" ? 0n : BigInt(this.quote.quote),
525
624
  amount: 0n,
625
+ launch: this.quote.launch && toSwapLaunchQuote(this.quote.launch),
526
626
  createdAt: Date.now()
527
627
  };
528
628
  const quoteContext = {
@@ -554,16 +654,32 @@ var SwapComposer = class SwapComposer {
554
654
  * Extract the actual output amount from the confirmed output transaction's inner transactions
555
655
  *
556
656
  * Analyzes only the output transaction (last app call in the swap) to find the
557
- * inner transaction that transfers the output asset to the user.
657
+ * inner transaction that transfers the output asset to the user. On a launch
658
+ * buy the output is a balance on the HayLaunch app, so it is read from the
659
+ * curve buy call's return value instead.
558
660
  */
559
661
  async extractActualOutputAmount() {
560
662
  if (!this.summaryData?.outputTxnId) return;
561
663
  const outputAssetId = this.summaryData.outputAssetId;
562
664
  const userAddress = this.address;
665
+ const launch = this.summaryData.launch;
563
666
  try {
564
- const innerTxns = (await this.algodClient.pendingTransactionInformation(this.summaryData.outputTxnId).do()).innerTxns;
565
- if (!innerTxns) return;
566
- for (const innerTxn of innerTxns) {
667
+ const pendingInfo = await this.algodClient.pendingTransactionInformation(this.summaryData.outputTxnId).do();
668
+ if (launch?.side === "buy") {
669
+ const tokensCredited = readUint64Return(pendingInfo.logs);
670
+ if (tokensCredited !== void 0) {
671
+ this.summaryData.outputAmount = tokensCredited;
672
+ this.summaryData.outputSender = launch.appAddress;
673
+ }
674
+ for (const innerTxn of pendingInfo.innerTxns ?? []) {
675
+ const txn = innerTxn.txn.txn;
676
+ if (launch.bondingTokenId === 0n) {
677
+ if (txn.payment?.receiver.toString() === userAddress) launch.refund += txn.payment.amount;
678
+ } else if (txn.assetTransfer?.receiver.toString() === userAddress && txn.assetTransfer.assetIndex === launch.bondingTokenId) launch.refund += txn.assetTransfer.amount;
679
+ }
680
+ return;
681
+ }
682
+ for (const innerTxn of pendingInfo.innerTxns ?? []) {
567
683
  const txn = innerTxn.txn.txn;
568
684
  const payment = txn.payment;
569
685
  const assetTransfer = txn.assetTransfer;
@@ -582,15 +698,20 @@ var SwapComposer = class SwapComposer {
582
698
  }
583
699
  }
584
700
  } catch {}
701
+ if (launch?.side === "sell" && launch.curveTxnId) try {
702
+ launch.bondingAmount = readUint64Return((await this.algodClient.pendingTransactionInformation(launch.curveTxnId).do()).logs);
703
+ } catch {}
585
704
  }
586
705
  };
587
-
588
706
  //#endregion
589
707
  //#region src/utils.ts
590
708
  /**
591
709
  * HTTP error with status code and response data
592
710
  */
593
711
  var HTTPError = class extends Error {
712
+ status;
713
+ statusText;
714
+ data;
594
715
  constructor(status, statusText, data) {
595
716
  super(`HTTP ${status} ${statusText}`);
596
717
  this.status = status;
@@ -627,7 +748,6 @@ async function request(url, options) {
627
748
  }
628
749
  return response.json();
629
750
  }
630
-
631
751
  //#endregion
632
752
  //#region src/client.ts
633
753
  /**
@@ -640,14 +760,14 @@ async function request(url, options) {
640
760
  * @example
641
761
  * ```typescript
642
762
  * const router = new RouterClient({
643
- * apiKey: 'your-api-key',
763
+ * apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)
644
764
  * })
645
765
  * ```
646
766
  *
647
767
  * @example
648
768
  * ```typescript
649
769
  * const router = new RouterClient({
650
- * apiKey: 'your-api-key',
770
+ * apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)
651
771
  * apiBaseUrl: 'https://hayrouter.txnlab.dev',
652
772
  * algodUri: 'https://mainnet-api.4160.nodely.dev/',
653
773
  * algodToken: '',
@@ -678,17 +798,17 @@ var RouterClient = class {
678
798
  * @param config.middleware - Array of middleware to apply to swaps (default: [])
679
799
  */
680
800
  constructor(config) {
681
- const debugLevel = config.debugLevel ?? DEFAULT_DEBUG_LEVEL;
801
+ const debugLevel = config.debugLevel ?? "none";
682
802
  Logger.setLevel(debugLevel);
683
803
  this.config = {
684
804
  apiKey: this.validateApiKey(config.apiKey),
685
- apiBaseUrl: config.apiBaseUrl ?? DEFAULT_API_BASE_URL,
686
- algodUri: config.algodUri ?? DEFAULT_ALGOD_URI,
687
- algodToken: config.algodToken ?? DEFAULT_ALGOD_TOKEN,
688
- algodPort: config.algodPort ?? DEFAULT_ALGOD_PORT,
805
+ apiBaseUrl: config.apiBaseUrl ?? "https://hayrouter.txnlab.dev/api",
806
+ algodUri: config.algodUri ?? "https://mainnet-api.4160.nodely.dev/",
807
+ algodToken: config.algodToken ?? "",
808
+ algodPort: config.algodPort ?? 443,
689
809
  referrerAddress: config.referrerAddress ? this.validateAddress(config.referrerAddress) : void 0,
690
- feeBps: this.validateFeeBps(config.feeBps ?? DEFAULT_FEE_BPS),
691
- autoOptIn: config.autoOptIn ?? DEFAULT_AUTO_OPT_IN,
810
+ feeBps: this.validateFeeBps(config.feeBps ?? 10),
811
+ autoOptIn: config.autoOptIn ?? false,
692
812
  debugLevel
693
813
  };
694
814
  this.algodClient = new Algodv2(this.config.algodToken, this.config.algodUri, this.config.algodPort);
@@ -713,6 +833,7 @@ var RouterClient = class {
713
833
  * - If true: API reduces maxGroupSize by 1 and includes opt-in (always included, even if not needed)
714
834
  * - If false: No opt-in transaction included
715
835
  * - If undefined: Falls back to `config.autoOptIn` behavior with account check (if `params.address` is provided)
836
+ * @param params.slippage - Slippage tolerance as a percentage, in [0, 100). Pass the value you will execute with; required for accurate pre-bonding launch token quotes
716
837
  * @returns A FetchQuoteResponse object with routing information
717
838
  *
718
839
  * @example
@@ -726,12 +847,15 @@ var RouterClient = class {
726
847
  * ```
727
848
  */
728
849
  async fetchQuote(params) {
729
- const { fromASAID, toASAID, amount, type = "fixed-input", disabledProtocols = [], maxGroupSize = DEFAULT_MAX_GROUP_SIZE, maxDepth = DEFAULT_MAX_DEPTH, optIn, address, _allowNonComposableSwaps = false } = params;
850
+ const { fromASAID, toASAID, amount, type = "fixed-input", disabledProtocols = [], maxGroupSize = 16, maxDepth = 4, optIn, address, slippage, _allowNonComposableSwaps = false } = params;
851
+ if (slippage !== void 0) this.validateSlippage(slippage);
730
852
  if (_allowNonComposableSwaps && (optIn === true || this.config.autoOptIn)) throw new Error("Cannot use _allowNonComposableSwaps with optIn or autoOptIn. When allowing non-composable swaps (Tinyman v1), you must handle asset opt-ins and opt-outs in separate transaction groups.");
731
- const allDisabledProtocols = _allowNonComposableSwaps ? [...new Set(["Humble", ...disabledProtocols])] : [...new Set([...DEPRECATED_PROTOCOLS, ...disabledProtocols])];
853
+ const allDisabledProtocols = _allowNonComposableSwaps ? [.../* @__PURE__ */ new Set(["Humble", ...disabledProtocols])] : [.../* @__PURE__ */ new Set([...DEPRECATED_PROTOCOLS, ...disabledProtocols])];
732
854
  let includeOptIn = optIn;
733
- if (includeOptIn === void 0 && this.config.autoOptIn) if (address) includeOptIn = await this.needsAssetOptIn(this.validateAddress(address), toASAID);
734
- else console.warn("autoOptIn is enabled but no address provided to fetchQuote(). Asset opt-in check skipped.");
855
+ if (includeOptIn === void 0 && this.config.autoOptIn) {
856
+ if (address) includeOptIn = await this.needsAssetOptIn(this.validateAddress(address), toASAID);
857
+ else console.warn("autoOptIn is enabled but no address provided to fetchQuote(). Asset opt-in check skipped.");
858
+ }
735
859
  const url = new URL(`${this.config.apiBaseUrl}/fetchQuote`);
736
860
  url.searchParams.append("apiKey", this.config.apiKey);
737
861
  url.searchParams.append("algodUri", this.config.algodUri);
@@ -746,6 +870,7 @@ var RouterClient = class {
746
870
  url.searchParams.append("maxGroupSize", maxGroupSize.toString());
747
871
  url.searchParams.append("maxDepth", maxDepth.toString());
748
872
  if (typeof includeOptIn === "boolean") url.searchParams.append("optIn", String(includeOptIn));
873
+ if (slippage !== void 0) url.searchParams.append("slippage", String(slippage));
749
874
  if (this.config.referrerAddress) url.searchParams.append("referrerAddress", this.config.referrerAddress);
750
875
  return request(url.toString());
751
876
  }
@@ -824,6 +949,7 @@ var RouterClient = class {
824
949
  * @param params.maxDepth - The maximum depth (default: 4)
825
950
  * @param params.address - The address of the account that will perform the swap (recommended when using `config.autoOptIn` or `params.optIn`)
826
951
  * @param params.optIn - Whether to include asset opt-in transaction
952
+ * @param params.slippage - Slippage tolerance as a percentage, in [0, 100). Pass the value you will execute with; required for accurate pre-bonding launch token quotes
827
953
  * @returns A SwapQuote enhanced quote result
828
954
  *
829
955
  * @example
@@ -855,11 +981,14 @@ var RouterClient = class {
855
981
  };
856
982
  for (const mw of this.middleware) if (await mw.shouldApply(quoteContext) && mw.adjustQuoteParams) adjustedParams = await mw.adjustQuoteParams(adjustedParams);
857
983
  const response = await this.fetchQuote(adjustedParams);
984
+ const { launch, ...rest } = response;
858
985
  return {
859
- ...response,
986
+ ...rest,
860
987
  quote: response.quote === "" ? 0n : BigInt(response.quote),
861
988
  amount: BigInt(params.amount),
862
989
  address: params.address ?? void 0,
990
+ slippage: adjustedParams.slippage,
991
+ launch: launch && toSwapLaunchQuote(launch),
863
992
  createdAt: Date.now()
864
993
  };
865
994
  }
@@ -913,6 +1042,7 @@ var RouterClient = class {
913
1042
  */
914
1043
  async newSwap(config) {
915
1044
  const { quote, address, slippage, signer, note } = config;
1045
+ if (quote.launch && "createdAt" in quote && quote.slippage !== slippage) console.warn(quote.slippage === void 0 ? `Launch token quote was fetched without slippage but the swap uses ${slippage}%. Pass the same slippage to newQuote() for an accurate quoted output.` : `Launch token quote was fetched with ${quote.slippage}% slippage but the swap uses ${slippage}%. The quoted output may not match what is delivered.`);
916
1046
  return new SwapComposer({
917
1047
  quote,
918
1048
  swapTxns: (await this.fetchSwapTransactions({
@@ -943,14 +1073,20 @@ var RouterClient = class {
943
1073
  return address;
944
1074
  }
945
1075
  /**
1076
+ * Validates a slippage percentage (0 <= slippage < 100)
1077
+ */
1078
+ validateSlippage(slippage) {
1079
+ if (!Number.isFinite(slippage) || slippage < 0 || slippage >= 100) throw new Error(`Invalid slippage: ${slippage} (must be >= 0 and < 100)`);
1080
+ return slippage;
1081
+ }
1082
+ /**
946
1083
  * Validates the fee in basis points (max 300 = 3.00%)
947
1084
  */
948
1085
  validateFeeBps(feeBps) {
949
- if (feeBps < 0 || feeBps > MAX_FEE_BPS) throw new Error(`Invalid fee: ${feeBps} basis points (must be between 0 and ${MAX_FEE_BPS})`);
1086
+ if (feeBps < 0 || feeBps > 300) throw new Error(`Invalid fee: ${feeBps} basis points (must be between 0 and 300)`);
950
1087
  return feeBps;
951
1088
  }
952
1089
  };
953
-
954
1090
  //#endregion
955
1091
  //#region src/middleware.ts
956
1092
  /**
@@ -966,7 +1102,7 @@ var RouterClient = class {
966
1102
  * })
967
1103
  *
968
1104
  * const router = new RouterClient({
969
- * apiKey: 'your-api-key',
1105
+ * apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)
970
1106
  * middleware: [autoOptOut],
971
1107
  * })
972
1108
  *
@@ -1022,7 +1158,7 @@ var AutoOptOutMiddleware = class {
1022
1158
  }];
1023
1159
  }
1024
1160
  };
1025
-
1026
1161
  //#endregion
1027
1162
  export { AutoOptOutMiddleware, DEFAULT_ALGOD_PORT, DEFAULT_ALGOD_TOKEN, DEFAULT_ALGOD_URI, DEFAULT_API_BASE_URL, DEFAULT_AUTO_OPT_IN, DEFAULT_CONFIRMATION_ROUNDS, DEFAULT_DEBUG_LEVEL, DEFAULT_FEE_BPS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_GROUP_SIZE, DEPRECATED_PROTOCOLS, HTTPError, MAX_FEE_BPS, Protocol, RouterClient, SwapComposer, SwapComposerStatus, request };
1163
+
1028
1164
  //# sourceMappingURL=index.mjs.map