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