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