@avail-project/ca-common 2.3.0 → 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/cjs/index.js +10593 -20
- package/dist/esm/index.mjs +10603 -0
- package/dist/types/index.d.ts +2608 -13
- package/package.json +41 -37
- package/dist/cjs/_polyfill.js +0 -147
- package/dist/cjs/balances/ub-api.js +0 -25
- package/dist/cjs/cosmos/index.js +0 -31
- package/dist/cjs/data/chaindata.js +0 -788
- package/dist/cjs/data/chainid.js +0 -93
- package/dist/cjs/data/currency.js +0 -78
- package/dist/cjs/data/index.js +0 -7
- package/dist/cjs/data/utils.js +0 -44
- package/dist/cjs/evmabi/erc20.abi.js +0 -349
- package/dist/cjs/evmabi/fibrousrouter.abi.js +0 -41
- package/dist/cjs/evmabi/index.js +0 -7
- package/dist/cjs/evmabi/vault.abi.js +0 -1021
- package/dist/cjs/evmabi/yakaggregator.abi.js +0 -275
- package/dist/cjs/permitutils/index.js +0 -186
- package/dist/cjs/proto/client.js +0 -48
- package/dist/cjs/proto/definition.js +0 -4131
- package/dist/cjs/proto/grpc.js +0 -298
- package/dist/cjs/rff/rff.js +0 -42
- package/dist/cjs/types/binarytypes.js +0 -2
- package/dist/cjs/types/index.js +0 -5
- package/dist/cjs/types/msgpack-axios.js +0 -22
- package/dist/cjs/vaultcontracts/index.js +0 -4
- package/dist/cjs/vaultcontracts/vaultcontracts.js +0 -222
- package/dist/cjs/xcs/autochoice.js +0 -575
- package/dist/cjs/xcs/bebop-agg.js +0 -156
- package/dist/cjs/xcs/fibrous-agg.js +0 -155
- package/dist/cjs/xcs/iface.js +0 -13
- package/dist/cjs/xcs/index.js +0 -9
- package/dist/cjs/xcs/lifi-agg.js +0 -177
- package/dist/cjs/xcs/selectsources.js +0 -255
- package/dist/esm/_polyfill.js +0 -143
- package/dist/esm/balances/ub-api.js +0 -21
- package/dist/esm/cosmos/index.js +0 -26
- package/dist/esm/data/chaindata.js +0 -785
- package/dist/esm/data/chainid.js +0 -87
- package/dist/esm/data/currency.js +0 -73
- package/dist/esm/data/index.js +0 -4
- package/dist/esm/data/utils.js +0 -34
- package/dist/esm/evmabi/erc20.abi.js +0 -346
- package/dist/esm/evmabi/fibrousrouter.abi.js +0 -38
- package/dist/esm/evmabi/index.js +0 -4
- package/dist/esm/evmabi/vault.abi.js +0 -1018
- package/dist/esm/evmabi/yakaggregator.abi.js +0 -272
- package/dist/esm/index.js +0 -13
- package/dist/esm/permitutils/index.js +0 -181
- package/dist/esm/proto/client.js +0 -11
- package/dist/esm/proto/definition.js +0 -4122
- package/dist/esm/proto/grpc.js +0 -292
- package/dist/esm/rff/rff.js +0 -38
- package/dist/esm/types/binarytypes.js +0 -1
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/types/msgpack-axios.js +0 -18
- package/dist/esm/vaultcontracts/index.js +0 -1
- package/dist/esm/vaultcontracts/vaultcontracts.js +0 -218
- package/dist/esm/xcs/autochoice.js +0 -559
- package/dist/esm/xcs/bebop-agg.js +0 -151
- package/dist/esm/xcs/fibrous-agg.js +0 -150
- package/dist/esm/xcs/iface.js +0 -10
- package/dist/esm/xcs/index.js +0 -6
- package/dist/esm/xcs/lifi-agg.js +0 -172
- package/dist/esm/xcs/selectsources.js +0 -251
- package/dist/types/_polyfill.d.ts +0 -1
- package/dist/types/balances/ub-api.d.ts +0 -14
- package/dist/types/cosmos/index.d.ts +0 -6
- package/dist/types/data/chaindata.d.ts +0 -20
- package/dist/types/data/chainid.d.ts +0 -31
- package/dist/types/data/currency.d.ts +0 -30
- package/dist/types/data/index.d.ts +0 -4
- package/dist/types/data/utils.d.ts +0 -10
- package/dist/types/evmabi/erc20.abi.d.ts +0 -264
- package/dist/types/evmabi/fibrousrouter.abi.d.ts +0 -77
- package/dist/types/evmabi/index.d.ts +0 -4
- package/dist/types/evmabi/vault.abi.d.ts +0 -785
- package/dist/types/evmabi/yakaggregator.abi.d.ts +0 -298
- package/dist/types/permitutils/index.d.ts +0 -13
- package/dist/types/proto/client.d.ts +0 -2
- package/dist/types/proto/definition.d.ts +0 -382
- package/dist/types/proto/grpc.d.ts +0 -75
- package/dist/types/rff/rff.d.ts +0 -9
- package/dist/types/types/binarytypes.d.ts +0 -1
- package/dist/types/types/index.d.ts +0 -2
- package/dist/types/types/msgpack-axios.d.ts +0 -1
- package/dist/types/vaultcontracts/index.d.ts +0 -22
- package/dist/types/vaultcontracts/vaultcontracts.d.ts +0 -8
- package/dist/types/xcs/autochoice.d.ts +0 -104
- package/dist/types/xcs/bebop-agg.d.ts +0 -104
- package/dist/types/xcs/fibrous-agg.d.ts +0 -56
- package/dist/types/xcs/iface.d.ts +0 -69
- package/dist/types/xcs/index.d.ts +0 -6
- package/dist/types/xcs/lifi-agg.d.ts +0 -42
- package/dist/types/xcs/selectsources.d.ts +0 -27
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AggregateAggregatorsMode = exports.AutoSelectionError = void 0;
|
|
4
|
-
exports.aggregateAggregators = aggregateAggregators;
|
|
5
|
-
exports.autoSelectSourcesV2 = autoSelectSourcesV2;
|
|
6
|
-
exports.autoSelectSourcesV2ByRecipient = autoSelectSourcesV2ByRecipient;
|
|
7
|
-
exports.determineDestinationSwaps = determineDestinationSwaps;
|
|
8
|
-
exports.liquidateInputHoldings = liquidateInputHoldings;
|
|
9
|
-
exports.liquidateInputHoldingsByRecipient = liquidateInputHoldingsByRecipient;
|
|
10
|
-
exports.destinationSwapWithExactIn = destinationSwapWithExactIn;
|
|
11
|
-
exports.getDestinationExactOutSwap = getDestinationExactOutSwap;
|
|
12
|
-
exports.getDestinationExactInSwap = getDestinationExactInSwap;
|
|
13
|
-
exports.liquidateSourceHoldings = liquidateSourceHoldings;
|
|
14
|
-
exports.autoSelectSources = autoSelectSources;
|
|
15
|
-
const tslib_1 = require("tslib");
|
|
16
|
-
const es_toolkit_1 = require("es-toolkit");
|
|
17
|
-
const viem_1 = require("viem");
|
|
18
|
-
const decimal_js_1 = tslib_1.__importDefault(require("decimal.js"));
|
|
19
|
-
const iface_1 = require("./iface");
|
|
20
|
-
const data_1 = require("../data");
|
|
21
|
-
class AutoSelectionError extends Error {
|
|
22
|
-
}
|
|
23
|
-
exports.AutoSelectionError = AutoSelectionError;
|
|
24
|
-
const safetyMultiplier = new decimal_js_1.default("1.025");
|
|
25
|
-
var AggregateAggregatorsMode;
|
|
26
|
-
(function (AggregateAggregatorsMode) {
|
|
27
|
-
AggregateAggregatorsMode[AggregateAggregatorsMode["MaximizeOutput"] = 0] = "MaximizeOutput";
|
|
28
|
-
AggregateAggregatorsMode[AggregateAggregatorsMode["MinimizeInput"] = 1] = "MinimizeInput";
|
|
29
|
-
})(AggregateAggregatorsMode || (exports.AggregateAggregatorsMode = AggregateAggregatorsMode = {}));
|
|
30
|
-
async function aggregateAggregators(requests, aggregators, mode) {
|
|
31
|
-
const responses = await Promise.all(aggregators.map(async (agg) => {
|
|
32
|
-
let quotes;
|
|
33
|
-
try {
|
|
34
|
-
quotes = await agg.getQuotes(requests);
|
|
35
|
-
}
|
|
36
|
-
catch (e) {
|
|
37
|
-
console.log("XCS | Failed to get quote from", agg, "in aggregateAggregators.", requests, "with:", e);
|
|
38
|
-
quotes = new Array(requests.length).fill(null);
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
quotes,
|
|
42
|
-
agg,
|
|
43
|
-
};
|
|
44
|
-
}));
|
|
45
|
-
const final = new Array(requests.length);
|
|
46
|
-
switch (mode) {
|
|
47
|
-
case AggregateAggregatorsMode.MaximizeOutput: {
|
|
48
|
-
for (let i = 0; i < requests.length; i++) {
|
|
49
|
-
const best = (0, data_1.maxByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })), (r) => r.quote?.output.amountRaw ?? 0n);
|
|
50
|
-
if (best != null) {
|
|
51
|
-
final[i] = best;
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
final[i] = {
|
|
55
|
-
quote: null,
|
|
56
|
-
aggregator: aggregators[0],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
case AggregateAggregatorsMode.MinimizeInput: {
|
|
63
|
-
for (let i = 0; i < requests.length; i++) {
|
|
64
|
-
const best = (0, data_1.minByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })),
|
|
65
|
-
// Default null quotes to MAX so they never win as the minimum
|
|
66
|
-
(r) => r.quote?.input.amountRaw ?? BigInt(Number.MAX_SAFE_INTEGER));
|
|
67
|
-
if (best != null) {
|
|
68
|
-
final[i] = best;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
final[i] = {
|
|
72
|
-
quote: null,
|
|
73
|
-
aggregator: aggregators[0],
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return final;
|
|
81
|
-
}
|
|
82
|
-
/*
|
|
83
|
-
In original autoSelectSources:
|
|
84
|
-
Assets = [1 ETH, 1 COT, 1 ETH, 1 USDT, 1 COT]
|
|
85
|
-
Output = 4
|
|
86
|
-
|
|
87
|
-
First loop that just removes cot:
|
|
88
|
-
quoteAssets = [1 ETH, 1 ETH, 1 USDT]
|
|
89
|
-
Output = 4
|
|
90
|
-
|
|
91
|
-
Outside the function we can just remove all COT's and assume those as being used
|
|
92
|
-
but that's incorrect as we want to use assets in exact order as holdings array.
|
|
93
|
-
We can't remove only COT's that are going to be used because we don't know which ones are going
|
|
94
|
-
to get used.
|
|
95
|
-
|
|
96
|
-
Proposed solution:
|
|
97
|
-
It should actually use assets in exact order, so it cant be done outside the function
|
|
98
|
-
1. The function has to keep an order of assets, separate out COT and non-COT assets.
|
|
99
|
-
2. Get quote for non-COT assets, then loop over the original order
|
|
100
|
-
3. Used either COT or quote depending on original order
|
|
101
|
-
4. Send back quotes and COT's used
|
|
102
|
-
|
|
103
|
-
Alg:
|
|
104
|
-
Assets = [1 ETH, 1 COT, 1 ETH, 1 USDT, 1 COT]
|
|
105
|
-
Output = 4
|
|
106
|
-
|
|
107
|
-
1. separate into two with indexes:
|
|
108
|
-
quotes = [(1 ETH, 0), (1 ETH, 2), (1 USDT, 3)]
|
|
109
|
-
cots = [(1 COT, 1), (1 COT, 4)]
|
|
110
|
-
|
|
111
|
-
2. Get quotes using only quotes
|
|
112
|
-
3. merge quote cots and sort by order
|
|
113
|
-
4. loop assets (original order):
|
|
114
|
-
if a quote:
|
|
115
|
-
output = output - quote_output_amount
|
|
116
|
-
if a cot:
|
|
117
|
-
output = output - cot_amount
|
|
118
|
-
if output <= 0:
|
|
119
|
-
break
|
|
120
|
-
5. return quotes and assets used.
|
|
121
|
-
*/
|
|
122
|
-
/**
|
|
123
|
-
* @deprecated Use {@link autoSelectSources} (object args; per-holding `takerAddress` and
|
|
124
|
-
* `receiverAddress` on each `HoldingWithSwapAddresses`).
|
|
125
|
-
*/
|
|
126
|
-
async function autoSelectSourcesV2(userAddress, holdings, outputRequired, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
127
|
-
return autoSelectSourcesV2ByRecipient(holdings.map((holding) => ({ ...holding, recipient: userAddress })), outputRequired, aggregators, commonCurrencyID);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* @deprecated Use {@link autoSelectSources} (object args; per-holding `takerAddress` and
|
|
131
|
-
* `receiverAddress` on each `HoldingWithSwapAddresses`).
|
|
132
|
-
*/
|
|
133
|
-
async function autoSelectSourcesV2ByRecipient(holdings, outputRequired, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
134
|
-
// Assumption: Holding is already sorted in usage priority
|
|
135
|
-
console.debug("XCS | SSV2:", {
|
|
136
|
-
holdings,
|
|
137
|
-
outputRequired: outputRequired.toFixed(),
|
|
138
|
-
});
|
|
139
|
-
const fullLiquidationQuotes = [];
|
|
140
|
-
const cotList = [];
|
|
141
|
-
for (const [idx, holding] of holdings.entries()) {
|
|
142
|
-
const chain = data_1.ChaindataMap.get(holding.chainID);
|
|
143
|
-
if (chain == null) {
|
|
144
|
-
throw new AutoSelectionError("Chain not found");
|
|
145
|
-
}
|
|
146
|
-
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
147
|
-
if (correspondingCurrency == null) {
|
|
148
|
-
console.debug("XCS | SS | Skipping because correspondingCurrency is null", {
|
|
149
|
-
chain,
|
|
150
|
-
correspondingCurrency,
|
|
151
|
-
});
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
155
|
-
const normalizedAmount = new decimal_js_1.default(holding.amountRaw).div(decimal_js_1.default.pow(10, correspondingCurrency.decimals));
|
|
156
|
-
cotList.push({
|
|
157
|
-
amount: normalizedAmount,
|
|
158
|
-
idx,
|
|
159
|
-
chainID: holding.chainID,
|
|
160
|
-
currency: correspondingCurrency,
|
|
161
|
-
originalHolding: holding,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
fullLiquidationQuotes.push({
|
|
166
|
-
req: {
|
|
167
|
-
userAddress: holding.recipient,
|
|
168
|
-
// New wrappers thread per-holding receiver via this field. Falls back to taker
|
|
169
|
-
// (`recipient`) when absent — preserves legacy positional-call behavior.
|
|
170
|
-
receiverAddress: holding.receiverAddress,
|
|
171
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
172
|
-
chain: chain.ChainID,
|
|
173
|
-
inputToken: holding.tokenAddress,
|
|
174
|
-
inputAmount: holding.amountRaw,
|
|
175
|
-
outputToken: correspondingCurrency.tokenAddress,
|
|
176
|
-
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
177
|
-
},
|
|
178
|
-
originalHolding: holding,
|
|
179
|
-
cur: correspondingCurrency,
|
|
180
|
-
idx,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Check if continuous COTs from the start can cover the entire requirement
|
|
185
|
-
// We can skip quoting unused holdings
|
|
186
|
-
if (cotList.length > 0 && cotList[0].idx === 0) {
|
|
187
|
-
let continuousCOTAmount = new decimal_js_1.default(0);
|
|
188
|
-
let continuousCount = 0;
|
|
189
|
-
for (const cot of cotList) {
|
|
190
|
-
// only consecutive cots allowed, otherwise we need to go to quoting
|
|
191
|
-
if (cot.idx !== continuousCount)
|
|
192
|
-
break;
|
|
193
|
-
continuousCOTAmount = continuousCOTAmount.add(cot.amount);
|
|
194
|
-
continuousCount++;
|
|
195
|
-
if (continuousCOTAmount.gte(outputRequired)) {
|
|
196
|
-
console.log("XCS | SS | Continuous COTs can satisfy requirement, skipping quotes");
|
|
197
|
-
const usedCOTs = [];
|
|
198
|
-
let remainder = outputRequired;
|
|
199
|
-
for (let i = 0; i < continuousCount; i++) {
|
|
200
|
-
const cot = cotList[i];
|
|
201
|
-
const amountToUse = decimal_js_1.default.min(remainder, cot.amount);
|
|
202
|
-
usedCOTs.push({
|
|
203
|
-
originalHolding: cot.originalHolding,
|
|
204
|
-
amountUsed: amountToUse,
|
|
205
|
-
idx: cot.idx,
|
|
206
|
-
cur: cot.currency,
|
|
207
|
-
});
|
|
208
|
-
remainder = remainder.minus(amountToUse);
|
|
209
|
-
if (remainder.lte(0))
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
console.debug("XCS | SS | Early return with continuous COTs:", {
|
|
213
|
-
cots: usedCOTs,
|
|
214
|
-
});
|
|
215
|
-
return { quoteResponses: [], usedCOTs };
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const processingQueue = [];
|
|
220
|
-
// Add COT holdings
|
|
221
|
-
for (const cot of cotList) {
|
|
222
|
-
processingQueue.push({
|
|
223
|
-
idx: cot.idx,
|
|
224
|
-
isCOT: true,
|
|
225
|
-
cotData: cot,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
// Add non-COT holdings
|
|
229
|
-
for (let i = 0; i < fullLiquidationQuotes.length; i++) {
|
|
230
|
-
processingQueue.push({
|
|
231
|
-
idx: fullLiquidationQuotes[i].idx,
|
|
232
|
-
isCOT: false,
|
|
233
|
-
quoteData: fullLiquidationQuotes[i],
|
|
234
|
-
responseIdx: i,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
// Sort by original index to maintain priority
|
|
238
|
-
processingQueue.sort((a, b) => a.idx - b.idx);
|
|
239
|
-
const responses = await aggregateAggregators(fullLiquidationQuotes.map((fq) => fq.req), aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
240
|
-
console.debug("AutoSelectSources:Quotes", responses);
|
|
241
|
-
const final = [];
|
|
242
|
-
const usedCOTs = [];
|
|
243
|
-
let remainder = outputRequired;
|
|
244
|
-
for (const item of processingQueue) {
|
|
245
|
-
if (remainder.lte(0)) {
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
if (item.isCOT) {
|
|
249
|
-
// Process COT holding - direct usage, no quote
|
|
250
|
-
const { cotData } = item;
|
|
251
|
-
const amountToUse = decimal_js_1.default.min(remainder, cotData.amount);
|
|
252
|
-
usedCOTs.push({
|
|
253
|
-
originalHolding: cotData.originalHolding,
|
|
254
|
-
amountUsed: amountToUse,
|
|
255
|
-
idx: cotData.idx,
|
|
256
|
-
cur: cotData.currency,
|
|
257
|
-
});
|
|
258
|
-
remainder = remainder.minus(amountToUse);
|
|
259
|
-
console.debug("selection:cot", {
|
|
260
|
-
idx: cotData.idx,
|
|
261
|
-
amountToUse: amountToUse.toFixed(),
|
|
262
|
-
remainder: remainder.toFixed(),
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
// Process non-COT holding - use existing quote logic
|
|
267
|
-
const { quoteData, responseIdx } = item;
|
|
268
|
-
const { quote: resp, aggregator } = responses[responseIdx];
|
|
269
|
-
if (resp == null) {
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
console.debug("selection:quote", {
|
|
273
|
-
remainder: remainder.toFixed(),
|
|
274
|
-
input: resp.input,
|
|
275
|
-
output: resp.output,
|
|
276
|
-
});
|
|
277
|
-
const divisor = decimal_js_1.default.pow(10, quoteData.cur.decimals);
|
|
278
|
-
const oamD = new decimal_js_1.default(resp.output.amount);
|
|
279
|
-
if (oamD.gt(remainder)) {
|
|
280
|
-
const indicativePrice = decimal_js_1.default.div(resp.input.amountRaw, resp.output.amountRaw);
|
|
281
|
-
const userBal = new decimal_js_1.default(quoteData.originalHolding.amountRaw);
|
|
282
|
-
// remainder is the output we want, so the input amount is remainder × indicativePrice
|
|
283
|
-
let expectedInput = decimal_js_1.default.min(remainder.mul(divisor).mul(indicativePrice).mul(safetyMultiplier), userBal);
|
|
284
|
-
let attempts = 0;
|
|
285
|
-
while (true) {
|
|
286
|
-
if (++attempts > 10) {
|
|
287
|
-
throw new AutoSelectionError("Partial quote did not converge");
|
|
288
|
-
}
|
|
289
|
-
console.debug("partial_quote_loop", {
|
|
290
|
-
indicativePrice: indicativePrice.toFixed(),
|
|
291
|
-
expectedInput: expectedInput.toFixed(),
|
|
292
|
-
userBal: userBal.toFixed(),
|
|
293
|
-
remainder: remainder.toFixed(),
|
|
294
|
-
});
|
|
295
|
-
const adequateQuoteResult = await aggregateAggregators([
|
|
296
|
-
{
|
|
297
|
-
...quoteData.req,
|
|
298
|
-
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
299
|
-
inputAmount: (0, data_1.convertDecimalToBigInt)(expectedInput),
|
|
300
|
-
},
|
|
301
|
-
], aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
302
|
-
if (adequateQuoteResult.length !== 1) {
|
|
303
|
-
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
304
|
-
}
|
|
305
|
-
const adequateQuote = adequateQuoteResult[0];
|
|
306
|
-
if (adequateQuote.quote == null) {
|
|
307
|
-
throw new AutoSelectionError("Couldn't get buy quote");
|
|
308
|
-
}
|
|
309
|
-
const quote = adequateQuote.quote;
|
|
310
|
-
console.log("partial_quote", {
|
|
311
|
-
quote,
|
|
312
|
-
});
|
|
313
|
-
const oam2D = new decimal_js_1.default(adequateQuote.quote.output.amount);
|
|
314
|
-
if (oam2D.gte(remainder)) {
|
|
315
|
-
final.push({
|
|
316
|
-
quote,
|
|
317
|
-
aggregator: adequateQuote.aggregator,
|
|
318
|
-
holding: quoteData.originalHolding,
|
|
319
|
-
chainID: Number(quoteData.req.chain.chainID),
|
|
320
|
-
});
|
|
321
|
-
remainder = remainder.minus(oam2D);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
else if (expectedInput.eq(userBal)) {
|
|
325
|
-
throw new AutoSelectionError("Holding was supposedly enough to meet the full requirement but ceased to be so subsequently");
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
expectedInput = decimal_js_1.default.min(expectedInput.mul(safetyMultiplier), userBal); // try again with higher amount
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
console.debug("full_quote", resp);
|
|
334
|
-
final.push({
|
|
335
|
-
quote: resp,
|
|
336
|
-
holding: quoteData.originalHolding,
|
|
337
|
-
aggregator,
|
|
338
|
-
chainID: Number(quoteData.req.chain.chainID),
|
|
339
|
-
});
|
|
340
|
-
remainder = remainder.minus(resp.output.amount);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
console.debug("quotes_and_remainder", {
|
|
345
|
-
remainder: remainder.toFixed(),
|
|
346
|
-
final,
|
|
347
|
-
});
|
|
348
|
-
if (remainder.gt(0)) {
|
|
349
|
-
throw new AutoSelectionError("NOT_ENOUGH_SWAP_FOR_REQUIREMENT");
|
|
350
|
-
}
|
|
351
|
-
console.log("final_quotes", { quotes: final, cots: usedCOTs });
|
|
352
|
-
return { quoteResponses: final, usedCOTs };
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* @deprecated Use {@link getDestinationExactOutSwap} (object args; explicit `takerAddress` and
|
|
356
|
-
* `receiverAddress`, both required).
|
|
357
|
-
*/
|
|
358
|
-
async function determineDestinationSwaps(userAddress, requirement, aggregators, commonCurrencyID = data_1.CurrencyID.USDC, receiverAddress) {
|
|
359
|
-
const chaindata = data_1.ChaindataMap.get(requirement.chainID);
|
|
360
|
-
if (chaindata == null) {
|
|
361
|
-
throw new AutoSelectionError("Chain not found");
|
|
362
|
-
}
|
|
363
|
-
const COT = chaindata.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
364
|
-
if (COT == null) {
|
|
365
|
-
throw new AutoSelectionError("COT not present on the destination chain");
|
|
366
|
-
}
|
|
367
|
-
// FIXME: Replace with oracle usage - should reduce time.
|
|
368
|
-
// what happens if we happen to sell the requirement for the COT, what would the amount be?
|
|
369
|
-
const fullLiquidationQR = {
|
|
370
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
371
|
-
chain: requirement.chainID,
|
|
372
|
-
userAddress,
|
|
373
|
-
receiverAddress,
|
|
374
|
-
inputToken: requirement.tokenAddress,
|
|
375
|
-
outputToken: COT.tokenAddress,
|
|
376
|
-
inputAmount: requirement.amountRaw,
|
|
377
|
-
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
378
|
-
};
|
|
379
|
-
const fullLiquidationResult = await aggregateAggregators([fullLiquidationQR], aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
380
|
-
if (fullLiquidationResult.length !== 1) {
|
|
381
|
-
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
382
|
-
}
|
|
383
|
-
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
384
|
-
if (fullLiquidationQuote.quote == null) {
|
|
385
|
-
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
386
|
-
}
|
|
387
|
-
let curAmount = (0, data_1.convertBigIntToDecimal)(fullLiquidationQuote.quote.output.amountRaw).mul(safetyMultiplier);
|
|
388
|
-
let attempts = 0;
|
|
389
|
-
while (true) {
|
|
390
|
-
if (++attempts > 10) {
|
|
391
|
-
throw new AutoSelectionError("Destination swap quote did not converge");
|
|
392
|
-
}
|
|
393
|
-
const buyQuoteResult = await aggregateAggregators([
|
|
394
|
-
{
|
|
395
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
396
|
-
userAddress,
|
|
397
|
-
receiverAddress,
|
|
398
|
-
chain: requirement.chainID,
|
|
399
|
-
inputToken: COT.tokenAddress,
|
|
400
|
-
outputToken: requirement.tokenAddress,
|
|
401
|
-
inputAmount: (0, data_1.convertDecimalToBigInt)(curAmount),
|
|
402
|
-
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
403
|
-
},
|
|
404
|
-
], aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
405
|
-
if (buyQuoteResult.length !== 1) {
|
|
406
|
-
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
407
|
-
}
|
|
408
|
-
const buyQuote = buyQuoteResult[0];
|
|
409
|
-
if (buyQuote.quote == null) {
|
|
410
|
-
throw new AutoSelectionError("Couldn't get buy quote");
|
|
411
|
-
}
|
|
412
|
-
console.debug("XCS | DDS | 2⒜ iteration", {
|
|
413
|
-
buyQuote,
|
|
414
|
-
curAmount,
|
|
415
|
-
});
|
|
416
|
-
if (buyQuote.quote.output.amountRaw >= requirement.amountRaw) {
|
|
417
|
-
return {
|
|
418
|
-
chainID: Number(requirement.chainID.chainID),
|
|
419
|
-
quote: buyQuote.quote,
|
|
420
|
-
aggregator: buyQuote.aggregator,
|
|
421
|
-
holding: requirement,
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
curAmount = curAmount.mul(safetyMultiplier); // try again with higher amount
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* @deprecated Use {@link liquidateSourceHoldings} (object args; per-holding `takerAddress`
|
|
431
|
-
* and `receiverAddress` on each `HoldingWithSwapAddresses`).
|
|
432
|
-
*/
|
|
433
|
-
async function liquidateInputHoldings(userAddress, holdings, aggregators, commonCurrencyID = data_1.CurrencyID.USDC, receiverAddress) {
|
|
434
|
-
return liquidateInputHoldingsByRecipient(holdings.map((holding) => ({ ...holding, recipient: userAddress })), aggregators, commonCurrencyID, receiverAddress);
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* @deprecated Use {@link liquidateSourceHoldings} (object args; per-holding `takerAddress`
|
|
438
|
-
* and `receiverAddress` on each `HoldingWithSwapAddresses`).
|
|
439
|
-
*/
|
|
440
|
-
async function liquidateInputHoldingsByRecipient(holdings, aggregators, commonCurrencyID = data_1.CurrencyID.USDC, receiverAddress) {
|
|
441
|
-
console.debug("XCS | LIH | Holdings:", holdings);
|
|
442
|
-
const groupedByChainID = (0, es_toolkit_1.groupBy)(holdings, (h) => (0, viem_1.bytesToHex)(h.chainID.toBytes()));
|
|
443
|
-
const fullLiquidationQuotes = [];
|
|
444
|
-
for (const holdings of Object.values(groupedByChainID)) {
|
|
445
|
-
const chain = data_1.ChaindataMap.get(holdings[0].chainID);
|
|
446
|
-
if (chain == null) {
|
|
447
|
-
throw new AutoSelectionError("Chain not found");
|
|
448
|
-
}
|
|
449
|
-
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
450
|
-
if (correspondingCurrency == null) {
|
|
451
|
-
console.debug("XCS | LIH | Skipping because correspondingCurrency is null", {
|
|
452
|
-
chain,
|
|
453
|
-
correspondingCurrency,
|
|
454
|
-
});
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
for (const holding of holdings) {
|
|
458
|
-
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
459
|
-
console.log("XCS | LIH | Disqualifying", holding, "because holding.tokenAddress = CA asset");
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
fullLiquidationQuotes.push({
|
|
463
|
-
req: {
|
|
464
|
-
userAddress: holding.recipient,
|
|
465
|
-
// Per-holding receiver wins (set by the new wrappers); shared param is the legacy
|
|
466
|
-
// positional-call fallback.
|
|
467
|
-
receiverAddress: holding.receiverAddress ?? receiverAddress,
|
|
468
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
469
|
-
chain: chain.ChainID,
|
|
470
|
-
inputToken: holding.tokenAddress,
|
|
471
|
-
inputAmount: holding.amountRaw,
|
|
472
|
-
outputToken: correspondingCurrency.tokenAddress,
|
|
473
|
-
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
474
|
-
},
|
|
475
|
-
// necessary for various purposes
|
|
476
|
-
originalHolding: holding,
|
|
477
|
-
cur: correspondingCurrency,
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const responses = await aggregateAggregators(fullLiquidationQuotes.map((fq) => fq.req), aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
482
|
-
console.debug("XCS | LIH | Responses:", responses);
|
|
483
|
-
const quotes = [];
|
|
484
|
-
for (const [i, response] of responses.entries()) {
|
|
485
|
-
if (response.quote !== null) {
|
|
486
|
-
quotes.push({
|
|
487
|
-
quote: response.quote,
|
|
488
|
-
aggregator: response.aggregator,
|
|
489
|
-
holding: fullLiquidationQuotes[i].originalHolding,
|
|
490
|
-
chainID: Number(fullLiquidationQuotes[i].req.chain.chainID),
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return quotes;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* @deprecated Use {@link getDestinationExactInSwap} (object args; explicit `takerAddress`
|
|
498
|
-
* and `receiverAddress`, both required).
|
|
499
|
-
*/
|
|
500
|
-
async function destinationSwapWithExactIn(userAddress, omniChainID, inputAmount, outputToken, aggregators, inputCurrency = data_1.CurrencyID.USDC, receiverAddress) {
|
|
501
|
-
const chaindata = data_1.ChaindataMap.get(omniChainID);
|
|
502
|
-
if (chaindata == null) {
|
|
503
|
-
throw new AutoSelectionError("Chain not found");
|
|
504
|
-
}
|
|
505
|
-
const COT = chaindata.Currencies.find((cur) => cur.currencyID === inputCurrency);
|
|
506
|
-
if (COT == null) {
|
|
507
|
-
throw new AutoSelectionError("COT not present on the destination chain");
|
|
508
|
-
}
|
|
509
|
-
const fullLiquidationResult = await aggregateAggregators([
|
|
510
|
-
{
|
|
511
|
-
type: iface_1.QuoteType.EXACT_IN,
|
|
512
|
-
chain: omniChainID,
|
|
513
|
-
userAddress,
|
|
514
|
-
receiverAddress,
|
|
515
|
-
inputToken: COT.tokenAddress,
|
|
516
|
-
outputToken: outputToken,
|
|
517
|
-
inputAmount: inputAmount,
|
|
518
|
-
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
519
|
-
},
|
|
520
|
-
], aggregators, AggregateAggregatorsMode.MaximizeOutput);
|
|
521
|
-
if (fullLiquidationResult.length !== 1) {
|
|
522
|
-
throw new AutoSelectionError("Unexpected response length from aggregateAggregators");
|
|
523
|
-
}
|
|
524
|
-
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
525
|
-
if (fullLiquidationQuote.quote == null) {
|
|
526
|
-
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
527
|
-
}
|
|
528
|
-
return {
|
|
529
|
-
chainID: Number(omniChainID.chainID),
|
|
530
|
-
quote: fullLiquidationQuote.quote,
|
|
531
|
-
aggregator: fullLiquidationQuote.aggregator,
|
|
532
|
-
holding: {
|
|
533
|
-
amountRaw: inputAmount,
|
|
534
|
-
chainID: omniChainID,
|
|
535
|
-
tokenAddress: COT.tokenAddress,
|
|
536
|
-
},
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
// =====================================================================================
|
|
540
|
-
// Object-arg wrappers around the legacy positional functions above.
|
|
541
|
-
//
|
|
542
|
-
// Aggregator vocabulary:
|
|
543
|
-
// takerAddress — on-chain executor of the swap (drives aggregator simulation /
|
|
544
|
-
// permit / approval routing). On 7702 chains this is the ephemeral; on
|
|
545
|
-
// non-Pectra chains it's the deployed Safe. Maps to the underlying
|
|
546
|
-
// QuoteRequest's `userAddress`.
|
|
547
|
-
// receiverAddress — recipient of the swap output. Maps to the underlying QuoteRequest's
|
|
548
|
-
// `receiverAddress`. Required on all 4 wrappers — the GS013-class bug we
|
|
549
|
-
// fixed came from forgetting this and silently defaulting to the wrong
|
|
550
|
-
// address. Even on source side (where it equals the taker today), require
|
|
551
|
-
// it explicitly so the type system forces every call site to acknowledge
|
|
552
|
-
// both roles.
|
|
553
|
-
//
|
|
554
|
-
// Wrap-only: each wrapper delegates to the deprecated positional fn. No business logic added.
|
|
555
|
-
// =====================================================================================
|
|
556
|
-
async function getDestinationExactOutSwap(args) {
|
|
557
|
-
return determineDestinationSwaps(args.takerAddress, args.requirement, args.aggregators, args.commonCurrencyID, args.receiverAddress);
|
|
558
|
-
}
|
|
559
|
-
async function getDestinationExactInSwap(args) {
|
|
560
|
-
return destinationSwapWithExactIn(args.takerAddress, args.chain, args.inputAmount, args.outputToken, args.aggregators, args.inputCurrency, args.receiverAddress);
|
|
561
|
-
}
|
|
562
|
-
async function liquidateSourceHoldings(args) {
|
|
563
|
-
return liquidateInputHoldingsByRecipient(args.holdings.map((h) => ({
|
|
564
|
-
...h,
|
|
565
|
-
recipient: h.takerAddress,
|
|
566
|
-
receiverAddress: h.receiverAddress,
|
|
567
|
-
})), args.aggregators, args.commonCurrencyID);
|
|
568
|
-
}
|
|
569
|
-
async function autoSelectSources(args) {
|
|
570
|
-
return autoSelectSourcesV2ByRecipient(args.holdings.map((h) => ({
|
|
571
|
-
...h,
|
|
572
|
-
recipient: h.takerAddress,
|
|
573
|
-
receiverAddress: h.receiverAddress,
|
|
574
|
-
})), args.outputRequired, args.aggregators, args.commonCurrencyID);
|
|
575
|
-
}
|