@avail-project/ca-common 1.0.1-beta1 → 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.
@@ -604,10 +604,10 @@ const RawData = [
604
604
  Currencies: [
605
605
  {
606
606
  CurrencyID: 1,
607
- TokenContractAddress: "0x000000000000000000000000590cb8868c6DeBc12CCd42E837042659cfB91504",
607
+ TokenContractAddress: "0x000000000000000000000000FAfDdbb3FC7688494971a79cc65DCa3EF82079E7",
608
608
  PermitVariant: permitutils_1.PermitVariant.EIP2612Canonical,
609
- PermitContractVersion: 2,
610
- TokenDecimals: 6,
609
+ PermitContractVersion: 1,
610
+ TokenDecimals: 18,
611
611
  IsGasToken: false,
612
612
  },
613
613
  {
@@ -617,6 +617,14 @@ const RawData = [
617
617
  TokenDecimals: 18,
618
618
  IsGasToken: true,
619
619
  },
620
+ {
621
+ CurrencyID: 2,
622
+ TokenContractAddress: "0x000000000000000000000000B8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
623
+ PermitVariant: permitutils_1.PermitVariant.EIP2612Canonical,
624
+ PermitContractVersion: 1,
625
+ TokenDecimals: 6,
626
+ IsGasToken: false,
627
+ },
620
628
  ],
621
629
  },
622
630
  //citrea mainnet
@@ -641,7 +649,7 @@ const RawData = [
641
649
  IsGasToken: false,
642
650
  },
643
651
  {
644
- CurrencyID: 3,
652
+ CurrencyID: 21,
645
653
  TokenContractAddress: "0x0000000000000000000000000000000000000000000000000000000000000000",
646
654
  PermitVariant: permitutils_1.PermitVariant.Unsupported,
647
655
  TokenDecimals: 18,
@@ -756,10 +764,7 @@ exports.RPCURLMap = new chainid_1.ChainIDKeyedMap([
756
764
  new chainid_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 50104),
757
765
  "https://sophon.gateway.tenderly.co/1d4STFT7zmG0vM5QowibCw",
758
766
  ],
759
- [
760
- new chainid_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 56),
761
- "https://rpcs.avail.so/bsc",
762
- ],
767
+ [new chainid_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 56), "https://rpcs.avail.so/bsc"],
763
768
  [
764
769
  new chainid_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 10143),
765
770
  "https://rpcs.avail.so/monadtestnet",
@@ -18,6 +18,7 @@ var CurrencyID;
18
18
  CurrencyID[CurrencyID["SOPH"] = 18] = "SOPH";
19
19
  CurrencyID[CurrencyID["TRX"] = 19] = "TRX";
20
20
  CurrencyID[CurrencyID["MON"] = 20] = "MON";
21
+ CurrencyID[CurrencyID["CBTC"] = 21] = "CBTC";
21
22
  })(CurrencyID || (exports.CurrencyID = CurrencyID = {}));
22
23
  class Currency {
23
24
  currencyID;
@@ -69,6 +69,10 @@ const dataSets = new Map([
69
69
  new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 4326),
70
70
  "0x10B69f0E3c21C1187526940A615959E9ee6012F9",
71
71
  ],
72
+ [
73
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 4114),
74
+ "0x10B69f0E3c21C1187526940A615959E9ee6012F9",
75
+ ],
72
76
  [
73
77
  new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 534352),
74
78
  "0x111111eA4f8BdfB5AE22c37ebC3eE17b82072F57",
@@ -81,6 +85,22 @@ const dataSets = new Map([
81
85
  new data_1.OmniversalChainID(definition_1.Universe.TRON, 728126428),
82
86
  "0x46de8c7e6f1da4dd851b62c20b78971f230fca5b",
83
87
  ],
88
+ [
89
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 1),
90
+ "0xAc73E77b4FE9BBAAA35C7147DC3Fd5286929A746",
91
+ ],
92
+ [
93
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 143),
94
+ "0xAc73E77b4FE9BBAAA35C7147DC3Fd5286929A746",
95
+ ],
96
+ [
97
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 999),
98
+ "0xAc73E77b4FE9BBAAA35C7147DC3Fd5286929A746",
99
+ ],
100
+ [
101
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 43114),
102
+ "0xAc73E77b4FE9BBAAA35C7147DC3Fd5286929A746",
103
+ ],
84
104
  ],
85
105
  ],
86
106
  [
@@ -134,6 +154,14 @@ const dataSets = new Map([
134
154
  new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 143),
135
155
  "0xC0DED5d7F424276c821AF21F68E1e663bC671C3D",
136
156
  ],
157
+ [
158
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 4114),
159
+ "0xAc73E77b4FE9BBAAA35C7147DC3Fd5286929A746",
160
+ ],
161
+ [
162
+ new data_1.OmniversalChainID(definition_1.Universe.ETHEREUM, 4326),
163
+ "0x5f02ED27A20BbDbB90EEf98670fA36c36fc02D12",
164
+ ],
137
165
  ],
