@strkfarm/sdk 2.0.0-staging.69 → 2.0.0-staging.70
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 +148 -48
- package/dist/index.browser.mjs +164 -63
- package/dist/index.d.ts +29 -3
- package/dist/index.js +166 -63
- package/dist/index.mjs +165 -63
- package/package.json +1 -1
- package/src/global.ts +5 -0
- package/src/interfaces/common.tsx +1 -0
- package/src/modules/index.ts +1 -0
- package/src/modules/pricer-avnu-api.ts +114 -0
- package/src/modules/pricer.ts +63 -45
- package/src/node/pricer-redis.ts +1 -0
- package/src/strategies/ekubo-cl-vault.tsx +4 -1
- package/src/strategies/universal-strategy.tsx +4 -1
- package/src/strategies/yoloVault.ts +4 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/modules/pricer.ts
|
|
2
|
-
import
|
|
2
|
+
import axios3 from "axios";
|
|
3
3
|
|
|
4
4
|
// src/global.ts
|
|
5
5
|
import axios from "axios";
|
|
@@ -466,8 +466,9 @@ var defaultTokens = [{
|
|
|
466
466
|
decimals: 18,
|
|
467
467
|
coingeckId: void 0,
|
|
468
468
|
displayDecimals: 6,
|
|
469
|
-
priceCheckAmount: 1e-4
|
|
469
|
+
priceCheckAmount: 1e-4,
|
|
470
470
|
// 112000 * 0.0001 = $11.2
|
|
471
|
+
dontPrice: true
|
|
471
472
|
}, {
|
|
472
473
|
name: "mRe7YIELD",
|
|
473
474
|
symbol: "mRe7YIELD",
|
|
@@ -476,7 +477,8 @@ var defaultTokens = [{
|
|
|
476
477
|
decimals: 18,
|
|
477
478
|
coingeckId: void 0,
|
|
478
479
|
displayDecimals: 2,
|
|
479
|
-
priceCheckAmount: 100
|
|
480
|
+
priceCheckAmount: 100,
|
|
481
|
+
dontPrice: true
|
|
480
482
|
}, {
|
|
481
483
|
name: "fyeWBTC",
|
|
482
484
|
symbol: "fyeWBTC",
|
|
@@ -485,8 +487,9 @@ var defaultTokens = [{
|
|
|
485
487
|
decimals: 8,
|
|
486
488
|
coingeckId: void 0,
|
|
487
489
|
displayDecimals: 6,
|
|
488
|
-
priceCheckAmount: 1e-3
|
|
490
|
+
priceCheckAmount: 1e-3,
|
|
489
491
|
// 112000 * 0.0001 = $110.2
|
|
492
|
+
dontPrice: true
|
|
490
493
|
}, {
|
|
491
494
|
name: "fyETH",
|
|
492
495
|
symbol: "fyETH",
|
|
@@ -495,7 +498,8 @@ var defaultTokens = [{
|
|
|
495
498
|
decimals: 18,
|
|
496
499
|
coingeckId: void 0,
|
|
497
500
|
displayDecimals: 4,
|
|
498
|
-
priceCheckAmount: 0.1
|
|
501
|
+
priceCheckAmount: 0.1,
|
|
502
|
+
dontPrice: true
|
|
499
503
|
}, {
|
|
500
504
|
name: "fyeUSDC",
|
|
501
505
|
symbol: "fyeUSDC",
|
|
@@ -504,7 +508,8 @@ var defaultTokens = [{
|
|
|
504
508
|
decimals: 6,
|
|
505
509
|
coingeckId: void 0,
|
|
506
510
|
displayDecimals: 2,
|
|
507
|
-
priceCheckAmount: 100
|
|
511
|
+
priceCheckAmount: 100,
|
|
512
|
+
dontPrice: true
|
|
508
513
|
}, {
|
|
509
514
|
name: "strkBTC",
|
|
510
515
|
symbol: "strkBTC",
|
|
@@ -795,7 +800,96 @@ var AvnuWrapper = class _AvnuWrapper {
|
|
|
795
800
|
}
|
|
796
801
|
};
|
|
797
802
|
|
|
803
|
+
// src/modules/pricer-avnu-api.ts
|
|
804
|
+
import axios2 from "axios";
|
|
805
|
+
var AVNU_TOKENS_API = "https://starknet.impulse.avnu.fi/v3/tokens";
|
|
806
|
+
var PricerAvnuApi = class extends PricerBase {
|
|
807
|
+
constructor(config, tokens2) {
|
|
808
|
+
super(config, tokens2);
|
|
809
|
+
this.prices = {};
|
|
810
|
+
this.refreshInterval = 15e3;
|
|
811
|
+
this.staleTime = 5 * 60 * 1e3;
|
|
812
|
+
this.pollTimer = null;
|
|
813
|
+
this.loading = false;
|
|
814
|
+
}
|
|
815
|
+
start() {
|
|
816
|
+
this._loadPrices();
|
|
817
|
+
this.pollTimer = setInterval(() => {
|
|
818
|
+
this._loadPrices();
|
|
819
|
+
}, this.refreshInterval);
|
|
820
|
+
}
|
|
821
|
+
stop() {
|
|
822
|
+
if (this.pollTimer) {
|
|
823
|
+
clearInterval(this.pollTimer);
|
|
824
|
+
this.pollTimer = null;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
isStale(timestamp) {
|
|
828
|
+
return Date.now() - timestamp.getTime() > this.staleTime;
|
|
829
|
+
}
|
|
830
|
+
hasPrice(tokenSymbol) {
|
|
831
|
+
const info = this.prices[tokenSymbol];
|
|
832
|
+
return !!info && !this.isStale(info.timestamp);
|
|
833
|
+
}
|
|
834
|
+
async getPrice(tokenSymbol) {
|
|
835
|
+
const info = this.prices[tokenSymbol];
|
|
836
|
+
if (!info) {
|
|
837
|
+
throw new Error(`AvnuApi: price of ${tokenSymbol} not found`);
|
|
838
|
+
}
|
|
839
|
+
if (this.isStale(info.timestamp)) {
|
|
840
|
+
throw new Error(`AvnuApi: price of ${tokenSymbol} is stale`);
|
|
841
|
+
}
|
|
842
|
+
return info;
|
|
843
|
+
}
|
|
844
|
+
async _loadPrices() {
|
|
845
|
+
if (this.loading) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
this.loading = true;
|
|
849
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
850
|
+
try {
|
|
851
|
+
const result = await axios2.get(AVNU_TOKENS_API);
|
|
852
|
+
const priceByAddress = /* @__PURE__ */ new Map();
|
|
853
|
+
for (const entry of result.data) {
|
|
854
|
+
const usd = entry.starknet?.usd;
|
|
855
|
+
if (usd != null && usd > 0) {
|
|
856
|
+
priceByAddress.set(ContractAddr.standardise(entry.address), usd);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
for (const token of this.tokens) {
|
|
860
|
+
if (token.symbol === "USDT" || token.symbol === "USDC") {
|
|
861
|
+
this.prices[token.symbol] = { price: 1, timestamp };
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
const targetToken = token.priceProxySymbol ? this.tokens.find((t) => t.symbol === token.priceProxySymbol) : token;
|
|
865
|
+
if (!targetToken) {
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
const addr = targetToken.address.address;
|
|
869
|
+
const price = priceByAddress.get(addr);
|
|
870
|
+
if (price != null) {
|
|
871
|
+
this.prices[token.symbol] = { price, timestamp };
|
|
872
|
+
logger.verbose(
|
|
873
|
+
`AvnuApi: ${token.symbol} -> $${price}`
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
} catch (error) {
|
|
878
|
+
logger.warn(`AvnuApi: failed to fetch tokens: ${error?.message ?? error}`);
|
|
879
|
+
} finally {
|
|
880
|
+
this.loading = false;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
|
|
798
885
|
// src/modules/pricer.ts
|
|
886
|
+
var PRICE_METHOD_PRIORITY = [
|
|
887
|
+
"AvnuApi",
|
|
888
|
+
"Coinbase",
|
|
889
|
+
"Coinmarketcap",
|
|
890
|
+
"Ekubo",
|
|
891
|
+
"Avnu"
|
|
892
|
+
];
|
|
799
893
|
var Pricer = class extends PricerBase {
|
|
800
894
|
// e.g. ETH/USDC
|
|
801
895
|
constructor(config, tokens2, refreshInterval = 3e4, staleTime = 6e4) {
|
|
@@ -814,6 +908,7 @@ var Pricer = class extends PricerBase {
|
|
|
814
908
|
this.EKUBO_API = "https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
|
|
815
909
|
this.refreshInterval = refreshInterval;
|
|
816
910
|
this.staleTime = staleTime;
|
|
911
|
+
this.avnuApiPricer = new PricerAvnuApi(config, tokens2);
|
|
817
912
|
}
|
|
818
913
|
isReady() {
|
|
819
914
|
const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
|
|
@@ -843,6 +938,7 @@ var Pricer = class extends PricerBase {
|
|
|
843
938
|
});
|
|
844
939
|
}
|
|
845
940
|
start() {
|
|
941
|
+
this.avnuApiPricer.start();
|
|
846
942
|
this._loadPrices();
|
|
847
943
|
setInterval(() => {
|
|
848
944
|
this._loadPrices();
|
|
@@ -867,6 +963,9 @@ var Pricer = class extends PricerBase {
|
|
|
867
963
|
let retry = 0;
|
|
868
964
|
while (retry < MAX_RETRIES) {
|
|
869
965
|
try {
|
|
966
|
+
if (token.dontPrice) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
870
969
|
if (token.symbol === "USDT" || token.symbol === "USDC") {
|
|
871
970
|
this.prices[token.symbol] = {
|
|
872
971
|
price: 1,
|
|
@@ -909,60 +1008,61 @@ var Pricer = class extends PricerBase {
|
|
|
909
1008
|
});
|
|
910
1009
|
if (this.isReady() && this.config.heartbeatUrl) {
|
|
911
1010
|
console.log(`sending beat`);
|
|
912
|
-
|
|
1011
|
+
axios3.get(this.config.heartbeatUrl).catch((err) => {
|
|
913
1012
|
console.error("Pricer: Heartbeat err", err);
|
|
914
1013
|
});
|
|
915
1014
|
}
|
|
916
1015
|
}
|
|
917
|
-
async _getPrice(token
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
1016
|
+
async _getPrice(token) {
|
|
1017
|
+
const pinned = this.methodToUse[token.symbol];
|
|
1018
|
+
if (pinned) {
|
|
1019
|
+
logger.verbose(`Fetching price of ${token.symbol} using pinned ${pinned}`);
|
|
1020
|
+
try {
|
|
1021
|
+
return await this._tryPriceMethod(token, pinned);
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
console.warn(`${pinned}: pinned price failed [${token.symbol}]: `, error.message);
|
|
1024
|
+
delete this.methodToUse[token.symbol];
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
for (const method of PRICE_METHOD_PRIORITY) {
|
|
1028
|
+
logger.verbose(`Fetching price of ${token.symbol} using ${method}`);
|
|
1029
|
+
try {
|
|
1030
|
+
const result = await this._tryPriceMethod(token, method);
|
|
1031
|
+
this.methodToUse[token.symbol] = method;
|
|
1032
|
+
return result;
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
console.warn(`${method}: price err [${token.symbol}]: `, error.message);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
throw new FatalError(`Price not found for ${token.symbol}`);
|
|
1038
|
+
}
|
|
1039
|
+
async _tryPriceMethod(token, method) {
|
|
1040
|
+
switch (method) {
|
|
1041
|
+
case "AvnuApi":
|
|
1042
|
+
return await this._getPriceAvnuApi(token);
|
|
921
1043
|
case "Coinbase":
|
|
922
|
-
|
|
923
|
-
// const result = await this._getPriceCoinbase(token);
|
|
924
|
-
// this.methodToUse[token.symbol] = 'Coinbase';
|
|
925
|
-
// return result;
|
|
926
|
-
// } catch (error: any) {
|
|
927
|
-
// console.warn(`Coinbase: price err: message [${token.symbol}]: `, error.message);
|
|
928
|
-
// // do nothing, try next
|
|
929
|
-
// }
|
|
1044
|
+
return await this._getPriceCoinbase(token);
|
|
930
1045
|
case "Coinmarketcap":
|
|
931
|
-
|
|
932
|
-
const result = await this._getPriceCoinMarketCap(token);
|
|
933
|
-
this.methodToUse[token.symbol] = "Coinmarketcap";
|
|
934
|
-
return result;
|
|
935
|
-
} catch (error) {
|
|
936
|
-
console.warn(`CoinMarketCap: price err [${token.symbol}]: `, Object.keys(error));
|
|
937
|
-
console.warn(`CoinMarketCap: price err [${token.symbol}]: `, error.message);
|
|
938
|
-
}
|
|
1046
|
+
return await this._getPriceCoinMarketCap(token);
|
|
939
1047
|
case "Ekubo":
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
} catch (error) {
|
|
945
|
-
console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
|
|
946
|
-
console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
|
|
947
|
-
}
|
|
1048
|
+
return await this._getPriceEkubo(
|
|
1049
|
+
token,
|
|
1050
|
+
new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals)
|
|
1051
|
+
);
|
|
948
1052
|
case "Avnu":
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
} catch (error) {
|
|
954
|
-
console.warn(`Avnu: price err [${token.symbol}]: `, error.message);
|
|
955
|
-
console.warn(`Avnu: price err [${token.symbol}]: `, Object.keys(error));
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
if (defaultMethod == "all") {
|
|
959
|
-
return await this._getPrice(token, "Coinbase");
|
|
1053
|
+
return await this._getAvnuPrice(
|
|
1054
|
+
token,
|
|
1055
|
+
new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals)
|
|
1056
|
+
);
|
|
960
1057
|
}
|
|
961
|
-
|
|
1058
|
+
}
|
|
1059
|
+
async _getPriceAvnuApi(token) {
|
|
1060
|
+
const priceInfo = await this.avnuApiPricer.getPrice(token.symbol);
|
|
1061
|
+
return priceInfo.price;
|
|
962
1062
|
}
|
|
963
1063
|
async _getPriceCoinbase(token) {
|
|
964
1064
|
const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
|
|
965
|
-
const result = await
|
|
1065
|
+
const result = await axios3.get(url);
|
|
966
1066
|
const data = result.data;
|
|
967
1067
|
return Number(data.data.amount);
|
|
968
1068
|
}
|
|
@@ -988,7 +1088,7 @@ var Pricer = class extends PricerBase {
|
|
|
988
1088
|
async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
989
1089
|
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
990
1090
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
|
|
991
|
-
const result = await
|
|
1091
|
+
const result = await axios3.get(url);
|
|
992
1092
|
const data = result.data;
|
|
993
1093
|
const multiplier = 1 / amountIn.toNumber();
|
|
994
1094
|
const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6)) * multiplier;
|
|
@@ -1134,7 +1234,7 @@ var Pragma = class extends PricerBase {
|
|
|
1134
1234
|
};
|
|
1135
1235
|
|
|
1136
1236
|
// src/modules/zkLend.ts
|
|
1137
|
-
import
|
|
1237
|
+
import axios4 from "axios";
|
|
1138
1238
|
|
|
1139
1239
|
// src/dataTypes/bignumber.browser.ts
|
|
1140
1240
|
import { uint256 as uint2564 } from "starknet";
|
|
@@ -1190,7 +1290,7 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
1190
1290
|
async init() {
|
|
1191
1291
|
try {
|
|
1192
1292
|
logger.verbose(`Initialising ${this.metadata.name}`);
|
|
1193
|
-
const result = await
|
|
1293
|
+
const result = await axios4.get(_ZkLend.POOLS_URL);
|
|
1194
1294
|
const data = result.data;
|
|
1195
1295
|
const savedTokens = await Global.getTokens();
|
|
1196
1296
|
data.forEach((pool) => {
|
|
@@ -1282,7 +1382,7 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
1282
1382
|
*/
|
|
1283
1383
|
async getPositions(user) {
|
|
1284
1384
|
const url = this.POSITION_URL.replace("{{USER_ADDR}}", user.address);
|
|
1285
|
-
const result = await
|
|
1385
|
+
const result = await axios4.get(url);
|
|
1286
1386
|
const data = result.data;
|
|
1287
1387
|
const lendingPosition = [];
|
|
1288
1388
|
logger.verbose(`${this.metadata.name}:: Positions: ${JSON.stringify(data)}`);
|
|
@@ -1315,7 +1415,7 @@ _ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
|
|
|
1315
1415
|
var ZkLend = _ZkLend;
|
|
1316
1416
|
|
|
1317
1417
|
// src/modules/pricer-from-api.ts
|
|
1318
|
-
import
|
|
1418
|
+
import axios5 from "axios";
|
|
1319
1419
|
|
|
1320
1420
|
// src/modules/apollo-client-config.ts
|
|
1321
1421
|
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
|
@@ -1697,7 +1797,7 @@ var PricerFromApi = class extends PricerBase {
|
|
|
1697
1797
|
const MAX_RETRIES = 5;
|
|
1698
1798
|
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
1699
1799
|
try {
|
|
1700
|
-
const priceInfo = await
|
|
1800
|
+
const priceInfo = await axios5.get(
|
|
1701
1801
|
`https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`
|
|
1702
1802
|
);
|
|
1703
1803
|
if (!priceInfo) {
|
|
@@ -2981,7 +3081,7 @@ var ERC20 = class {
|
|
|
2981
3081
|
};
|
|
2982
3082
|
|
|
2983
3083
|
// src/modules/ekubo-quoter.ts
|
|
2984
|
-
import
|
|
3084
|
+
import axios6 from "axios";
|
|
2985
3085
|
var EkuboQuoter = class {
|
|
2986
3086
|
// e.g. ETH/USDC'
|
|
2987
3087
|
constructor(config) {
|
|
@@ -2999,7 +3099,7 @@ var EkuboQuoter = class {
|
|
|
2999
3099
|
let _fromToken = amount.gt(0) ? fromToken : toToken;
|
|
3000
3100
|
let _toToken = amount.gt(0) ? toToken : fromToken;
|
|
3001
3101
|
try {
|
|
3002
|
-
const quote = await
|
|
3102
|
+
const quote = await axios6.get(this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", _fromToken).replace("{{TOKEN_TO_ADDRESS}}", _toToken));
|
|
3003
3103
|
console.log(`Ekubo quote from ${_fromToken} to ${_toToken} for ${amount.toString()}: ${JSON.stringify(quote.data)}`);
|
|
3004
3104
|
return quote.data;
|
|
3005
3105
|
} catch (error) {
|
|
@@ -4901,9 +5001,9 @@ var BaseStrategy = class extends CacheClass {
|
|
|
4901
5001
|
};
|
|
4902
5002
|
|
|
4903
5003
|
// src/node/headless.browser.ts
|
|
4904
|
-
import
|
|
5004
|
+
import axios7 from "axios";
|
|
4905
5005
|
async function getAPIUsingHeadlessBrowser(url) {
|
|
4906
|
-
const res = await
|
|
5006
|
+
const res = await axios7.get(url);
|
|
4907
5007
|
return res.data;
|
|
4908
5008
|
}
|
|
4909
5009
|
|
|
@@ -16832,7 +16932,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16832
16932
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
16833
16933
|
const token1Usd = Number(amount1.toFixed(13)) * P1.price;
|
|
16834
16934
|
const totalUsdValue = token0Usd + token1Usd;
|
|
16835
|
-
if (totalUsdValue === 0 || token0Usd === 0 || token1Usd === 0 || amount0.eq(0) || amount1.eq(0)) {
|
|
16935
|
+
if ((totalUsdValue === 0 || token0Usd === 0 || token1Usd === 0 || amount0.eq(0) || amount1.eq(0)) && this.metadata.settings?.liveStatus === "Active" /* ACTIVE */) {
|
|
16836
16936
|
logger.warn(
|
|
16837
16937
|
`${this.metadata.name}:getTVL - Zero value detected: usdValue=${totalUsdValue}, amount0=${amount0.toString()}, amount1=${amount1.toString()}, token0Price=${P0.price}, token1Price=${P1.price}, token0Usd=${token0Usd}, token1Usd=${token1Usd}`
|
|
16838
16938
|
);
|
|
@@ -29057,7 +29157,7 @@ var YoLoVault = class extends BaseStrategy {
|
|
|
29057
29157
|
sDec
|
|
29058
29158
|
).multipliedBy(secondaryTokenPrice.price);
|
|
29059
29159
|
const totalUsdValue = primaryTokenUsd.plus(secondaryTokenUsd).toNumber();
|
|
29060
|
-
if (totalUsdValue === 0 || primaryTokenAmount.eq(0) || secondaryTokenAmount.eq(0)) {
|
|
29160
|
+
if ((totalUsdValue === 0 || primaryTokenAmount.eq(0) || secondaryTokenAmount.eq(0)) && this.metadata.settings?.liveStatus === "Active" /* ACTIVE */) {
|
|
29061
29161
|
logger.warn(
|
|
29062
29162
|
`${this.metadata.name}:getTVL - Zero value detected: usdValue=${totalUsdValue}, primaryTokenAmount=${primaryTokenAmount.toString()}, secondaryTokenAmount=${secondaryTokenAmount.toString()}, primaryTokenPrice=${primaryTokenPrice.price}, secondaryTokenPrice=${secondaryTokenPrice.price}, primaryTokenUsd=${primaryTokenUsd.toNumber()}, secondaryTokenUsd=${secondaryTokenUsd.toNumber()}`
|
|
29063
29163
|
);
|
|
@@ -32416,7 +32516,7 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
|
|
|
32416
32516
|
this.metadata.depositTokens[0].symbol
|
|
32417
32517
|
);
|
|
32418
32518
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
32419
|
-
if (usdValue === 0 || amount.eq(0)) {
|
|
32519
|
+
if ((usdValue === 0 || amount.eq(0)) && this.metadata.settings?.liveStatus === "Active" /* ACTIVE */) {
|
|
32420
32520
|
logger.warn(
|
|
32421
32521
|
`${this.metadata.name}:getTVL - Zero value detected: usdValue=${usdValue}, amount=${amount.toString()}, price=${price.price}`
|
|
32422
32522
|
);
|
|
@@ -35155,7 +35255,7 @@ function createStrategy(type, config, pricer, metadata) {
|
|
|
35155
35255
|
}
|
|
35156
35256
|
|
|
35157
35257
|
// src/modules/pricer-lst.ts
|
|
35158
|
-
import
|
|
35258
|
+
import axios8 from "axios";
|
|
35159
35259
|
var PricerLST2 = class extends Pricer {
|
|
35160
35260
|
// e.g. xSTRK/STRK
|
|
35161
35261
|
constructor(config, tokenMaps) {
|
|
@@ -35191,7 +35291,7 @@ var PricerLST2 = class extends Pricer {
|
|
|
35191
35291
|
async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
35192
35292
|
const underlying = this.getUnderlying(token);
|
|
35193
35293
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei()).replace("{{UNDERLYING_ADDRESS}}", underlying.address.toString());
|
|
35194
|
-
const result = await
|
|
35294
|
+
const result = await axios8.get(url);
|
|
35195
35295
|
const data = result.data;
|
|
35196
35296
|
const multiplier = 1 / amountIn.toNumber();
|
|
35197
35297
|
const outputUnderlying = Number(Web3Number.fromWei(data.total_calculated, underlying.decimals).toFixed(6)) * multiplier;
|
|
@@ -35343,6 +35443,7 @@ var PricerRedis = class extends Pricer {
|
|
|
35343
35443
|
async startWithRedis(redisUrl) {
|
|
35344
35444
|
await this.initRedis(redisUrl);
|
|
35345
35445
|
logger.info(`Starting Pricer with Redis`);
|
|
35446
|
+
this.avnuApiPricer.start();
|
|
35346
35447
|
this._loadPrices(this._setRedisPrices.bind(this));
|
|
35347
35448
|
setInterval(() => {
|
|
35348
35449
|
this._loadPrices(this._setRedisPrices.bind(this));
|
|
@@ -35776,6 +35877,7 @@ export {
|
|
|
35776
35877
|
PasswordJsonCryptoUtil,
|
|
35777
35878
|
Pragma,
|
|
35778
35879
|
Pricer,
|
|
35880
|
+
PricerAvnuApi,
|
|
35779
35881
|
PricerFromApi,
|
|
35780
35882
|
PricerLST2 as PricerLST,
|
|
35781
35883
|
PricerRedis,
|
package/package.json
CHANGED
package/src/global.ts
CHANGED
|
@@ -156,6 +156,7 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
156
156
|
coingeckId: undefined,
|
|
157
157
|
displayDecimals: 6,
|
|
158
158
|
priceCheckAmount: 0.0001, // 112000 * 0.0001 = $11.2
|
|
159
|
+
dontPrice: true,
|
|
159
160
|
}, {
|
|
160
161
|
name: 'mRe7YIELD',
|
|
161
162
|
symbol: 'mRe7YIELD',
|
|
@@ -165,6 +166,7 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
165
166
|
coingeckId: undefined,
|
|
166
167
|
displayDecimals: 2,
|
|
167
168
|
priceCheckAmount: 100,
|
|
169
|
+
dontPrice: true,
|
|
168
170
|
}, {
|
|
169
171
|
name: "fyeWBTC",
|
|
170
172
|
symbol: "fyeWBTC",
|
|
@@ -174,6 +176,7 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
174
176
|
coingeckId: undefined,
|
|
175
177
|
displayDecimals: 6,
|
|
176
178
|
priceCheckAmount: 0.001, // 112000 * 0.0001 = $110.2
|
|
179
|
+
dontPrice: true,
|
|
177
180
|
}, {
|
|
178
181
|
name: "fyETH",
|
|
179
182
|
symbol: "fyETH",
|
|
@@ -183,6 +186,7 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
183
186
|
coingeckId: undefined,
|
|
184
187
|
displayDecimals: 4,
|
|
185
188
|
priceCheckAmount: 0.1,
|
|
189
|
+
dontPrice: true,
|
|
186
190
|
}, {
|
|
187
191
|
name: "fyeUSDC",
|
|
188
192
|
symbol: "fyeUSDC",
|
|
@@ -192,6 +196,7 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
192
196
|
coingeckId: undefined,
|
|
193
197
|
displayDecimals: 2,
|
|
194
198
|
priceCheckAmount: 100,
|
|
199
|
+
dontPrice: true,
|
|
195
200
|
}, {
|
|
196
201
|
name: 'strkBTC',
|
|
197
202
|
symbol: 'strkBTC',
|
|
@@ -35,6 +35,7 @@ export interface TokenInfo {
|
|
|
35
35
|
displayDecimals: number;
|
|
36
36
|
priceProxySymbol?: string; // for tokens like illiquid tokens, we use a proxy symbol to get the price
|
|
37
37
|
priceCheckAmount?: number; // for tokens like BTC, doing 1BTC price check may not be ideal, esp on illiquid netwrks like sn
|
|
38
|
+
dontPrice?: boolean; // a flag that skips pricer to check these tokens.
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
export enum Network {
|
package/src/modules/index.ts
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { TokenInfo } from "@/interfaces/common";
|
|
3
|
+
import { IConfig } from "@/interfaces/common";
|
|
4
|
+
import { PricerBase } from "./pricerBase";
|
|
5
|
+
import { PriceInfo } from "./pricer";
|
|
6
|
+
import { logger } from "@/utils/logger";
|
|
7
|
+
import { ContractAddr } from "@/dataTypes";
|
|
8
|
+
|
|
9
|
+
const AVNU_TOKENS_API = "https://starknet.impulse.avnu.fi/v3/tokens";
|
|
10
|
+
|
|
11
|
+
interface AvnuTokenApiEntry {
|
|
12
|
+
address: string;
|
|
13
|
+
symbol: string;
|
|
14
|
+
starknet?: {
|
|
15
|
+
usd?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Polls Avnu impulse tokens API and keeps USD prices in memory for configured tokens.
|
|
21
|
+
* Price timestamp is set when each poll request completes.
|
|
22
|
+
*/
|
|
23
|
+
export class PricerAvnuApi extends PricerBase {
|
|
24
|
+
protected prices: { [key: string]: PriceInfo } = {};
|
|
25
|
+
|
|
26
|
+
readonly refreshInterval = 15_000;
|
|
27
|
+
readonly staleTime = 5 * 60 * 1000;
|
|
28
|
+
|
|
29
|
+
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
30
|
+
private loading = false;
|
|
31
|
+
|
|
32
|
+
constructor(config: IConfig, tokens: TokenInfo[]) {
|
|
33
|
+
super(config, tokens);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
start() {
|
|
37
|
+
this._loadPrices();
|
|
38
|
+
this.pollTimer = setInterval(() => {
|
|
39
|
+
this._loadPrices();
|
|
40
|
+
}, this.refreshInterval);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stop() {
|
|
44
|
+
if (this.pollTimer) {
|
|
45
|
+
clearInterval(this.pollTimer);
|
|
46
|
+
this.pollTimer = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
isStale(timestamp: Date) {
|
|
51
|
+
return Date.now() - timestamp.getTime() > this.staleTime;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
hasPrice(tokenSymbol: string) {
|
|
55
|
+
const info = this.prices[tokenSymbol];
|
|
56
|
+
return !!info && !this.isStale(info.timestamp);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getPrice(tokenSymbol: string): Promise<PriceInfo> {
|
|
60
|
+
const info = this.prices[tokenSymbol];
|
|
61
|
+
if (!info) {
|
|
62
|
+
throw new Error(`AvnuApi: price of ${tokenSymbol} not found`);
|
|
63
|
+
}
|
|
64
|
+
if (this.isStale(info.timestamp)) {
|
|
65
|
+
throw new Error(`AvnuApi: price of ${tokenSymbol} is stale`);
|
|
66
|
+
}
|
|
67
|
+
return info;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
protected async _loadPrices() {
|
|
71
|
+
if (this.loading) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.loading = true;
|
|
75
|
+
const timestamp = new Date();
|
|
76
|
+
try {
|
|
77
|
+
const result = await axios.get<AvnuTokenApiEntry[]>(AVNU_TOKENS_API);
|
|
78
|
+
const priceByAddress = new Map<string, number>();
|
|
79
|
+
for (const entry of result.data) {
|
|
80
|
+
const usd = entry.starknet?.usd;
|
|
81
|
+
if (usd != null && usd > 0) {
|
|
82
|
+
priceByAddress.set(ContractAddr.standardise(entry.address), usd);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const token of this.tokens) {
|
|
87
|
+
if (token.symbol === "USDT" || token.symbol === "USDC") {
|
|
88
|
+
this.prices[token.symbol] = { price: 1, timestamp };
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const targetToken = token.priceProxySymbol
|
|
93
|
+
? this.tokens.find((t) => t.symbol === token.priceProxySymbol)
|
|
94
|
+
: token;
|
|
95
|
+
if (!targetToken) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const addr = targetToken.address.address;
|
|
100
|
+
const price = priceByAddress.get(addr);
|
|
101
|
+
if (price != null) {
|
|
102
|
+
this.prices[token.symbol] = { price, timestamp };
|
|
103
|
+
logger.verbose(
|
|
104
|
+
`AvnuApi: ${token.symbol} -> $${price}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (error: any) {
|
|
109
|
+
logger.warn(`AvnuApi: failed to fetch tokens: ${error?.message ?? error}`);
|
|
110
|
+
} finally {
|
|
111
|
+
this.loading = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|