@strkfarm/sdk 1.1.68 → 1.1.69
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/index.browser.global.js +3789 -3764
- package/dist/index.browser.mjs +170 -145
- package/dist/index.d.ts +2 -1
- package/dist/index.js +174 -149
- package/dist/index.mjs +170 -145
- package/package.json +1 -1
- package/src/modules/pricer.ts +35 -2
package/dist/index.mjs
CHANGED
|
@@ -429,6 +429,150 @@ var PricerBase = class {
|
|
|
429
429
|
}
|
|
430
430
|
};
|
|
431
431
|
|
|
432
|
+
// src/modules/avnu.ts
|
|
433
|
+
import { uint256 } from "starknet";
|
|
434
|
+
import { fetchBuildExecuteTransaction, fetchQuotes } from "@avnu/avnu-sdk";
|
|
435
|
+
|
|
436
|
+
// src/utils/oz-merkle.ts
|
|
437
|
+
import { toHex } from "@ericnordelo/strk-merkle-tree/dist/bytes";
|
|
438
|
+
import { processMultiProof, processProof } from "@ericnordelo/strk-merkle-tree/dist/core";
|
|
439
|
+
import { standardLeafHash } from "@ericnordelo/strk-merkle-tree/dist/hashes";
|
|
440
|
+
import { MerkleTreeImpl } from "@ericnordelo/strk-merkle-tree/dist/merkletree";
|
|
441
|
+
import { num as num2 } from "starknet";
|
|
442
|
+
import { pedersen } from "@scure/starknet";
|
|
443
|
+
function hash_leaf(leaf) {
|
|
444
|
+
if (leaf.data.length < 1) {
|
|
445
|
+
throw new Error("Invalid leaf data");
|
|
446
|
+
}
|
|
447
|
+
let firstElement = leaf.data[0];
|
|
448
|
+
let value = firstElement;
|
|
449
|
+
for (let i = 1; i < leaf.data.length; i++) {
|
|
450
|
+
value = pedersen_hash(value, leaf.data[i]);
|
|
451
|
+
}
|
|
452
|
+
return `0x${num2.toHexString(value).replace(/^0x/, "").padStart(64, "0")}`;
|
|
453
|
+
}
|
|
454
|
+
function pedersen_hash(a, b) {
|
|
455
|
+
return BigInt(pedersen(a, b).toString());
|
|
456
|
+
}
|
|
457
|
+
var StandardMerkleTree = class _StandardMerkleTree extends MerkleTreeImpl {
|
|
458
|
+
constructor(tree, values, leafEncoding) {
|
|
459
|
+
super(tree, values, (leaf) => {
|
|
460
|
+
return hash_leaf(leaf);
|
|
461
|
+
});
|
|
462
|
+
this.tree = tree;
|
|
463
|
+
this.values = values;
|
|
464
|
+
this.leafEncoding = leafEncoding;
|
|
465
|
+
}
|
|
466
|
+
static of(values, leafEncoding = [], options = {}) {
|
|
467
|
+
const [tree, indexedValues] = MerkleTreeImpl.prepare(values, options, (leaf) => {
|
|
468
|
+
return hash_leaf(leaf);
|
|
469
|
+
});
|
|
470
|
+
return new _StandardMerkleTree(tree, indexedValues, leafEncoding);
|
|
471
|
+
}
|
|
472
|
+
static verify(root, leafEncoding, leaf, proof) {
|
|
473
|
+
return toHex(root) === processProof(standardLeafHash(leafEncoding, leaf), proof);
|
|
474
|
+
}
|
|
475
|
+
static verifyMultiProof(root, leafEncoding, multiproof) {
|
|
476
|
+
return toHex(root) === processMultiProof({
|
|
477
|
+
leaves: multiproof.leaves.map((leaf) => standardLeafHash(leafEncoding, leaf)),
|
|
478
|
+
proof: multiproof.proof,
|
|
479
|
+
proofFlags: multiproof.proofFlags
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
dump() {
|
|
483
|
+
return {
|
|
484
|
+
format: "standard-v1",
|
|
485
|
+
leafEncoding: this.leafEncoding,
|
|
486
|
+
tree: this.tree,
|
|
487
|
+
values: this.values
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/utils/index.ts
|
|
493
|
+
function assert(condition, message) {
|
|
494
|
+
if (!condition) {
|
|
495
|
+
throw new Error(message);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function getTrovesEndpoint() {
|
|
499
|
+
return process.env.TROVES_ENDPOINT || "https://app.troves.fi";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/modules/avnu.ts
|
|
503
|
+
var AvnuWrapper = class {
|
|
504
|
+
async getQuotes(fromToken, toToken, amountWei, taker, retry = 0, excludeSources = ["Haiko(Solvers)"]) {
|
|
505
|
+
const MAX_RETRY = 5;
|
|
506
|
+
const params = {
|
|
507
|
+
sellTokenAddress: fromToken,
|
|
508
|
+
buyTokenAddress: toToken,
|
|
509
|
+
sellAmount: amountWei,
|
|
510
|
+
takerAddress: taker,
|
|
511
|
+
// excludeSources: ['Nostra', 'Haiko(Solvers)']
|
|
512
|
+
excludeSources
|
|
513
|
+
// excludeSources: ['Haiko(Solvers)'] // to resolve InvalidOraclePrice error
|
|
514
|
+
};
|
|
515
|
+
assert(fromToken != toToken, "From and to tokens are the same");
|
|
516
|
+
const quotes = await fetchQuotes(params);
|
|
517
|
+
const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
|
|
518
|
+
if (filteredQuotes.length == 0) {
|
|
519
|
+
if (retry < MAX_RETRY) {
|
|
520
|
+
await new Promise((res) => setTimeout(res, 3e3));
|
|
521
|
+
return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
|
|
522
|
+
}
|
|
523
|
+
throw new Error("no quotes found");
|
|
524
|
+
}
|
|
525
|
+
return filteredQuotes[0];
|
|
526
|
+
}
|
|
527
|
+
async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount, options) {
|
|
528
|
+
const calldata = await fetchBuildExecuteTransaction(quote.quoteId, taker, void 0, void 0, options);
|
|
529
|
+
const call = calldata.calls[1];
|
|
530
|
+
const callData = call.calldata;
|
|
531
|
+
const routesLen = Number(callData[11]);
|
|
532
|
+
assert(routesLen > 0, "No routes found");
|
|
533
|
+
let startIndex = 12;
|
|
534
|
+
const routes = [];
|
|
535
|
+
for (let i = 0; i < routesLen; ++i) {
|
|
536
|
+
const swap_params_len = Number(callData[startIndex + 4]);
|
|
537
|
+
const route = {
|
|
538
|
+
token_from: callData[startIndex],
|
|
539
|
+
token_to: callData[startIndex + 1],
|
|
540
|
+
exchange_address: callData[startIndex + 2],
|
|
541
|
+
percent: Number(callData[startIndex + 3]),
|
|
542
|
+
additional_swap_params: swap_params_len > 0 ? callData.slice(startIndex + 5, startIndex + 5 + swap_params_len) : []
|
|
543
|
+
};
|
|
544
|
+
routes.push(route);
|
|
545
|
+
startIndex += 5 + swap_params_len;
|
|
546
|
+
}
|
|
547
|
+
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
548
|
+
const swapInfo = {
|
|
549
|
+
token_from_address: quote.sellTokenAddress,
|
|
550
|
+
token_from_amount: uint256.bnToUint256(quote.sellAmount),
|
|
551
|
+
token_to_address: quote.buyTokenAddress,
|
|
552
|
+
token_to_amount: uint256.bnToUint256(_minAmount),
|
|
553
|
+
token_to_min_amount: uint256.bnToUint256(_minAmount),
|
|
554
|
+
beneficiary: taker,
|
|
555
|
+
integrator_fee_amount_bps: integratorFeeBps,
|
|
556
|
+
integrator_fee_recipient: integratorFeeRecipient,
|
|
557
|
+
routes
|
|
558
|
+
};
|
|
559
|
+
return swapInfo;
|
|
560
|
+
}
|
|
561
|
+
static buildZeroSwap(tokenToSell, beneficiary, tokenToBuy = tokenToSell) {
|
|
562
|
+
return {
|
|
563
|
+
token_from_address: tokenToSell.address,
|
|
564
|
+
token_from_amount: uint256.bnToUint256(0),
|
|
565
|
+
token_to_address: tokenToBuy.address,
|
|
566
|
+
token_to_amount: uint256.bnToUint256(0),
|
|
567
|
+
token_to_min_amount: uint256.bnToUint256(0),
|
|
568
|
+
beneficiary,
|
|
569
|
+
integrator_fee_amount_bps: 0,
|
|
570
|
+
integrator_fee_recipient: beneficiary,
|
|
571
|
+
routes: []
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
432
576
|
// src/modules/pricer.ts
|
|
433
577
|
var Pricer = class extends PricerBase {
|
|
434
578
|
// e.g. ETH/USDC
|
|
@@ -445,7 +589,7 @@ var Pricer = class extends PricerBase {
|
|
|
445
589
|
*/
|
|
446
590
|
// ! switch to USDC (new) later
|
|
447
591
|
this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
448
|
-
this.EKUBO_API = "https://
|
|
592
|
+
this.EKUBO_API = "https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
|
|
449
593
|
this.refreshInterval = refreshInterval;
|
|
450
594
|
this.staleTime = staleTime;
|
|
451
595
|
}
|
|
@@ -579,6 +723,15 @@ var Pricer = class extends PricerBase {
|
|
|
579
723
|
console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
|
|
580
724
|
console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
|
|
581
725
|
}
|
|
726
|
+
case "Avnu":
|
|
727
|
+
try {
|
|
728
|
+
const result = await this._getAvnuPrice(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
|
|
729
|
+
this.methodToUse[token.symbol] = "Avnu";
|
|
730
|
+
return result;
|
|
731
|
+
} catch (error) {
|
|
732
|
+
console.warn(`Avnu: price err [${token.symbol}]: `, error.message);
|
|
733
|
+
console.warn(`Avnu: price err [${token.symbol}]: `, Object.keys(error));
|
|
734
|
+
}
|
|
582
735
|
}
|
|
583
736
|
if (defaultMethod == "all") {
|
|
584
737
|
return await this._getPrice(token, "Coinbase");
|
|
@@ -594,6 +747,22 @@ var Pricer = class extends PricerBase {
|
|
|
594
747
|
async _getPriceCoinMarketCap(token) {
|
|
595
748
|
throw new Error("Not implemented");
|
|
596
749
|
}
|
|
750
|
+
async _getAvnuPrice(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
751
|
+
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
752
|
+
const avnuWrapper = new AvnuWrapper();
|
|
753
|
+
const usdcAddress = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
|
|
754
|
+
const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), "0x1");
|
|
755
|
+
const multiplier = 1 / amountIn.toNumber();
|
|
756
|
+
const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
|
|
757
|
+
logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
|
|
758
|
+
if (outputUSDC === 0 && retry < 3) {
|
|
759
|
+
const amountIn2 = new Web3Number(100, token.decimals);
|
|
760
|
+
return await this._getAvnuPrice(token, amountIn2, retry + 1);
|
|
761
|
+
}
|
|
762
|
+
const usdcPrice = 1;
|
|
763
|
+
logger.verbose(`USDC Price: ${usdcPrice}`);
|
|
764
|
+
return outputUSDC * usdcPrice;
|
|
765
|
+
}
|
|
597
766
|
async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
598
767
|
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
599
768
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
|
|
@@ -2107,150 +2276,6 @@ var ERC20 = class {
|
|
|
2107
2276
|
}
|
|
2108
2277
|
};
|
|
2109
2278
|
|
|
2110
|
-
// src/modules/avnu.ts
|
|
2111
|
-
import { uint256 } from "starknet";
|
|
2112
|
-
import { fetchBuildExecuteTransaction, fetchQuotes } from "@avnu/avnu-sdk";
|
|
2113
|
-
|
|
2114
|
-
// src/utils/oz-merkle.ts
|
|
2115
|
-
import { toHex } from "@ericnordelo/strk-merkle-tree/dist/bytes";
|
|
2116
|
-
import { processMultiProof, processProof } from "@ericnordelo/strk-merkle-tree/dist/core";
|
|
2117
|
-
import { standardLeafHash } from "@ericnordelo/strk-merkle-tree/dist/hashes";
|
|
2118
|
-
import { MerkleTreeImpl } from "@ericnordelo/strk-merkle-tree/dist/merkletree";
|
|
2119
|
-
import { num as num2 } from "starknet";
|
|
2120
|
-
import { pedersen } from "@scure/starknet";
|
|
2121
|
-
function hash_leaf(leaf) {
|
|
2122
|
-
if (leaf.data.length < 1) {
|
|
2123
|
-
throw new Error("Invalid leaf data");
|
|
2124
|
-
}
|
|
2125
|
-
let firstElement = leaf.data[0];
|
|
2126
|
-
let value = firstElement;
|
|
2127
|
-
for (let i = 1; i < leaf.data.length; i++) {
|
|
2128
|
-
value = pedersen_hash(value, leaf.data[i]);
|
|
2129
|
-
}
|
|
2130
|
-
return `0x${num2.toHexString(value).replace(/^0x/, "").padStart(64, "0")}`;
|
|
2131
|
-
}
|
|
2132
|
-
function pedersen_hash(a, b) {
|
|
2133
|
-
return BigInt(pedersen(a, b).toString());
|
|
2134
|
-
}
|
|
2135
|
-
var StandardMerkleTree = class _StandardMerkleTree extends MerkleTreeImpl {
|
|
2136
|
-
constructor(tree, values, leafEncoding) {
|
|
2137
|
-
super(tree, values, (leaf) => {
|
|
2138
|
-
return hash_leaf(leaf);
|
|
2139
|
-
});
|
|
2140
|
-
this.tree = tree;
|
|
2141
|
-
this.values = values;
|
|
2142
|
-
this.leafEncoding = leafEncoding;
|
|
2143
|
-
}
|
|
2144
|
-
static of(values, leafEncoding = [], options = {}) {
|
|
2145
|
-
const [tree, indexedValues] = MerkleTreeImpl.prepare(values, options, (leaf) => {
|
|
2146
|
-
return hash_leaf(leaf);
|
|
2147
|
-
});
|
|
2148
|
-
return new _StandardMerkleTree(tree, indexedValues, leafEncoding);
|
|
2149
|
-
}
|
|
2150
|
-
static verify(root, leafEncoding, leaf, proof) {
|
|
2151
|
-
return toHex(root) === processProof(standardLeafHash(leafEncoding, leaf), proof);
|
|
2152
|
-
}
|
|
2153
|
-
static verifyMultiProof(root, leafEncoding, multiproof) {
|
|
2154
|
-
return toHex(root) === processMultiProof({
|
|
2155
|
-
leaves: multiproof.leaves.map((leaf) => standardLeafHash(leafEncoding, leaf)),
|
|
2156
|
-
proof: multiproof.proof,
|
|
2157
|
-
proofFlags: multiproof.proofFlags
|
|
2158
|
-
});
|
|
2159
|
-
}
|
|
2160
|
-
dump() {
|
|
2161
|
-
return {
|
|
2162
|
-
format: "standard-v1",
|
|
2163
|
-
leafEncoding: this.leafEncoding,
|
|
2164
|
-
tree: this.tree,
|
|
2165
|
-
values: this.values
|
|
2166
|
-
};
|
|
2167
|
-
}
|
|
2168
|
-
};
|
|
2169
|
-
|
|
2170
|
-
// src/utils/index.ts
|
|
2171
|
-
function assert(condition, message) {
|
|
2172
|
-
if (!condition) {
|
|
2173
|
-
throw new Error(message);
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
function getTrovesEndpoint() {
|
|
2177
|
-
return process.env.TROVES_ENDPOINT || "https://app.troves.fi";
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// src/modules/avnu.ts
|
|
2181
|
-
var AvnuWrapper = class {
|
|
2182
|
-
async getQuotes(fromToken, toToken, amountWei, taker, retry = 0, excludeSources = ["Haiko(Solvers)"]) {
|
|
2183
|
-
const MAX_RETRY = 5;
|
|
2184
|
-
const params = {
|
|
2185
|
-
sellTokenAddress: fromToken,
|
|
2186
|
-
buyTokenAddress: toToken,
|
|
2187
|
-
sellAmount: amountWei,
|
|
2188
|
-
takerAddress: taker,
|
|
2189
|
-
// excludeSources: ['Nostra', 'Haiko(Solvers)']
|
|
2190
|
-
excludeSources
|
|
2191
|
-
// excludeSources: ['Haiko(Solvers)'] // to resolve InvalidOraclePrice error
|
|
2192
|
-
};
|
|
2193
|
-
assert(fromToken != toToken, "From and to tokens are the same");
|
|
2194
|
-
const quotes = await fetchQuotes(params);
|
|
2195
|
-
const filteredQuotes = quotes.filter((q) => q.sellAmount.toString() == amountWei);
|
|
2196
|
-
if (filteredQuotes.length == 0) {
|
|
2197
|
-
if (retry < MAX_RETRY) {
|
|
2198
|
-
await new Promise((res) => setTimeout(res, 3e3));
|
|
2199
|
-
return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
|
|
2200
|
-
}
|
|
2201
|
-
throw new Error("no quotes found");
|
|
2202
|
-
}
|
|
2203
|
-
return filteredQuotes[0];
|
|
2204
|
-
}
|
|
2205
|
-
async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount, options) {
|
|
2206
|
-
const calldata = await fetchBuildExecuteTransaction(quote.quoteId, taker, void 0, void 0, options);
|
|
2207
|
-
const call = calldata.calls[1];
|
|
2208
|
-
const callData = call.calldata;
|
|
2209
|
-
const routesLen = Number(callData[11]);
|
|
2210
|
-
assert(routesLen > 0, "No routes found");
|
|
2211
|
-
let startIndex = 12;
|
|
2212
|
-
const routes = [];
|
|
2213
|
-
for (let i = 0; i < routesLen; ++i) {
|
|
2214
|
-
const swap_params_len = Number(callData[startIndex + 4]);
|
|
2215
|
-
const route = {
|
|
2216
|
-
token_from: callData[startIndex],
|
|
2217
|
-
token_to: callData[startIndex + 1],
|
|
2218
|
-
exchange_address: callData[startIndex + 2],
|
|
2219
|
-
percent: Number(callData[startIndex + 3]),
|
|
2220
|
-
additional_swap_params: swap_params_len > 0 ? callData.slice(startIndex + 5, startIndex + 5 + swap_params_len) : []
|
|
2221
|
-
};
|
|
2222
|
-
routes.push(route);
|
|
2223
|
-
startIndex += 5 + swap_params_len;
|
|
2224
|
-
}
|
|
2225
|
-
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
2226
|
-
const swapInfo = {
|
|
2227
|
-
token_from_address: quote.sellTokenAddress,
|
|
2228
|
-
token_from_amount: uint256.bnToUint256(quote.sellAmount),
|
|
2229
|
-
token_to_address: quote.buyTokenAddress,
|
|
2230
|
-
token_to_amount: uint256.bnToUint256(_minAmount),
|
|
2231
|
-
token_to_min_amount: uint256.bnToUint256(_minAmount),
|
|
2232
|
-
beneficiary: taker,
|
|
2233
|
-
integrator_fee_amount_bps: integratorFeeBps,
|
|
2234
|
-
integrator_fee_recipient: integratorFeeRecipient,
|
|
2235
|
-
routes
|
|
2236
|
-
};
|
|
2237
|
-
return swapInfo;
|
|
2238
|
-
}
|
|
2239
|
-
static buildZeroSwap(tokenToSell, beneficiary, tokenToBuy = tokenToSell) {
|
|
2240
|
-
return {
|
|
2241
|
-
token_from_address: tokenToSell.address,
|
|
2242
|
-
token_from_amount: uint256.bnToUint256(0),
|
|
2243
|
-
token_to_address: tokenToBuy.address,
|
|
2244
|
-
token_to_amount: uint256.bnToUint256(0),
|
|
2245
|
-
token_to_min_amount: uint256.bnToUint256(0),
|
|
2246
|
-
beneficiary,
|
|
2247
|
-
integrator_fee_amount_bps: 0,
|
|
2248
|
-
integrator_fee_recipient: beneficiary,
|
|
2249
|
-
routes: []
|
|
2250
|
-
};
|
|
2251
|
-
}
|
|
2252
|
-
};
|
|
2253
|
-
|
|
2254
2279
|
// src/modules/ekubo-quoter.ts
|
|
2255
2280
|
import axios5 from "axios";
|
|
2256
2281
|
var EkuboQuoter = class {
|
package/package.json
CHANGED
package/src/modules/pricer.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { IConfig } from "@/interfaces/common";
|
|
|
5
5
|
import { Web3Number } from "@/dataTypes";
|
|
6
6
|
import { PricerBase } from "./pricerBase";
|
|
7
7
|
import { logger } from "@/utils/logger";
|
|
8
|
+
import { AvnuWrapper } from "./avnu";
|
|
8
9
|
|
|
9
10
|
export interface PriceInfo {
|
|
10
11
|
price: number,
|
|
@@ -21,14 +22,14 @@ export class Pricer extends PricerBase {
|
|
|
21
22
|
|
|
22
23
|
// code populates this map during runtime to determine which method to use for a given token
|
|
23
24
|
// The method set will be the first one to try after first attempt
|
|
24
|
-
protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'} = {};
|
|
25
|
+
protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap' | 'Avnu'} = {};
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
|
|
28
29
|
*/
|
|
29
30
|
// ! switch to USDC (new) later
|
|
30
31
|
protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
31
|
-
protected EKUBO_API = 'https://
|
|
32
|
+
protected EKUBO_API = 'https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb'; // e.g. ETH/USDC
|
|
32
33
|
|
|
33
34
|
constructor(config: IConfig, tokens: TokenInfo[], refreshInterval = 30000, staleTime = 60000) {
|
|
34
35
|
super(config, tokens);
|
|
@@ -175,6 +176,15 @@ export class Pricer extends PricerBase {
|
|
|
175
176
|
console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
|
|
176
177
|
// do nothing, try next
|
|
177
178
|
}
|
|
179
|
+
case 'Avnu':
|
|
180
|
+
try {
|
|
181
|
+
const result = await this._getAvnuPrice(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
|
|
182
|
+
this.methodToUse[token.symbol] = 'Avnu';
|
|
183
|
+
return result;
|
|
184
|
+
} catch (error: any) {
|
|
185
|
+
console.warn(`Avnu: price err [${token.symbol}]: `, error.message);
|
|
186
|
+
console.warn(`Avnu: price err [${token.symbol}]: `, Object.keys(error));
|
|
187
|
+
}
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
// if methodToUse is the default one, pass Coinbase to try all from start
|
|
@@ -201,6 +211,29 @@ export class Pricer extends PricerBase {
|
|
|
201
211
|
throw new Error("Not implemented");
|
|
202
212
|
}
|
|
203
213
|
|
|
214
|
+
async _getAvnuPrice(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
215
|
+
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
216
|
+
|
|
217
|
+
const avnuWrapper = new AvnuWrapper();
|
|
218
|
+
const usdcAddress = '0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb';
|
|
219
|
+
const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), '0x1');
|
|
220
|
+
const multiplier = 1 / amountIn.toNumber();
|
|
221
|
+
const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
|
|
222
|
+
logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
|
|
223
|
+
if (outputUSDC === 0 && retry < 3) {
|
|
224
|
+
// try again with a higher amount
|
|
225
|
+
const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
|
|
226
|
+
return await this._getAvnuPrice(token, amountIn, retry + 1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// if usdc depegs, it will not longer be 1 USD
|
|
230
|
+
// so we need to get the price of USDC in USD
|
|
231
|
+
// and then convert the outputUSDC to USD
|
|
232
|
+
const usdcPrice = 1; // (await this.getPrice('USDC')).price;
|
|
233
|
+
logger.verbose(`USDC Price: ${usdcPrice}`);
|
|
234
|
+
return outputUSDC * usdcPrice;
|
|
235
|
+
}
|
|
236
|
+
|
|
204
237
|
async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
205
238
|
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
206
239
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
|