@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.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://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.1.68",
3
+ "version": "1.1.69",
4
4
  "description": "STRKFarm TS SDK (Meant for our internal use, but feel free to use it)",
5
5
  "typings": "dist/index.d.ts",
6
6
  "types": "dist/index.d.ts",
@@ -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://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'; // e.g. ETH/USDC
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());