@avail-project/ca-common 1.0.1 → 2.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/cjs/xcs/autochoice.js +96 -237
- package/dist/cjs/xcs/bebop-agg.js +34 -5
- package/dist/cjs/xcs/index.js +0 -1
- package/dist/cjs/xcs/lifi-agg.js +34 -5
- package/dist/esm/xcs/autochoice.js +98 -238
- package/dist/esm/xcs/bebop-agg.js +34 -5
- package/dist/esm/xcs/index.js +0 -1
- package/dist/esm/xcs/lifi-agg.js +34 -5
- package/dist/types/xcs/autochoice.d.ts +6 -52
- package/dist/types/xcs/bebop-agg.d.ts +2 -2
- package/dist/types/xcs/iface.d.ts +37 -5
- package/dist/types/xcs/index.d.ts +0 -1
- package/dist/types/xcs/lifi-agg.d.ts +18 -14
- package/package.json +1 -1
- package/dist/cjs/xcs/yieldyak-agg.js +0 -113
- package/dist/esm/xcs/yieldyak-agg.js +0 -109
- package/dist/types/xcs/yieldyak-agg.d.ts +0 -21
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AutoSelectionError = void 0;
|
|
4
4
|
exports.aggregateAggregators = aggregateAggregators;
|
|
5
5
|
exports.autoSelectSourcesV2 = autoSelectSourcesV2;
|
|
6
|
-
exports.autoSelectSources = autoSelectSources;
|
|
7
6
|
exports.determineDestinationSwaps = determineDestinationSwaps;
|
|
8
7
|
exports.liquidateInputHoldings = liquidateInputHoldings;
|
|
9
8
|
exports.destinationSwapWithExactIn = destinationSwapWithExactIn;
|
|
@@ -36,7 +35,7 @@ async function aggregateAggregators(requests, aggregators, mode) {
|
|
|
36
35
|
switch (mode) {
|
|
37
36
|
case 0 /* AggregateAggregatorsMode.MaximizeOutput */: {
|
|
38
37
|
for (let i = 0; i < requests.length; i++) {
|
|
39
|
-
const best = (0, data_1.maxByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })), (r) => r.quote?.
|
|
38
|
+
const best = (0, data_1.maxByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })), (r) => r.quote?.output.amountRaw ?? 0n);
|
|
40
39
|
if (best != null) {
|
|
41
40
|
final[i] = best;
|
|
42
41
|
}
|
|
@@ -51,7 +50,9 @@ async function aggregateAggregators(requests, aggregators, mode) {
|
|
|
51
50
|
}
|
|
52
51
|
case 1 /* AggregateAggregatorsMode.MinimizeInput */: {
|
|
53
52
|
for (let i = 0; i < requests.length; i++) {
|
|
54
|
-
const best = (0, data_1.minByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })),
|
|
53
|
+
const best = (0, data_1.minByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })),
|
|
54
|
+
// Default null quotes to MAX so they never win as the minimum
|
|
55
|
+
(r) => r.quote?.input.amountRaw ?? BigInt(Number.MAX_SAFE_INTEGER));
|
|
55
56
|
if (best != null) {
|
|
56
57
|
final[i] = best;
|
|
57
58
|
}
|
|
@@ -109,7 +110,7 @@ cots = [(1 COT, 1), (1 COT, 4)]
|
|
|
109
110
|
*/
|
|
110
111
|
async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
111
112
|
// Assumption: Holding is already sorted in usage priority
|
|
112
|
-
console.
|
|
113
|
+
console.debug("XCS | SSV2:", {
|
|
113
114
|
holdings,
|
|
114
115
|
outputRequired: outputRequired.toFixed(),
|
|
115
116
|
});
|
|
@@ -122,14 +123,14 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
122
123
|
}
|
|
123
124
|
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
124
125
|
if (correspondingCurrency == null) {
|
|
125
|
-
console.
|
|
126
|
+
console.debug("XCS | SS | Skipping because correspondingCurrency is null", {
|
|
126
127
|
chain,
|
|
127
128
|
correspondingCurrency,
|
|
128
129
|
});
|
|
129
130
|
continue;
|
|
130
131
|
}
|
|
131
132
|
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
132
|
-
const normalizedAmount = new decimal_js_1.default(holding.
|
|
133
|
+
const normalizedAmount = new decimal_js_1.default(holding.amountRaw).div(decimal_js_1.default.pow(10, correspondingCurrency.decimals));
|
|
133
134
|
cotList.push({
|
|
134
135
|
amount: normalizedAmount,
|
|
135
136
|
idx,
|
|
@@ -145,7 +146,7 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
145
146
|
type: iface_1.QuoteType.EXACT_IN,
|
|
146
147
|
chain: chain.ChainID,
|
|
147
148
|
inputToken: holding.tokenAddress,
|
|
148
|
-
inputAmount: holding.
|
|
149
|
+
inputAmount: holding.amountRaw,
|
|
149
150
|
outputToken: correspondingCurrency.tokenAddress,
|
|
150
151
|
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
151
152
|
},
|
|
@@ -183,10 +184,10 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
183
184
|
if (remainder.lte(0))
|
|
184
185
|
break;
|
|
185
186
|
}
|
|
186
|
-
console.
|
|
187
|
+
console.debug("XCS | SS | Early return with continuous COTs:", {
|
|
187
188
|
cots: usedCOTs,
|
|
188
189
|
});
|
|
189
|
-
return {
|
|
190
|
+
return { quoteResponses: [], usedCOTs };
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
}
|
|
@@ -211,7 +212,7 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
211
212
|
// Sort by original index to maintain priority
|
|
212
213
|
processingQueue.sort((a, b) => a.idx - b.idx);
|
|
213
214
|
const responses = await aggregateAggregators(fullLiquidationQuotes.map((fq) => fq.req), aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
214
|
-
console.
|
|
215
|
+
console.debug("AutoSelectSources:Quotes", responses);
|
|
215
216
|
const final = [];
|
|
216
217
|
const usedCOTs = [];
|
|
217
218
|
let remainder = outputRequired;
|
|
@@ -230,7 +231,7 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
230
231
|
cur: cotData.currency,
|
|
231
232
|
});
|
|
232
233
|
remainder = remainder.minus(amountToUse);
|
|
233
|
-
console.
|
|
234
|
+
console.debug("selection:cot", {
|
|
234
235
|
idx: cotData.idx,
|
|
235
236
|
amountToUse: amountToUse.toFixed(),
|
|
236
237
|
remainder: remainder.toFixed(),
|
|
@@ -239,30 +240,32 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
239
240
|
else {
|
|
240
241
|
// Process non-COT holding - use existing quote logic
|
|
241
242
|
const { quoteData, responseIdx } = item;
|
|
242
|
-
const { quote: resp, aggregator
|
|
243
|
+
const { quote: resp, aggregator } = responses[responseIdx];
|
|
243
244
|
if (resp == null) {
|
|
244
245
|
continue;
|
|
245
246
|
}
|
|
246
|
-
console.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
q: quoteData,
|
|
251
|
-
resp,
|
|
252
|
-
agg,
|
|
247
|
+
console.debug("selection:quote", {
|
|
248
|
+
remainder: remainder.toFixed(),
|
|
249
|
+
input: resp.input,
|
|
250
|
+
output: resp.output,
|
|
253
251
|
});
|
|
254
252
|
const divisor = decimal_js_1.default.pow(10, quoteData.cur.decimals);
|
|
255
|
-
const oamD = decimal_js_1.default
|
|
253
|
+
const oamD = new decimal_js_1.default(resp.output.amount);
|
|
256
254
|
if (oamD.gt(remainder)) {
|
|
257
|
-
const indicativePrice = decimal_js_1.default.div(resp.
|
|
258
|
-
const userBal = new decimal_js_1.default(quoteData.originalHolding.
|
|
255
|
+
const indicativePrice = decimal_js_1.default.div(resp.input.amountRaw, resp.output.amountRaw);
|
|
256
|
+
const userBal = new decimal_js_1.default(quoteData.originalHolding.amountRaw);
|
|
259
257
|
// remainder is the output we want, so the input amount is remainder × indicativePrice
|
|
260
258
|
let expectedInput = decimal_js_1.default.min(remainder.mul(divisor).mul(indicativePrice).mul(safetyMultiplier), userBal);
|
|
259
|
+
let attempts = 0;
|
|
261
260
|
while (true) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
261
|
+
if (++attempts > 10) {
|
|
262
|
+
throw new AutoSelectionError("Partial quote did not converge");
|
|
263
|
+
}
|
|
264
|
+
console.debug("partial_quote_loop", {
|
|
265
|
+
indicativePrice: indicativePrice.toFixed(),
|
|
266
|
+
expectedInput: expectedInput.toFixed(),
|
|
267
|
+
userBal: userBal.toFixed(),
|
|
268
|
+
remainder: remainder.toFixed(),
|
|
266
269
|
});
|
|
267
270
|
const adequateQuoteResult = await aggregateAggregators([
|
|
268
271
|
{
|
|
@@ -272,21 +275,23 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
272
275
|
},
|
|
273
276
|
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
274
277
|
if (adequateQuoteResult.length !== 1) {
|
|
275
|
-
throw new AutoSelectionError("
|
|
278
|
+
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
276
279
|
}
|
|
277
280
|
const adequateQuote = adequateQuoteResult[0];
|
|
278
281
|
if (adequateQuote.quote == null) {
|
|
279
282
|
throw new AutoSelectionError("Couldn't get buy quote");
|
|
280
283
|
}
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
const quote = adequateQuote.quote;
|
|
285
|
+
console.log("partial_quote", {
|
|
286
|
+
quote,
|
|
283
287
|
});
|
|
284
|
-
const oam2D = decimal_js_1.default
|
|
288
|
+
const oam2D = new decimal_js_1.default(adequateQuote.quote.output.amount);
|
|
285
289
|
if (oam2D.gte(remainder)) {
|
|
286
290
|
final.push({
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
quote,
|
|
292
|
+
aggregator: adequateQuote.aggregator,
|
|
293
|
+
holding: quoteData.originalHolding,
|
|
294
|
+
chainID: Number(quoteData.req.chain.chainID),
|
|
290
295
|
});
|
|
291
296
|
remainder = remainder.minus(oam2D);
|
|
292
297
|
break;
|
|
@@ -300,168 +305,29 @@ async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggreg
|
|
|
300
305
|
}
|
|
301
306
|
}
|
|
302
307
|
else {
|
|
303
|
-
console.
|
|
308
|
+
console.debug("full_quote", resp);
|
|
304
309
|
final.push({
|
|
305
|
-
...quoteData,
|
|
306
310
|
quote: resp,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
console.log("XCS | SS | 3⒜", {
|
|
314
|
-
remainder,
|
|
315
|
-
final,
|
|
316
|
-
});
|
|
317
|
-
if (remainder.gt(0)) {
|
|
318
|
-
throw new AutoSelectionError("Failed to accumulate enough swaps to meet requirement");
|
|
319
|
-
}
|
|
320
|
-
console.log("XCS | SS | Final:", { quotes: final, cots: usedCOTs });
|
|
321
|
-
return { quotes: final, usedCOTs };
|
|
322
|
-
}
|
|
323
|
-
async function autoSelectSources(userAddress, holdings, outputRequired, aggregators, collectionFees, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
324
|
-
console.log("XCS | SS | Holdings:", holdings);
|
|
325
|
-
const groupedByChainID = (0, es_toolkit_1.groupBy)(holdings, (h) => (0, viem_1.bytesToHex)(h.chainID.toBytes()));
|
|
326
|
-
const fullLiquidationQuotes = [];
|
|
327
|
-
for (const holdings of Object.values(groupedByChainID)) {
|
|
328
|
-
const chain = data_1.ChaindataMap.get(holdings[0].chainID);
|
|
329
|
-
if (chain == null) {
|
|
330
|
-
throw new AutoSelectionError("Chain not found");
|
|
331
|
-
}
|
|
332
|
-
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
333
|
-
if (correspondingCurrency == null) {
|
|
334
|
-
console.log("XCS | SS | Skipping because correspondingCurrency is null", {
|
|
335
|
-
chain,
|
|
336
|
-
correspondingCurrency,
|
|
337
|
-
});
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const cfeeTuple = collectionFees.find((cf) => {
|
|
341
|
-
return (cf.universe === chain.Universe &&
|
|
342
|
-
Buffer.compare(cf.chainID, chain.ChainID32) === 0 &&
|
|
343
|
-
// output token is the CA one
|
|
344
|
-
Buffer.compare(cf.tokenAddress, correspondingCurrency.tokenAddress) ===
|
|
345
|
-
0);
|
|
346
|
-
});
|
|
347
|
-
const cfee = cfeeTuple != null ? (0, viem_1.bytesToBigInt)(cfeeTuple.fee) : 0n;
|
|
348
|
-
for (const holding of holdings) {
|
|
349
|
-
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
350
|
-
console.log("XCS | SS | Disqualifying", holding, "because holding.tokenAddress = CA asset");
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
fullLiquidationQuotes.push({
|
|
354
|
-
req: {
|
|
355
|
-
userAddress,
|
|
356
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
357
|
-
chain: chain.ChainID,
|
|
358
|
-
inputToken: holding.tokenAddress,
|
|
359
|
-
inputAmount: holding.amount,
|
|
360
|
-
outputToken: correspondingCurrency.tokenAddress,
|
|
361
|
-
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
362
|
-
},
|
|
363
|
-
// necessary for various purposes
|
|
364
|
-
cfee,
|
|
365
|
-
originalHolding: holding,
|
|
366
|
-
cur: correspondingCurrency,
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// const groupedByChainID = groupBy(quoteOutputs, h => h.chainIDHex)
|
|
371
|
-
const quotesByValue = (0, es_toolkit_1.orderBy)(fullLiquidationQuotes, [
|
|
372
|
-
(quoteOut) => quoteOut.cfee,
|
|
373
|
-
(quoteOut) => quoteOut.originalHolding.value, // once optimized for collections, we select the biggest asset we hold
|
|
374
|
-
], ["asc", "desc"]);
|
|
375
|
-
const responses = await aggregateAggregators(quotesByValue.map((fq) => fq.req), aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
376
|
-
console.log("XCS | SS | Responses:", responses);
|
|
377
|
-
const final = [];
|
|
378
|
-
let remainder = outputRequired; // assuming all that chains have the same amount of fixed point places
|
|
379
|
-
for (let i = 0; i < quotesByValue.length; i++) {
|
|
380
|
-
if (remainder.lte(0)) {
|
|
381
|
-
break;
|
|
382
|
-
}
|
|
383
|
-
const q = quotesByValue[i];
|
|
384
|
-
const { quote: resp, aggregator: agg } = responses[i];
|
|
385
|
-
if (resp == null) {
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
console.log("XCS | SS | 1", {
|
|
389
|
-
i,
|
|
390
|
-
remainder,
|
|
391
|
-
q,
|
|
392
|
-
resp,
|
|
393
|
-
agg,
|
|
394
|
-
});
|
|
395
|
-
const divisor = decimal_js_1.default.pow(10, q.cur.decimals);
|
|
396
|
-
const oamD = (0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum).div(divisor);
|
|
397
|
-
if (oamD.gt(remainder)) {
|
|
398
|
-
const indicativePrice = (0, data_1.convertBigIntToDecimal)(resp.inputAmount).div((0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum));
|
|
399
|
-
const userBal = (0, data_1.convertBigIntToDecimal)(q.originalHolding.amount);
|
|
400
|
-
// remainder is the output we want, so the input amount is remainder × indicativePrice
|
|
401
|
-
let expectedInput = decimal_js_1.default.min(remainder.mul(divisor).mul(indicativePrice).mul(safetyMultiplier), userBal);
|
|
402
|
-
while (true) {
|
|
403
|
-
console.log("XCS | SS | 2⒜", {
|
|
404
|
-
indicativePrice,
|
|
405
|
-
expectedInput,
|
|
406
|
-
userBal,
|
|
311
|
+
holding: quoteData.originalHolding,
|
|
312
|
+
aggregator,
|
|
313
|
+
chainID: Number(quoteData.req.chain.chainID),
|
|
407
314
|
});
|
|
408
|
-
|
|
409
|
-
{
|
|
410
|
-
...q.req,
|
|
411
|
-
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
412
|
-
inputAmount: (0, data_1.convertDecimalToBigInt)(expectedInput),
|
|
413
|
-
},
|
|
414
|
-
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
415
|
-
if (adequateQuoteResult.length !== 1) {
|
|
416
|
-
throw new AutoSelectionError("???");
|
|
417
|
-
}
|
|
418
|
-
const adequateQuote = adequateQuoteResult[0];
|
|
419
|
-
if (adequateQuote.quote == null) {
|
|
420
|
-
throw new AutoSelectionError("Couldn't get buy quote");
|
|
421
|
-
}
|
|
422
|
-
console.log("XCS | SS | 2⒜⑴", {
|
|
423
|
-
adequateQuote,
|
|
424
|
-
});
|
|
425
|
-
const oam2D = (0, data_1.convertBigIntToDecimal)(adequateQuote.quote.outputAmountMinimum).div(divisor);
|
|
426
|
-
if (oam2D.gte(remainder)) {
|
|
427
|
-
final.push({
|
|
428
|
-
...q,
|
|
429
|
-
quote: adequateQuote.quote,
|
|
430
|
-
agg: adequateQuote.aggregator,
|
|
431
|
-
});
|
|
432
|
-
remainder = remainder.minus(oam2D);
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
else if (expectedInput.eq(userBal)) {
|
|
436
|
-
throw new AutoSelectionError("Holding was supposedly enough to meet the full requirement but ceased to be so subsequently");
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
expectedInput = decimal_js_1.default.min(expectedInput.mul(safetyMultiplier), userBal); // try again with higher amount
|
|
440
|
-
}
|
|
315
|
+
remainder = remainder.minus(resp.output.amount);
|
|
441
316
|
}
|
|
442
317
|
}
|
|
443
|
-
else {
|
|
444
|
-
console.log("XCS | SS | 2⒝", resp);
|
|
445
|
-
final.push({
|
|
446
|
-
...q,
|
|
447
|
-
quote: resp,
|
|
448
|
-
agg,
|
|
449
|
-
});
|
|
450
|
-
remainder = remainder.minus((0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum).div(divisor));
|
|
451
|
-
}
|
|
452
318
|
}
|
|
453
|
-
console.
|
|
454
|
-
remainder,
|
|
319
|
+
console.debug("quotes_and_remainder", {
|
|
320
|
+
remainder: remainder.toFixed(),
|
|
455
321
|
final,
|
|
456
322
|
});
|
|
457
323
|
if (remainder.gt(0)) {
|
|
458
|
-
throw new AutoSelectionError("
|
|
324
|
+
throw new AutoSelectionError("NOT_ENOUGH_SWAP_FOR_REQUIREMENT");
|
|
459
325
|
}
|
|
460
|
-
console.log("
|
|
461
|
-
return final;
|
|
326
|
+
console.log("final_quotes", { quotes: final, cots: usedCOTs });
|
|
327
|
+
return { quoteResponses: final, usedCOTs };
|
|
462
328
|
}
|
|
463
|
-
async function determineDestinationSwaps(userAddress,
|
|
464
|
-
const chaindata = data_1.ChaindataMap.get(chainID);
|
|
329
|
+
async function determineDestinationSwaps(userAddress, requirement, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
330
|
+
const chaindata = data_1.ChaindataMap.get(requirement.chainID);
|
|
465
331
|
if (chaindata == null) {
|
|
466
332
|
throw new AutoSelectionError("Chain not found");
|
|
467
333
|
}
|
|
@@ -473,33 +339,32 @@ async function determineDestinationSwaps(userAddress, chainID, requirement, aggr
|
|
|
473
339
|
// what happens if we happen to sell the requirement for the COT, what would the amount be?
|
|
474
340
|
const fullLiquidationQR = {
|
|
475
341
|
type: iface_1.QuoteType.EXACT_IN,
|
|
476
|
-
chain: chainID,
|
|
342
|
+
chain: requirement.chainID,
|
|
477
343
|
userAddress,
|
|
478
344
|
inputToken: requirement.tokenAddress,
|
|
479
345
|
outputToken: COT.tokenAddress,
|
|
480
|
-
inputAmount: requirement.
|
|
346
|
+
inputAmount: requirement.amountRaw,
|
|
481
347
|
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
482
348
|
};
|
|
483
349
|
const fullLiquidationResult = await aggregateAggregators([fullLiquidationQR], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
484
350
|
if (fullLiquidationResult.length !== 1) {
|
|
485
|
-
throw new AutoSelectionError("
|
|
351
|
+
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
486
352
|
}
|
|
487
353
|
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
488
354
|
if (fullLiquidationQuote.quote == null) {
|
|
489
355
|
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
490
356
|
}
|
|
491
|
-
let curAmount = (0, data_1.convertBigIntToDecimal)(fullLiquidationQuote.quote.
|
|
492
|
-
|
|
493
|
-
fullLiquidationQR,
|
|
494
|
-
fullLiquidationResult,
|
|
495
|
-
COT,
|
|
496
|
-
});
|
|
357
|
+
let curAmount = (0, data_1.convertBigIntToDecimal)(fullLiquidationQuote.quote.output.amountRaw).mul(safetyMultiplier);
|
|
358
|
+
let attempts = 0;
|
|
497
359
|
while (true) {
|
|
360
|
+
if (++attempts > 10) {
|
|
361
|
+
throw new AutoSelectionError("Destination swap quote did not converge");
|
|
362
|
+
}
|
|
498
363
|
const buyQuoteResult = await aggregateAggregators([
|
|
499
364
|
{
|
|
500
365
|
type: iface_1.QuoteType.EXACT_IN,
|
|
501
366
|
userAddress,
|
|
502
|
-
chain: chainID,
|
|
367
|
+
chain: requirement.chainID,
|
|
503
368
|
inputToken: COT.tokenAddress,
|
|
504
369
|
outputToken: requirement.tokenAddress,
|
|
505
370
|
inputAmount: (0, data_1.convertDecimalToBigInt)(curAmount),
|
|
@@ -507,21 +372,22 @@ async function determineDestinationSwaps(userAddress, chainID, requirement, aggr
|
|
|
507
372
|
},
|
|
508
373
|
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
509
374
|
if (buyQuoteResult.length !== 1) {
|
|
510
|
-
throw new AutoSelectionError("
|
|
375
|
+
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
511
376
|
}
|
|
512
377
|
const buyQuote = buyQuoteResult[0];
|
|
513
378
|
if (buyQuote.quote == null) {
|
|
514
379
|
throw new AutoSelectionError("Couldn't get buy quote");
|
|
515
380
|
}
|
|
516
|
-
console.
|
|
381
|
+
console.debug("XCS | DDS | 2⒜ iteration", {
|
|
517
382
|
buyQuote,
|
|
518
383
|
curAmount,
|
|
519
384
|
});
|
|
520
|
-
if (buyQuote.quote.
|
|
385
|
+
if (buyQuote.quote.output.amountRaw >= requirement.amountRaw) {
|
|
521
386
|
return {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
387
|
+
chainID: Number(requirement.chainID.chainID),
|
|
388
|
+
quote: buyQuote.quote,
|
|
389
|
+
aggregator: buyQuote.aggregator,
|
|
390
|
+
holding: requirement,
|
|
525
391
|
};
|
|
526
392
|
}
|
|
527
393
|
else {
|
|
@@ -529,8 +395,8 @@ async function determineDestinationSwaps(userAddress, chainID, requirement, aggr
|
|
|
529
395
|
}
|
|
530
396
|
}
|
|
531
397
|
}
|
|
532
|
-
async function liquidateInputHoldings(userAddress, holdings, aggregators,
|
|
533
|
-
console.
|
|
398
|
+
async function liquidateInputHoldings(userAddress, holdings, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
399
|
+
console.debug("XCS | LIH | Holdings:", holdings);
|
|
534
400
|
const groupedByChainID = (0, es_toolkit_1.groupBy)(holdings, (h) => (0, viem_1.bytesToHex)(h.chainID.toBytes()));
|
|
535
401
|
const fullLiquidationQuotes = [];
|
|
536
402
|
for (const holdings of Object.values(groupedByChainID)) {
|
|
@@ -540,20 +406,12 @@ async function liquidateInputHoldings(userAddress, holdings, aggregators, collec
|
|
|
540
406
|
}
|
|
541
407
|
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
542
408
|
if (correspondingCurrency == null) {
|
|
543
|
-
console.
|
|
409
|
+
console.debug("XCS | LIH | Skipping because correspondingCurrency is null", {
|
|
544
410
|
chain,
|
|
545
411
|
correspondingCurrency,
|
|
546
412
|
});
|
|
547
413
|
continue;
|
|
548
414
|
}
|
|
549
|
-
const cfeeTuple = collectionFees.find((cf) => {
|
|
550
|
-
return (cf.universe === chain.Universe &&
|
|
551
|
-
Buffer.compare(cf.chainID, chain.ChainID32) === 0 &&
|
|
552
|
-
// output token is the CA one
|
|
553
|
-
Buffer.compare(cf.tokenAddress, correspondingCurrency.tokenAddress) ===
|
|
554
|
-
0);
|
|
555
|
-
});
|
|
556
|
-
const cfee = cfeeTuple != null ? (0, viem_1.bytesToBigInt)(cfeeTuple.fee) : 0n;
|
|
557
415
|
for (const holding of holdings) {
|
|
558
416
|
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
559
417
|
console.log("XCS | LIH | Disqualifying", holding, "because holding.tokenAddress = CA asset");
|
|
@@ -565,48 +423,44 @@ async function liquidateInputHoldings(userAddress, holdings, aggregators, collec
|
|
|
565
423
|
type: iface_1.QuoteType.EXACT_IN,
|
|
566
424
|
chain: chain.ChainID,
|
|
567
425
|
inputToken: holding.tokenAddress,
|
|
568
|
-
inputAmount: holding.
|
|
426
|
+
inputAmount: holding.amountRaw,
|
|
569
427
|
outputToken: correspondingCurrency.tokenAddress,
|
|
570
428
|
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
571
429
|
},
|
|
572
430
|
// necessary for various purposes
|
|
573
|
-
cfee,
|
|
574
431
|
originalHolding: holding,
|
|
575
432
|
cur: correspondingCurrency,
|
|
576
433
|
});
|
|
577
434
|
}
|
|
578
435
|
}
|
|
579
436
|
const responses = await aggregateAggregators(fullLiquidationQuotes.map((fq) => fq.req), aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
580
|
-
console.
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
.
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
return
|
|
593
|
-
quotes: validResponses,
|
|
594
|
-
total,
|
|
595
|
-
};
|
|
437
|
+
console.debug("XCS | LIH | Responses:", responses);
|
|
438
|
+
const quotes = [];
|
|
439
|
+
for (const [i, response] of responses.entries()) {
|
|
440
|
+
if (response.quote !== null) {
|
|
441
|
+
quotes.push({
|
|
442
|
+
quote: response.quote,
|
|
443
|
+
aggregator: response.aggregator,
|
|
444
|
+
holding: fullLiquidationQuotes[i].originalHolding,
|
|
445
|
+
chainID: Number(fullLiquidationQuotes[i].req.chain.chainID),
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return quotes;
|
|
596
450
|
}
|
|
597
|
-
async function destinationSwapWithExactIn(userAddress,
|
|
598
|
-
const chaindata = data_1.ChaindataMap.get(
|
|
451
|
+
async function destinationSwapWithExactIn(userAddress, omniChainID, inputAmount, outputToken, aggregators, inputCurrency = data_1.CurrencyID.USDC) {
|
|
452
|
+
const chaindata = data_1.ChaindataMap.get(omniChainID);
|
|
599
453
|
if (chaindata == null) {
|
|
600
454
|
throw new AutoSelectionError("Chain not found");
|
|
601
455
|
}
|
|
602
|
-
const COT = chaindata.Currencies.find((cur) => cur.currencyID ===
|
|
456
|
+
const COT = chaindata.Currencies.find((cur) => cur.currencyID === inputCurrency);
|
|
603
457
|
if (COT == null) {
|
|
604
458
|
throw new AutoSelectionError("COT not present on the destination chain");
|
|
605
459
|
}
|
|
606
460
|
const fullLiquidationResult = await aggregateAggregators([
|
|
607
461
|
{
|
|
608
462
|
type: iface_1.QuoteType.EXACT_IN,
|
|
609
|
-
chain:
|
|
463
|
+
chain: omniChainID,
|
|
610
464
|
userAddress,
|
|
611
465
|
inputToken: COT.tokenAddress,
|
|
612
466
|
outputToken: outputToken,
|
|
@@ -615,15 +469,20 @@ async function destinationSwapWithExactIn(userAddress, chainID, inputAmount, out
|
|
|
615
469
|
},
|
|
616
470
|
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
617
471
|
if (fullLiquidationResult.length !== 1) {
|
|
618
|
-
throw new AutoSelectionError("
|
|
472
|
+
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
619
473
|
}
|
|
620
474
|
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
621
475
|
if (fullLiquidationQuote.quote == null) {
|
|
622
476
|
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
623
477
|
}
|
|
624
478
|
return {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
479
|
+
chainID: Number(omniChainID.chainID),
|
|
480
|
+
quote: fullLiquidationQuote.quote,
|
|
481
|
+
aggregator: fullLiquidationQuote.aggregator,
|
|
482
|
+
holding: {
|
|
483
|
+
amountRaw: inputAmount,
|
|
484
|
+
chainID: omniChainID,
|
|
485
|
+
tokenAddress: COT.tokenAddress,
|
|
486
|
+
},
|
|
628
487
|
};
|
|
629
488
|
}
|
|
@@ -7,6 +7,7 @@ const viem_1 = require("viem");
|
|
|
7
7
|
const iface_1 = require("./iface");
|
|
8
8
|
const definition_1 = require("../proto/definition");
|
|
9
9
|
const data_1 = require("../data");
|
|
10
|
+
const decimal_js_1 = tslib_1.__importDefault(require("decimal.js"));
|
|
10
11
|
// https://api.bebop.xyz/{jam|pmm}/chains
|
|
11
12
|
const ChainNameMapping = new Map(Object.entries({
|
|
12
13
|
ethereum: 1,
|
|
@@ -42,6 +43,7 @@ class BebopAggregator {
|
|
|
42
43
|
params: {
|
|
43
44
|
source: "arcana",
|
|
44
45
|
},
|
|
46
|
+
timeout: 10_000,
|
|
45
47
|
});
|
|
46
48
|
}
|
|
47
49
|
async getQuotes(requests) {
|
|
@@ -104,12 +106,39 @@ class BebopAggregator {
|
|
|
104
106
|
return null;
|
|
105
107
|
}
|
|
106
108
|
const buyT = bestRoute.quote.buyTokens[outputTokenAddr];
|
|
109
|
+
const sellT = bestRoute.quote.sellTokens[inputTokenAddr];
|
|
110
|
+
const outputAmountInDecimal = new decimal_js_1.default(buyT.minimumAmount)
|
|
111
|
+
.div(decimal_js_1.default.pow(10, buyT.decimals))
|
|
112
|
+
.toFixed(buyT.decimals);
|
|
113
|
+
const inputAmountInDecimal = new decimal_js_1.default(sellT.amount)
|
|
114
|
+
.div(decimal_js_1.default.pow(10, sellT.decimals))
|
|
115
|
+
.toFixed(sellT.decimals);
|
|
107
116
|
return {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
expiry: bestRoute.quote.expiry,
|
|
118
|
+
input: {
|
|
119
|
+
amount: inputAmountInDecimal,
|
|
120
|
+
amountRaw: BigInt(sellT.amount),
|
|
121
|
+
contractAddress: inputTokenAddr,
|
|
122
|
+
decimals: sellT.decimals,
|
|
123
|
+
value: decimal_js_1.default.mul(inputAmountInDecimal, sellT.priceUsd).toNumber(),
|
|
124
|
+
symbol: sellT.symbol,
|
|
125
|
+
},
|
|
126
|
+
output: {
|
|
127
|
+
amount: outputAmountInDecimal,
|
|
128
|
+
amountRaw: BigInt(buyT.minimumAmount),
|
|
129
|
+
contractAddress: outputTokenAddr,
|
|
130
|
+
decimals: buyT.decimals,
|
|
131
|
+
value: decimal_js_1.default.mul(buyT.priceUsd, outputAmountInDecimal).toNumber(),
|
|
132
|
+
symbol: buyT.symbol,
|
|
133
|
+
},
|
|
134
|
+
txData: {
|
|
135
|
+
approvalAddress: bestRoute.quote.approvalTarget,
|
|
136
|
+
tx: {
|
|
137
|
+
to: bestRoute.quote.tx.to,
|
|
138
|
+
value: bestRoute.quote.tx.value,
|
|
139
|
+
data: bestRoute.quote.tx.data,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
113
142
|
};
|
|
114
143
|
}));
|
|
115
144
|
return list.map((item) => {
|
package/dist/cjs/xcs/index.js
CHANGED
|
@@ -3,6 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./iface"), exports);
|
|
5
5
|
tslib_1.__exportStar(require("./lifi-agg"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./yieldyak-agg"), exports);
|
|
7
6
|
tslib_1.__exportStar(require("./bebop-agg"), exports);
|
|
8
7
|
tslib_1.__exportStar(require("./autochoice"), exports);
|