138
166
  ],
139
167
  [
@@ -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?.outputAmountMinimum ?? 0n);
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 })), (r) => r.quote?.inputAmount ?? 0n);
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.log("XCS | SSV2:", {
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.log("XCS | SS | Skipping because correspondingCurrency is null", {
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.amount).div(decimal_js_1.default.pow(10, correspondingCurrency.decimals));
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.amount,
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.log("XCS | SS | Early return with continuous COTs:", {
187
+ console.debug("XCS | SS | Early return with continuous COTs:", {
187
188
  cots: usedCOTs,
188
189
  });
189
- return { quotes: [], usedCOTs };
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.log("XCS | SS | Responses:", responses);
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.log("XCS | SS | Used COT", {
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: agg } = responses[responseIdx];
243
+ const { quote: resp, aggregator } = responses[responseIdx];
243
244
  if (resp == null) {
244
245
  continue;
245
246
  }
246
- console.log("XCS | SS | 1", {
247
- i: responseIdx,
248
- idx: quoteData.idx,
249
- remainder,
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.div(resp.outputAmountMinimum, divisor);
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.inputAmount, resp.outputAmountMinimum);
258
- const userBal = new decimal_js_1.default(quoteData.originalHolding.amount);
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
- console.log("XCS | SS | 2⒜", {
263
- indicativePrice,
264
- expectedInput,
265
- userBal,
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
- console.log("XCS | SS | 2⒜⑴", {
282
- adequateQuote,
284
+ const quote = adequateQuote.quote;
285
+ console.log("partial_quote", {
286
+ quote,
283
287
  });
284
- const oam2D = decimal_js_1.default.div(adequateQuote.quote.outputAmountMinimum, divisor);
288
+ const oam2D = new decimal_js_1.default(adequateQuote.quote.output.amount);
285
289
  if (oam2D.gte(remainder)) {
286
290
  final.push({
287
- ...quoteData,
288
- quote: adequateQuote.quote,
289
- agg: adequateQuote.aggregator,
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.log("XCS | SS | 2⒝", resp);
308
+ console.debug("full_quote", resp);
304
309
  final.push({
305
- ...quoteData,
306
310
  quote: resp,
307
- agg,
308
- });
309
- remainder = remainder.minus((0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum).div(divisor));
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
- const adequateQuoteResult = await aggregateAggregators([
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.log("XCS | SS | 3⒜", {
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("Failed to accumulate enough swaps to meet requirement");
324
+ throw new AutoSelectionError("NOT_ENOUGH_SWAP_FOR_REQUIREMENT");
459
325
  }
460
- console.log("XCS | SS | Final:", final);
461
- return final;
326
+ console.log("final_quotes", { quotes: final, cots: usedCOTs });
327
+ return { quoteResponses: final, usedCOTs };
462
328
  }
463
- async function determineDestinationSwaps(userAddress, chainID, requirement, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
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.amount,
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.outputAmountLikely).mul(safetyMultiplier);
492
- console.log("XCS | DDS | 1⒜", {
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.log("XCS | DDS | 2⒜ iteration", {
381
+ console.debug("XCS | DDS | 2⒜ iteration", {
517
382
  buyQuote,
518
383
  curAmount,
519
384
  });
520
- if (buyQuote.quote.outputAmountMinimum >= requirement.amount) {
385
+ if (buyQuote.quote.output.amountRaw >= requirement.amountRaw) {
521
386
  return {
522
- ...buyQuote,
523
- inputAmount: (0, data_1.convertBigIntToDecimal)(buyQuote.quote.inputAmount).div(decimal_js_1.default.pow(10, COT.decimals)),
524
- outputAmount: requirement.amount,
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, collectionFees, commonCurrencyID = data_1.CurrencyID.USDC) {
533
- console.log("XCS | LIH | Holdings:", holdings);
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.log("XCS | LIH | Skipping because correspondingCurrency is null", {
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.amount,
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.log("XCS | LIH | Responses:", responses);
581
- const validResponses = responses
582
- .filter((r) => r.quote !== null)
583
- .map((r, i) => ({
584
- ...r,
585
- ...fullLiquidationQuotes[i],
586
- agg: r.aggregator,
587
- quote: r.quote,
588
- }));
589
- const total = validResponses.reduce((acc, r) => {
590
- return acc.add(new decimal_js_1.default(r.quote.outputAmountMinimum ?? 0n).div(decimal_js_1.default.pow(10, r.cur.decimals)));
591
- }, new decimal_js_1.default(0));
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, chainID, inputAmount, outputToken, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
598
- const chaindata = data_1.ChaindataMap.get(chainID);
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 === commonCurrencyID);
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: chainID,
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
- ...fullLiquidationQuote,
626
- inputAmount: (0, data_1.convertBigIntToDecimal)(inputAmount).div(decimal_js_1.default.pow(10, COT.decimals)),
627
- outputAmount: fullLiquidationQuote.quote.outputAmountMinimum,
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
  }