@avail-project/ca-common 1.0.0-beta.1
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/balances/ub-api.js +25 -0
- package/dist/cjs/cosmos/index.js +31 -0
- package/dist/cjs/data/chaindata.js +680 -0
- package/dist/cjs/data/chainid.js +91 -0
- package/dist/cjs/data/currency.js +77 -0
- package/dist/cjs/data/index.js +7 -0
- package/dist/cjs/data/utils.js +44 -0
- package/dist/cjs/evmabi/erc20.abi.js +349 -0
- package/dist/cjs/evmabi/index.js +6 -0
- package/dist/cjs/evmabi/vault.abi.js +1021 -0
- package/dist/cjs/evmabi/yakaggregator.abi.js +275 -0
- package/dist/cjs/fuelcontracts/ArcanaVault.js +2407 -0
- package/dist/cjs/fuelcontracts/ArcanaVaultFactory.js +18 -0
- package/dist/cjs/fuelcontracts/common.js +3 -0
- package/dist/cjs/fuelcontracts/index.js +5 -0
- package/dist/cjs/index.js +21 -0
- package/dist/cjs/permitutils/index.js +186 -0
- package/dist/cjs/proto/definition.js +4263 -0
- package/dist/cjs/proto/grpc.js +303 -0
- package/dist/cjs/rff/fuel.js +27 -0
- package/dist/cjs/rff/rff.js +77 -0
- package/dist/cjs/types/binarytypes.js +2 -0
- package/dist/cjs/types/index.js +5 -0
- package/dist/cjs/types/msgpack-axios.js +22 -0
- package/dist/cjs/vaultcontracts/index.js +4 -0
- package/dist/cjs/vaultcontracts/vaultcontracts.js +145 -0
- package/dist/cjs/xcs/autochoice.js +374 -0
- package/dist/cjs/xcs/bebop-agg.js +128 -0
- package/dist/cjs/xcs/iface.js +13 -0
- package/dist/cjs/xcs/index.js +8 -0
- package/dist/cjs/xcs/lifi-agg.js +104 -0
- package/dist/cjs/xcs/yieldyak-agg.js +113 -0
- package/dist/esm/balances/ub-api.js +21 -0
- package/dist/esm/cosmos/index.js +26 -0
- package/dist/esm/data/chaindata.js +677 -0
- package/dist/esm/data/chainid.js +85 -0
- package/dist/esm/data/currency.js +72 -0
- package/dist/esm/data/index.js +4 -0
- package/dist/esm/data/utils.js +34 -0
- package/dist/esm/evmabi/erc20.abi.js +346 -0
- package/dist/esm/evmabi/index.js +3 -0
- package/dist/esm/evmabi/vault.abi.js +1018 -0
- package/dist/esm/evmabi/yakaggregator.abi.js +272 -0
- package/dist/esm/fuelcontracts/ArcanaVault.js +2402 -0
- package/dist/esm/fuelcontracts/ArcanaVaultFactory.js +14 -0
- package/dist/esm/fuelcontracts/common.js +2 -0
- package/dist/esm/fuelcontracts/index.js +2 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/permitutils/index.js +181 -0
- package/dist/esm/proto/definition.js +4255 -0
- package/dist/esm/proto/grpc.js +297 -0
- package/dist/esm/rff/fuel.js +23 -0
- package/dist/esm/rff/rff.js +73 -0
- package/dist/esm/types/binarytypes.js +1 -0
- package/dist/esm/types/index.js +2 -0
- package/dist/esm/types/msgpack-axios.js +18 -0
- package/dist/esm/vaultcontracts/index.js +1 -0
- package/dist/esm/vaultcontracts/vaultcontracts.js +141 -0
- package/dist/esm/xcs/autochoice.js +364 -0
- package/dist/esm/xcs/bebop-agg.js +123 -0
- package/dist/esm/xcs/iface.js +10 -0
- package/dist/esm/xcs/index.js +5 -0
- package/dist/esm/xcs/lifi-agg.js +99 -0
- package/dist/esm/xcs/yieldyak-agg.js +109 -0
- package/dist/types/balances/ub-api.d.ts +14 -0
- package/dist/types/cosmos/index.d.ts +6 -0
- package/dist/types/data/chaindata.d.ts +20 -0
- package/dist/types/data/chainid.d.ts +31 -0
- package/dist/types/data/currency.d.ts +29 -0
- package/dist/types/data/index.d.ts +4 -0
- package/dist/types/data/utils.d.ts +10 -0
- package/dist/types/evmabi/erc20.abi.d.ts +264 -0
- package/dist/types/evmabi/index.d.ts +3 -0
- package/dist/types/evmabi/vault.abi.d.ts +785 -0
- package/dist/types/evmabi/yakaggregator.abi.d.ts +298 -0
- package/dist/types/fuelcontracts/ArcanaVault.d.ts +448 -0
- package/dist/types/fuelcontracts/ArcanaVaultFactory.d.ts +8 -0
- package/dist/types/fuelcontracts/common.d.ts +23 -0
- package/dist/types/fuelcontracts/index.d.ts +2 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/permitutils/index.d.ts +13 -0
- package/dist/types/proto/definition.d.ts +373 -0
- package/dist/types/proto/grpc.d.ts +75 -0
- package/dist/types/rff/fuel.d.ts +4 -0
- package/dist/types/rff/rff.d.ts +12 -0
- package/dist/types/types/binarytypes.d.ts +1 -0
- package/dist/types/types/index.d.ts +2 -0
- package/dist/types/types/msgpack-axios.d.ts +1 -0
- package/dist/types/vaultcontracts/index.d.ts +22 -0
- package/dist/types/vaultcontracts/vaultcontracts.d.ts +8 -0
- package/dist/types/xcs/autochoice.d.ts +57 -0
- package/dist/types/xcs/bebop-agg.d.ts +104 -0
- package/dist/types/xcs/iface.d.ts +36 -0
- package/dist/types/xcs/index.d.ts +5 -0
- package/dist/types/xcs/lifi-agg.d.ts +35 -0
- package/dist/types/xcs/yieldyak-agg.d.ts +21 -0
- package/package.json +63 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoSelectionError = void 0;
|
|
4
|
+
exports.aggregateAggregators = aggregateAggregators;
|
|
5
|
+
exports.autoSelectSources = autoSelectSources;
|
|
6
|
+
exports.determineDestinationSwaps = determineDestinationSwaps;
|
|
7
|
+
exports.liquidateInputHoldings = liquidateInputHoldings;
|
|
8
|
+
exports.destinationSwapWithExactIn = destinationSwapWithExactIn;
|
|
9
|
+
const tslib_1 = require("tslib");
|
|
10
|
+
const es_toolkit_1 = require("es-toolkit");
|
|
11
|
+
const viem_1 = require("viem");
|
|
12
|
+
const decimal_js_1 = tslib_1.__importDefault(require("decimal.js"));
|
|
13
|
+
const iface_1 = require("./iface");
|
|
14
|
+
const data_1 = require("../data");
|
|
15
|
+
class AutoSelectionError extends Error {
|
|
16
|
+
}
|
|
17
|
+
exports.AutoSelectionError = AutoSelectionError;
|
|
18
|
+
const safetyMultiplier = new decimal_js_1.default("1.025");
|
|
19
|
+
async function aggregateAggregators(requests, aggregators, mode) {
|
|
20
|
+
const responses = await Promise.all(aggregators.map(async (agg) => {
|
|
21
|
+
let quotes;
|
|
22
|
+
try {
|
|
23
|
+
quotes = await agg.getQuotes(requests);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
console.log("XCS | Failed to get quote from", agg, "in aggregateAggregators.", requests, "with:", e);
|
|
27
|
+
quotes = new Array(requests.length).fill(null);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
quotes,
|
|
31
|
+
agg,
|
|
32
|
+
};
|
|
33
|
+
}));
|
|
34
|
+
const final = new Array(requests.length);
|
|
35
|
+
switch (mode) {
|
|
36
|
+
case 0 /* AggregateAggregatorsMode.MaximizeOutput */: {
|
|
37
|
+
for (let i = 0; i < requests.length; i++) {
|
|
38
|
+
const best = (0, data_1.maxByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })), (r) => r.quote?.outputAmountMinimum ?? 0n);
|
|
39
|
+
if (best != null) {
|
|
40
|
+
final[i] = best;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
final[i] = {
|
|
44
|
+
quote: null,
|
|
45
|
+
aggregator: aggregators[0],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case 1 /* AggregateAggregatorsMode.MinimizeInput */: {
|
|
52
|
+
for (let i = 0; i < requests.length; i++) {
|
|
53
|
+
const best = (0, data_1.minByBigInt)(responses.map((ra) => ({ quote: ra.quotes[i], aggregator: ra.agg })), (r) => r.quote?.inputAmount ?? 0n);
|
|
54
|
+
if (best != null) {
|
|
55
|
+
final[i] = best;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
final[i] = {
|
|
59
|
+
quote: null,
|
|
60
|
+
aggregator: aggregators[0],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return final;
|
|
68
|
+
}
|
|
69
|
+
async function autoSelectSources(userAddress, holdings, outputRequired, aggregators, collectionFees, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
70
|
+
console.log("XCS | SS | Holdings:", holdings);
|
|
71
|
+
const groupedByChainID = (0, es_toolkit_1.groupBy)(holdings, (h) => (0, viem_1.bytesToHex)(h.chainID.toBytes()));
|
|
72
|
+
const fullLiquidationQuotes = [];
|
|
73
|
+
for (const holdings of Object.values(groupedByChainID)) {
|
|
74
|
+
const chain = data_1.ChaindataMap.get(holdings[0].chainID);
|
|
75
|
+
if (chain == null) {
|
|
76
|
+
throw new AutoSelectionError("Chain not found");
|
|
77
|
+
}
|
|
78
|
+
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
79
|
+
if (correspondingCurrency == null) {
|
|
80
|
+
console.log("XCS | SS | Skipping because correspondingCurrency is null", {
|
|
81
|
+
chain,
|
|
82
|
+
correspondingCurrency,
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const cfeeTuple = collectionFees.find((cf) => {
|
|
87
|
+
return (cf.universe === chain.Universe &&
|
|
88
|
+
Buffer.compare(cf.chainID, chain.ChainID32) === 0 &&
|
|
89
|
+
// output token is the CA one
|
|
90
|
+
Buffer.compare(cf.tokenAddress, correspondingCurrency.tokenAddress) ===
|
|
91
|
+
0);
|
|
92
|
+
});
|
|
93
|
+
const cfee = cfeeTuple != null ? (0, viem_1.bytesToBigInt)(cfeeTuple.fee) : 0n;
|
|
94
|
+
for (const holding of holdings) {
|
|
95
|
+
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
96
|
+
console.log("XCS | SS | Disqualifying", holding, "because holding.tokenAddress = CA asset");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
fullLiquidationQuotes.push({
|
|
100
|
+
req: {
|
|
101
|
+
userAddress,
|
|
102
|
+
type: iface_1.QuoteType.EXACT_IN,
|
|
103
|
+
chain: chain.ChainID,
|
|
104
|
+
inputToken: holding.tokenAddress,
|
|
105
|
+
inputAmount: holding.amount,
|
|
106
|
+
outputToken: correspondingCurrency.tokenAddress,
|
|
107
|
+
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
108
|
+
},
|
|
109
|
+
// necessary for various purposes
|
|
110
|
+
cfee,
|
|
111
|
+
originalHolding: holding,
|
|
112
|
+
cur: correspondingCurrency,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// const groupedByChainID = groupBy(quoteOutputs, h => h.chainIDHex)
|
|
117
|
+
const quotesByValue = (0, es_toolkit_1.orderBy)(fullLiquidationQuotes, [
|
|
118
|
+
(quoteOut) => quoteOut.cfee,
|
|
119
|
+
(quoteOut) => quoteOut.originalHolding.value, // once optimized for collections, we select the biggest asset we hold
|
|
120
|
+
], ["asc", "desc"]);
|
|
121
|
+
const responses = await aggregateAggregators(quotesByValue.map((fq) => fq.req), aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
122
|
+
console.log("XCS | SS | Responses:", responses);
|
|
123
|
+
const final = [];
|
|
124
|
+
let remainder = outputRequired; // assuming all that chains have the same amount of fixed point places
|
|
125
|
+
for (let i = 0; i < quotesByValue.length; i++) {
|
|
126
|
+
if (remainder.lte(0)) {
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
const q = quotesByValue[i];
|
|
130
|
+
const { quote: resp, aggregator: agg } = responses[i];
|
|
131
|
+
if (resp == null) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
console.log("XCS | SS | 1", {
|
|
135
|
+
i,
|
|
136
|
+
remainder,
|
|
137
|
+
q,
|
|
138
|
+
resp,
|
|
139
|
+
agg,
|
|
140
|
+
});
|
|
141
|
+
const divisor = decimal_js_1.default.pow(10, q.cur.decimals);
|
|
142
|
+
const oamD = (0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum).div(divisor);
|
|
143
|
+
if (oamD.gt(remainder)) {
|
|
144
|
+
const indicativePrice = (0, data_1.convertBigIntToDecimal)(resp.inputAmount).div((0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum));
|
|
145
|
+
const userBal = (0, data_1.convertBigIntToDecimal)(q.originalHolding.amount);
|
|
146
|
+
// remainder is the output we want, so the input amount is remainder × indicativePrice
|
|
147
|
+
let expectedInput = decimal_js_1.default.min(remainder.mul(divisor).mul(indicativePrice).mul(safetyMultiplier), userBal);
|
|
148
|
+
while (true) {
|
|
149
|
+
console.log("XCS | SS | 2⒜", {
|
|
150
|
+
indicativePrice,
|
|
151
|
+
expectedInput,
|
|
152
|
+
userBal,
|
|
153
|
+
});
|
|
154
|
+
const adequateQuoteResult = await aggregateAggregators([
|
|
155
|
+
{
|
|
156
|
+
...q.req,
|
|
157
|
+
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
158
|
+
inputAmount: (0, data_1.convertDecimalToBigInt)(expectedInput),
|
|
159
|
+
},
|
|
160
|
+
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
161
|
+
if (adequateQuoteResult.length !== 1) {
|
|
162
|
+
throw new AutoSelectionError("???");
|
|
163
|
+
}
|
|
164
|
+
const adequateQuote = adequateQuoteResult[0];
|
|
165
|
+
if (adequateQuote.quote == null) {
|
|
166
|
+
throw new AutoSelectionError("Couldn't get buy quote");
|
|
167
|
+
}
|
|
168
|
+
console.log("XCS | SS | 2⒜⑴", {
|
|
169
|
+
adequateQuote,
|
|
170
|
+
});
|
|
171
|
+
const oam2D = (0, data_1.convertBigIntToDecimal)(adequateQuote.quote.outputAmountMinimum).div(divisor);
|
|
172
|
+
if (oam2D.gte(remainder)) {
|
|
173
|
+
final.push({
|
|
174
|
+
...q,
|
|
175
|
+
quote: adequateQuote.quote,
|
|
176
|
+
agg: adequateQuote.aggregator,
|
|
177
|
+
});
|
|
178
|
+
remainder = remainder.minus(oam2D);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
else if (expectedInput.eq(userBal)) {
|
|
182
|
+
throw new AutoSelectionError("Holding was supposedly enough to meet the full requirement but ceased to be so subsequently");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
expectedInput = decimal_js_1.default.min(expectedInput.mul(safetyMultiplier), userBal); // try again with higher amount
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
console.log("XCS | SS | 2⒝", resp);
|
|
191
|
+
final.push({
|
|
192
|
+
...q,
|
|
193
|
+
quote: resp,
|
|
194
|
+
agg,
|
|
195
|
+
});
|
|
196
|
+
remainder = remainder.minus((0, data_1.convertBigIntToDecimal)(resp.outputAmountMinimum).div(divisor));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
console.log("XCS | SS | 3⒜", {
|
|
200
|
+
remainder,
|
|
201
|
+
final,
|
|
202
|
+
});
|
|
203
|
+
if (remainder.gt(0)) {
|
|
204
|
+
throw new AutoSelectionError("Failed to accumulate enough swaps to meet requirement");
|
|
205
|
+
}
|
|
206
|
+
console.log("XCS | SS | Final:", final);
|
|
207
|
+
return final;
|
|
208
|
+
}
|
|
209
|
+
async function determineDestinationSwaps(userAddress, chainID, requirement, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
210
|
+
const chaindata = data_1.ChaindataMap.get(chainID);
|
|
211
|
+
if (chaindata == null) {
|
|
212
|
+
throw new AutoSelectionError("Chain not found");
|
|
213
|
+
}
|
|
214
|
+
const COT = chaindata.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
215
|
+
if (COT == null) {
|
|
216
|
+
throw new AutoSelectionError("COT not present on the destination chain");
|
|
217
|
+
}
|
|
218
|
+
// what happens if we happen to sell the requirement for the COT, what would the amount be?
|
|
219
|
+
const fullLiquidationQR = {
|
|
220
|
+
type: iface_1.QuoteType.EXACT_IN,
|
|
221
|
+
chain: chainID,
|
|
222
|
+
userAddress,
|
|
223
|
+
inputToken: requirement.tokenAddress,
|
|
224
|
+
outputToken: COT.tokenAddress,
|
|
225
|
+
inputAmount: requirement.amount,
|
|
226
|
+
seriousness: iface_1.QuoteSeriousness.PRICE_SURVEY,
|
|
227
|
+
};
|
|
228
|
+
const fullLiquidationResult = await aggregateAggregators([fullLiquidationQR], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
229
|
+
if (fullLiquidationResult.length !== 1) {
|
|
230
|
+
throw new AutoSelectionError("???");
|
|
231
|
+
}
|
|
232
|
+
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
233
|
+
if (fullLiquidationQuote.quote == null) {
|
|
234
|
+
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
235
|
+
}
|
|
236
|
+
let curAmount = (0, data_1.convertBigIntToDecimal)(fullLiquidationQuote.quote.outputAmountLikely).mul(safetyMultiplier);
|
|
237
|
+
console.log("XCS | DDS | 1⒜", {
|
|
238
|
+
fullLiquidationQR,
|
|
239
|
+
fullLiquidationResult,
|
|
240
|
+
COT,
|
|
241
|
+
});
|
|
242
|
+
while (true) {
|
|
243
|
+
const buyQuoteResult = await aggregateAggregators([
|
|
244
|
+
{
|
|
245
|
+
type: iface_1.QuoteType.EXACT_IN,
|
|
246
|
+
userAddress,
|
|
247
|
+
chain: chainID,
|
|
248
|
+
inputToken: COT.tokenAddress,
|
|
249
|
+
outputToken: requirement.tokenAddress,
|
|
250
|
+
inputAmount: (0, data_1.convertDecimalToBigInt)(curAmount),
|
|
251
|
+
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
252
|
+
},
|
|
253
|
+
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
254
|
+
if (buyQuoteResult.length !== 1) {
|
|
255
|
+
throw new AutoSelectionError("???");
|
|
256
|
+
}
|
|
257
|
+
const buyQuote = buyQuoteResult[0];
|
|
258
|
+
if (buyQuote.quote == null) {
|
|
259
|
+
throw new AutoSelectionError("Couldn't get buy quote");
|
|
260
|
+
}
|
|
261
|
+
console.log("XCS | DDS | 2⒜ iteration", {
|
|
262
|
+
buyQuote,
|
|
263
|
+
curAmount,
|
|
264
|
+
});
|
|
265
|
+
if (buyQuote.quote.outputAmountMinimum >= requirement.amount) {
|
|
266
|
+
return {
|
|
267
|
+
...buyQuote,
|
|
268
|
+
inputAmount: (0, data_1.convertBigIntToDecimal)(buyQuote.quote.inputAmount).div(decimal_js_1.default.pow(10, COT.decimals)),
|
|
269
|
+
outputAmount: requirement.amount,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
curAmount = curAmount.mul(safetyMultiplier); // try again with higher amount
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function liquidateInputHoldings(userAddress, holdings, aggregators, collectionFees, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
278
|
+
console.log("XCS | LIH | Holdings:", holdings);
|
|
279
|
+
const groupedByChainID = (0, es_toolkit_1.groupBy)(holdings, (h) => (0, viem_1.bytesToHex)(h.chainID.toBytes()));
|
|
280
|
+
const fullLiquidationQuotes = [];
|
|
281
|
+
for (const holdings of Object.values(groupedByChainID)) {
|
|
282
|
+
const chain = data_1.ChaindataMap.get(holdings[0].chainID);
|
|
283
|
+
if (chain == null) {
|
|
284
|
+
throw new AutoSelectionError("Chain not found");
|
|
285
|
+
}
|
|
286
|
+
const correspondingCurrency = chain.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
287
|
+
if (correspondingCurrency == null) {
|
|
288
|
+
console.log("XCS | LIH | Skipping because correspondingCurrency is null", {
|
|
289
|
+
chain,
|
|
290
|
+
correspondingCurrency,
|
|
291
|
+
});
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const cfeeTuple = collectionFees.find((cf) => {
|
|
295
|
+
return (cf.universe === chain.Universe &&
|
|
296
|
+
Buffer.compare(cf.chainID, chain.ChainID32) === 0 &&
|
|
297
|
+
// output token is the CA one
|
|
298
|
+
Buffer.compare(cf.tokenAddress, correspondingCurrency.tokenAddress) ===
|
|
299
|
+
0);
|
|
300
|
+
});
|
|
301
|
+
const cfee = cfeeTuple != null ? (0, viem_1.bytesToBigInt)(cfeeTuple.fee) : 0n;
|
|
302
|
+
for (const holding of holdings) {
|
|
303
|
+
if (Buffer.compare(holding.tokenAddress, correspondingCurrency.tokenAddress) === 0) {
|
|
304
|
+
console.log("XCS | LIH | Disqualifying", holding, "because holding.tokenAddress = CA asset");
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
fullLiquidationQuotes.push({
|
|
308
|
+
req: {
|
|
309
|
+
userAddress,
|
|
310
|
+
type: iface_1.QuoteType.EXACT_IN,
|
|
311
|
+
chain: chain.ChainID,
|
|
312
|
+
inputToken: holding.tokenAddress,
|
|
313
|
+
inputAmount: holding.amount,
|
|
314
|
+
outputToken: correspondingCurrency.tokenAddress,
|
|
315
|
+
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
316
|
+
},
|
|
317
|
+
// necessary for various purposes
|
|
318
|
+
cfee,
|
|
319
|
+
originalHolding: holding,
|
|
320
|
+
cur: correspondingCurrency,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const responses = await aggregateAggregators(fullLiquidationQuotes.map((fq) => fq.req), aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
325
|
+
console.log("XCS | LIH | Responses:", responses);
|
|
326
|
+
const validResponses = responses
|
|
327
|
+
.filter((r) => r.quote !== null)
|
|
328
|
+
.map((r, i) => ({
|
|
329
|
+
...r,
|
|
330
|
+
...fullLiquidationQuotes[i],
|
|
331
|
+
agg: r.aggregator,
|
|
332
|
+
quote: r.quote,
|
|
333
|
+
}));
|
|
334
|
+
const total = validResponses.reduce((acc, r) => {
|
|
335
|
+
return acc.add(new decimal_js_1.default(r.quote.outputAmountMinimum ?? 0n).div(decimal_js_1.default.pow(10, r.cur.decimals)));
|
|
336
|
+
}, new decimal_js_1.default(0));
|
|
337
|
+
return {
|
|
338
|
+
quotes: validResponses,
|
|
339
|
+
total,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
async function destinationSwapWithExactIn(userAddress, chainID, inputAmount, outputToken, aggregators, commonCurrencyID = data_1.CurrencyID.USDC) {
|
|
343
|
+
const chaindata = data_1.ChaindataMap.get(chainID);
|
|
344
|
+
if (chaindata == null) {
|
|
345
|
+
throw new AutoSelectionError("Chain not found");
|
|
346
|
+
}
|
|
347
|
+
const COT = chaindata.Currencies.find((cur) => cur.currencyID === commonCurrencyID);
|
|
348
|
+
if (COT == null) {
|
|
349
|
+
throw new AutoSelectionError("COT not present on the destination chain");
|
|
350
|
+
}
|
|
351
|
+
const fullLiquidationResult = await aggregateAggregators([
|
|
352
|
+
{
|
|
353
|
+
type: iface_1.QuoteType.EXACT_IN,
|
|
354
|
+
chain: chainID,
|
|
355
|
+
userAddress,
|
|
356
|
+
inputToken: COT.tokenAddress,
|
|
357
|
+
outputToken: outputToken,
|
|
358
|
+
inputAmount: inputAmount,
|
|
359
|
+
seriousness: iface_1.QuoteSeriousness.SERIOUS,
|
|
360
|
+
},
|
|
361
|
+
], aggregators, 0 /* AggregateAggregatorsMode.MaximizeOutput */);
|
|
362
|
+
if (fullLiquidationResult.length !== 1) {
|
|
363
|
+
throw new AutoSelectionError("???");
|
|
364
|
+
}
|
|
365
|
+
const fullLiquidationQuote = fullLiquidationResult[0];
|
|
366
|
+
if (fullLiquidationQuote.quote == null) {
|
|
367
|
+
throw new AutoSelectionError("Couldn't get full liquidation quote");
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
...fullLiquidationQuote,
|
|
371
|
+
inputAmount: (0, data_1.convertBigIntToDecimal)(inputAmount).div(decimal_js_1.default.pow(10, COT.decimals)),
|
|
372
|
+
outputAmount: fullLiquidationQuote.quote.outputAmountMinimum,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BebopAggregator = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const axios_1 = tslib_1.__importStar(require("axios"));
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const iface_1 = require("./iface");
|
|
8
|
+
const definition_1 = require("../proto/definition");
|
|
9
|
+
const data_1 = require("../data");
|
|
10
|
+
// https://api.bebop.xyz/{jam|pmm}/chains
|
|
11
|
+
const ChainNameMapping = new Map(Object.entries({
|
|
12
|
+
ethereum: 1,
|
|
13
|
+
arbitrum: 42161,
|
|
14
|
+
optimism: 10,
|
|
15
|
+
base: 8453,
|
|
16
|
+
taiko: 167000,
|
|
17
|
+
bsc: 56,
|
|
18
|
+
monadtestnet: 10143,
|
|
19
|
+
megaethtestnet: 6342,
|
|
20
|
+
berachain: 80094,
|
|
21
|
+
polygon: 137,
|
|
22
|
+
zksync: 324,
|
|
23
|
+
blast: 81457,
|
|
24
|
+
mode: 34443,
|
|
25
|
+
scroll: 534352,
|
|
26
|
+
superseed: 5330,
|
|
27
|
+
}).map(([k, v]) => [(0, viem_1.bytesToHex)((0, data_1.encodeChainID36)(definition_1.Universe.ETHEREUM, v)), k]));
|
|
28
|
+
class BebopAggregator {
|
|
29
|
+
static BASE_URL = "https://api.bebop.xyz/router";
|
|
30
|
+
static COMMON_OPTIONS = {
|
|
31
|
+
approval_type: "Standard",
|
|
32
|
+
skip_validation: "true",
|
|
33
|
+
gasless: false,
|
|
34
|
+
};
|
|
35
|
+
axios;
|
|
36
|
+
constructor(apiKey) {
|
|
37
|
+
this.axios = axios_1.default.create({
|
|
38
|
+
baseURL: BebopAggregator.BASE_URL,
|
|
39
|
+
headers: {
|
|
40
|
+
"Source-Auth": apiKey,
|
|
41
|
+
},
|
|
42
|
+
params: {
|
|
43
|
+
source: "arcana",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async getQuotes(requests) {
|
|
48
|
+
const list = await Promise.allSettled(requests.map(async (r) => {
|
|
49
|
+
const chainName = ChainNameMapping.get((0, viem_1.bytesToHex)(r.chain.toBytes()));
|
|
50
|
+
if (chainName == null) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
let respPromise;
|
|
54
|
+
const inputTokenAddr = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.inputToken.subarray(12)));
|
|
55
|
+
const outputTokenAddr = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.outputToken.subarray(12)));
|
|
56
|
+
const userAddrHex = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.userAddress.subarray(12)));
|
|
57
|
+
switch (r.type) {
|
|
58
|
+
case iface_1.QuoteType.EXACT_IN: {
|
|
59
|
+
respPromise = this.axios({
|
|
60
|
+
method: "GET",
|
|
61
|
+
url: `/${chainName}/v1/quote`,
|
|
62
|
+
params: {
|
|
63
|
+
sell_tokens: inputTokenAddr,
|
|
64
|
+
buy_tokens: outputTokenAddr,
|
|
65
|
+
taker_address: userAddrHex,
|
|
66
|
+
sell_amounts: r.inputAmount.toString(),
|
|
67
|
+
...BebopAggregator.COMMON_OPTIONS,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case iface_1.QuoteType.EXACT_OUT: {
|
|
73
|
+
respPromise = this.axios({
|
|
74
|
+
method: "GET",
|
|
75
|
+
url: `/${chainName}/v1/quote`,
|
|
76
|
+
params: {
|
|
77
|
+
sell_tokens: inputTokenAddr,
|
|
78
|
+
buy_tokens: outputTokenAddr,
|
|
79
|
+
taker_address: userAddrHex,
|
|
80
|
+
buy_amounts: r.outputAmount.toString(),
|
|
81
|
+
...BebopAggregator.COMMON_OPTIONS,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let resp;
|
|
88
|
+
try {
|
|
89
|
+
resp = await respPromise;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
if (e instanceof axios_1.AxiosError && e.isAxiosError) {
|
|
93
|
+
if (e.response?.status === 404 &&
|
|
94
|
+
e.response.data.code === 1002 &&
|
|
95
|
+
e.response.data.message ===
|
|
96
|
+
"No available quotes for the requested transfer") {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw e;
|
|
101
|
+
}
|
|
102
|
+
const bestRoute = resp.data.routes?.at(0);
|
|
103
|
+
if (bestRoute == null) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const buyT = bestRoute.quote.buyTokens[outputTokenAddr];
|
|
107
|
+
return {
|
|
108
|
+
type: r.type,
|
|
109
|
+
inputAmount: BigInt(bestRoute.quote.sellTokens[inputTokenAddr].amount),
|
|
110
|
+
outputAmountMinimum: BigInt(buyT.minimumAmount),
|
|
111
|
+
outputAmountLikely: BigInt(buyT.amount),
|
|
112
|
+
originalResponse: bestRoute,
|
|
113
|
+
};
|
|
114
|
+
}));
|
|
115
|
+
return list.map((item) => {
|
|
116
|
+
switch (item.status) {
|
|
117
|
+
case "fulfilled": {
|
|
118
|
+
return item.value;
|
|
119
|
+
}
|
|
120
|
+
case "rejected": {
|
|
121
|
+
console.error("Caught error in fetching Bebop quotes:", item.reason);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.BebopAggregator = BebopAggregator;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuoteSeriousness = exports.QuoteType = void 0;
|
|
4
|
+
var QuoteType;
|
|
5
|
+
(function (QuoteType) {
|
|
6
|
+
QuoteType[QuoteType["EXACT_IN"] = 0] = "EXACT_IN";
|
|
7
|
+
QuoteType[QuoteType["EXACT_OUT"] = 1] = "EXACT_OUT";
|
|
8
|
+
})(QuoteType || (exports.QuoteType = QuoteType = {}));
|
|
9
|
+
var QuoteSeriousness;
|
|
10
|
+
(function (QuoteSeriousness) {
|
|
11
|
+
QuoteSeriousness[QuoteSeriousness["PRICE_SURVEY"] = 0] = "PRICE_SURVEY";
|
|
12
|
+
QuoteSeriousness[QuoteSeriousness["SERIOUS"] = 1] = "SERIOUS";
|
|
13
|
+
})(QuoteSeriousness || (exports.QuoteSeriousness = QuoteSeriousness = {}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./iface"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./lifi-agg"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./yieldyak-agg"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./bebop-agg"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./autochoice"), exports);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LiFiAggregator = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const axios_1 = tslib_1.__importStar(require("axios"));
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const iface_1 = require("./iface");
|
|
8
|
+
const definition_1 = require("../proto/definition");
|
|
9
|
+
class LiFiAggregator {
|
|
10
|
+
static BASE_URL_V1 = "https://li.quest/v1";
|
|
11
|
+
static COMMON_OPTIONS = {
|
|
12
|
+
denyExchanges: "openocean",
|
|
13
|
+
slippage: "0.01",
|
|
14
|
+
};
|
|
15
|
+
axios;
|
|
16
|
+
constructor(apiKey) {
|
|
17
|
+
this.axios = axios_1.default.create({
|
|
18
|
+
baseURL: LiFiAggregator.BASE_URL_V1,
|
|
19
|
+
headers: {
|
|
20
|
+
"x-lifi-api-key": apiKey,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async getQuotes(requests) {
|
|
25
|
+
const list = await Promise.allSettled(requests.map(async (r) => {
|
|
26
|
+
if (r.chain.universe !== definition_1.Universe.ETHEREUM) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
let respPromise;
|
|
30
|
+
const chIDStr = r.chain.chainID.toString();
|
|
31
|
+
const inputTokenAddr = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.inputToken.subarray(12)));
|
|
32
|
+
const outputTokenAddr = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.outputToken.subarray(12)));
|
|
33
|
+
const userAddrHex = (0, viem_1.getAddress)((0, viem_1.bytesToHex)(r.userAddress.subarray(12)));
|
|
34
|
+
switch (r.type) {
|
|
35
|
+
case iface_1.QuoteType.EXACT_IN: {
|
|
36
|
+
respPromise = this.axios({
|
|
37
|
+
method: "GET",
|
|
38
|
+
url: "/quote",
|
|
39
|
+
params: {
|
|
40
|
+
fromChain: chIDStr,
|
|
41
|
+
toChain: chIDStr,
|
|
42
|
+
fromToken: inputTokenAddr,
|
|
43
|
+
toToken: outputTokenAddr,
|
|
44
|
+
fromAddress: userAddrHex,
|
|
45
|
+
fromAmount: r.inputAmount.toString(),
|
|
46
|
+
...LiFiAggregator.COMMON_OPTIONS,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case iface_1.QuoteType.EXACT_OUT: {
|
|
52
|
+
respPromise = this.axios({
|
|
53
|
+
method: "GET",
|
|
54
|
+
url: "/quote/toAmount",
|
|
55
|
+
params: {
|
|
56
|
+
fromChain: chIDStr,
|
|
57
|
+
toChain: chIDStr,
|
|
58
|
+
fromToken: inputTokenAddr,
|
|
59
|
+
toToken: outputTokenAddr,
|
|
60
|
+
fromAddress: userAddrHex,
|
|
61
|
+
toAmount: r.outputAmount.toString(),
|
|
62
|
+
...LiFiAggregator.COMMON_OPTIONS,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let resp;
|
|
69
|
+
try {
|
|
70
|
+
resp = await respPromise;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e instanceof axios_1.AxiosError && e.isAxiosError) {
|
|
74
|
+
if (e.response?.status === 404 &&
|
|
75
|
+
e.response.data.code === 1002 &&
|
|
76
|
+
e.response.data.message ===
|
|
77
|
+
"No available quotes for the requested transfer") {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
type: r.type,
|
|
85
|
+
inputAmount: BigInt(resp.data.estimate.fromAmount),
|
|
86
|
+
outputAmountMinimum: BigInt(resp.data.estimate.toAmountMin),
|
|
87
|
+
outputAmountLikely: BigInt(resp.data.estimate.toAmount),
|
|
88
|
+
originalResponse: resp.data,
|
|
89
|
+
};
|
|
90
|
+
}));
|
|
91
|
+
return list.map((item) => {
|
|
92
|
+
switch (item.status) {
|
|
93
|
+
case "fulfilled": {
|
|
94
|
+
return item.value;
|
|
95
|
+
}
|
|
96
|
+
case "rejected": {
|
|
97
|
+
console.error("Caught error in fetching LiFi quotes:", item.reason);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.LiFiAggregator = LiFiAggregator;
|