@strkfarm/sdk 1.0.28 → 1.0.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
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",
@@ -1,3 +1,4 @@
1
+ import { logger } from "@/global";
1
2
  import BigNumber from "bignumber.js";
2
3
 
3
4
  export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
@@ -13,22 +14,22 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
13
14
  }
14
15
 
15
16
  multipliedBy(value: string | number | T): T {
16
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
17
+ const _value = this.getStandardString(value);
17
18
  return this.construct(this.mul(_value).toString(), this.decimals);
18
19
  }
19
20
 
20
21
  dividedBy(value: string | number | T): T {
21
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
22
+ const _value = this.getStandardString(value);
22
23
  return this.construct(this.div(_value).toString(), this.decimals);
23
24
  }
24
25
 
25
26
  plus(value: string | number | T): T {
26
- const _value = Number(value).toFixed(this.maxToFixedDecimals());
27
+ const _value = this.getStandardString(value);
27
28
  return this.construct(this.add(_value).toString(), this.decimals);
28
29
  }
29
30
 
30
31
  minus(n: number | string | T, base?: number): T {
31
- const _value = Number(n).toFixed(this.maxToFixedDecimals());
32
+ const _value = this.getStandardString(n);
32
33
  return this.construct(super.minus(_value, base).toString(), this.decimals);
33
34
  }
34
35
 
@@ -49,9 +50,16 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
49
50
  }
50
51
 
51
52
  private maxToFixedDecimals() {
52
- return Math.min(this.decimals, 13);
53
+ return Math.min(this.decimals, 18);
54
+ }
55
+
56
+ private getStandardString(value: string | number | T): string {
57
+ if (typeof value == 'string') {
58
+ return value
59
+ }
60
+ return value.toFixed(this.maxToFixedDecimals())
53
61
  }
54
62
  }
55
63
 
56
- BigNumber.config({ DECIMAL_PLACES: 18 })
57
- _Web3Number.config({ DECIMAL_PLACES: 18 })
64
+ BigNumber.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN })
65
+ _Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN })
package/src/global.ts CHANGED
@@ -52,42 +52,48 @@ const defaultTokens: TokenInfo[] = [{
52
52
  logo: 'https://assets.coingecko.com/coins/images/26433/small/starknet.png',
53
53
  address: ContractAddr.from('0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d'),
54
54
  decimals: 18,
55
- coingeckId: 'starknet'
55
+ coingeckId: 'starknet',
56
+ displayDecimals: 2,
56
57
  }, {
57
58
  name: 'xSTRK',
58
59
  symbol: 'xSTRK',
59
60
  logo: 'https://dashboard.endur.fi/endur-fi.svg',
60
61
  address: ContractAddr.from('0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a'),
61
62
  decimals: 18,
62
- coingeckId: undefined
63
+ coingeckId: undefined,
64
+ displayDecimals: 2,
63
65
  }, {
64
66
  name: 'ETH',
65
67
  symbol: 'ETH',
66
68
  logo: 'https://opbnb.bscscan.com/token/images/ether.svg',
67
69
  address: ContractAddr.from('0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'),
68
70
  decimals: 18,
69
- coingeckId: undefined
71
+ coingeckId: undefined,
72
+ displayDecimals: 4,
70
73
  }, {
71
74
  name: 'USDC',
72
75
  symbol: 'USDC',
73
76
  logo: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png',
74
77
  address: ContractAddr.from('0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'),
75
78
  decimals: 6,
76
- coingeckId: undefined
79
+ coingeckId: undefined,
80
+ displayDecimals: 2,
77
81
  }, {
78
82
  name: 'USDT',
79
83
  symbol: 'USDT',
80
84
  logo: 'https://assets.coingecko.com/coins/images/325/small/Tether.png',
81
85
  address: ContractAddr.from('0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8'),
82
86
  decimals: 6,
83
- coingeckId: undefined
87
+ coingeckId: undefined,
88
+ displayDecimals: 2,
84
89
  }, {
85
90
  name: 'WBTC',
86
91
  symbol: 'WBTC',
87
92
  logo: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png',
88
93
  address: ContractAddr.from('0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac'),
89
94
  decimals: 8,
90
- coingeckId: undefined
95
+ coingeckId: undefined,
96
+ displayDecimals: 6,
91
97
  }]
92
98
  const tokens: TokenInfo[] = defaultTokens;
93
99
 
@@ -150,6 +156,7 @@ export class Global {
150
156
  decimals: token.decimals,
151
157
  logo: token.logoUri,
152
158
  coingeckId: token.extensions.coingeckoId,
159
+ displayDecimals: 2
153
160
  });
154
161
  });
155
162
  console.log(tokens);
@@ -29,6 +29,7 @@ export interface TokenInfo {
29
29
  decimals: number,
30
30
  logo: string,
31
31
  coingeckId?: string,
32
+ displayDecimals: number
32
33
  }
33
34
 
34
35
  export enum Network {
@@ -73,10 +74,12 @@ export interface IStrategyMetadata<T> {
73
74
  netRisk: number,
74
75
  notARisks: string[]
75
76
  },
77
+ apyMethodology?: string,
76
78
  additionalInfo: T
77
79
  }
78
80
 
79
81
  export interface IInvestmentFlow {
82
+ id?: string, // used to link flows
80
83
  title: string,
81
84
  subItems: {key: string, value: string}[],
82
85
  linkedFlows: IInvestmentFlow[],
@@ -3,6 +3,7 @@ import { uint256 } from "starknet";
3
3
  import { Call, Uint256 } from "starknet";
4
4
  import { fetchBuildExecuteTransaction, fetchQuotes, Quote } from "@avnu/avnu-sdk";
5
5
  import { assert } from "../utils";
6
+ import { logger } from "@/global";
6
7
 
7
8
  export interface Route {
8
9
  token_from: string,
@@ -26,68 +27,82 @@ export interface SwapInfo {
26
27
 
27
28
 
28
29
  export class AvnuWrapper {
29
- async getQuotes(
30
- fromToken: string,
31
- toToken: string,
32
- amountWei: string,
33
- taker: string,
34
- ) {
35
- const params: any = {
36
- sellTokenAddress: fromToken,
37
- buyTokenAddress: toToken,
38
- sellAmount: amountWei,
39
- takerAddress: taker,
40
- };
41
- assert(fromToken != toToken, 'From and to tokens are the same');
30
+ async getQuotes(
31
+ fromToken: string,
32
+ toToken: string,
33
+ amountWei: string,
34
+ taker: string,
35
+ retry = 0
36
+ ): Promise<Quote> {
37
+ const MAX_RETRY = 5;
38
+ logger.verbose(`${AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
39
+ const params: any = {
40
+ sellTokenAddress: fromToken,
41
+ buyTokenAddress: toToken,
42
+ sellAmount: amountWei,
43
+ takerAddress: taker,
44
+ // excludeSources: ['Nostra', 'Haiko(Solvers)']
45
+ excludeSources: ['Haiko(Solvers)'] // to resolve InvalidOraclePrice error
46
+ };
47
+ assert(fromToken != toToken, "From and to tokens are the same");
42
48
 
43
- const quotes = await fetchQuotes(params);
44
- assert(quotes.length > 0, 'No quotes found');
45
- return quotes[0];
49
+ const quotes = await fetchQuotes(params);
50
+ if (quotes.length == 0) {
51
+ if (retry < MAX_RETRY) {
52
+ await new Promise((res) => setTimeout(res, 3000))
53
+ return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
54
+ }
55
+ throw new Error('no quotes found')
46
56
  }
57
+ return quotes[0];
58
+ }
47
59
 
48
- async getSwapInfo(
49
- quote: Quote,
50
- taker: string,
51
- integratorFeeBps: number,
52
- integratorFeeRecipient: string,
53
- minAmount: string
54
- ) {
55
- const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
56
- // its the multi swap function call
57
- const call: Call = calldata.calls[1];
58
- const callData: string[] = call.calldata as string[];
59
- const routesLen: number = Number(callData[11]);
60
- assert(routesLen > 0, 'No routes found');
61
-
62
- // use call data to re-construct routes
63
- let startIndex = 12;
64
- const routes: Route[] = [];
65
- for(let i=0; i<routesLen; ++i) {
66
- const swap_params_len = Number(callData[startIndex + 4]);
67
- const route: Route = {
68
- token_from: callData[startIndex],
69
- token_to: callData[startIndex + 1],
70
- exchange_address: callData[startIndex + 2],
71
- percent: Number(callData[startIndex + 3]),
72
- additional_swap_params: swap_params_len > 0 ? callData.slice(startIndex + 5, startIndex + 5 + swap_params_len): []
73
- }
74
- routes.push(route);
75
- startIndex += 5 + swap_params_len;
76
- }
60
+ async getSwapInfo(
61
+ quote: Quote,
62
+ taker: string,
63
+ integratorFeeBps: number,
64
+ integratorFeeRecipient: string,
65
+ minAmount: string
66
+ ) {
67
+ const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
68
+ // its the multi swap function call
69
+ const call: Call = calldata.calls[1];
70
+ const callData: string[] = call.calldata as string[];
71
+ const routesLen: number = Number(callData[11]);
72
+ assert(routesLen > 0, "No routes found");
77
73
 
78
- // swapInfo as expected by the strategy
79
- const swapInfo: SwapInfo = {
80
- token_from_address: quote.sellTokenAddress,
81
- token_from_amount: uint256.bnToUint256(quote.sellAmount),
82
- token_to_address: quote.buyTokenAddress,
83
- token_to_amount: uint256.bnToUint256(quote.buyAmount),
84
- token_to_min_amount: uint256.bnToUint256(minAmount),
85
- beneficiary: taker,
86
- integrator_fee_amount_bps: integratorFeeBps,
87
- integrator_fee_recipient: integratorFeeRecipient,
88
- routes
89
- };
90
-
91
- return swapInfo;
74
+ // use call data to re-construct routes
75
+ let startIndex = 12;
76
+ const routes: Route[] = [];
77
+ for (let i = 0; i < routesLen; ++i) {
78
+ const swap_params_len = Number(callData[startIndex + 4]);
79
+ const route: Route = {
80
+ token_from: callData[startIndex],
81
+ token_to: callData[startIndex + 1],
82
+ exchange_address: callData[startIndex + 2],
83
+ percent: Number(callData[startIndex + 3]),
84
+ additional_swap_params:
85
+ swap_params_len > 0
86
+ ? callData.slice(startIndex + 5, startIndex + 5 + swap_params_len)
87
+ : [],
88
+ };
89
+ routes.push(route);
90
+ startIndex += 5 + swap_params_len;
92
91
  }
92
+
93
+ // swapInfo as expected by the strategy
94
+ const swapInfo: SwapInfo = {
95
+ token_from_address: quote.sellTokenAddress,
96
+ token_from_amount: uint256.bnToUint256(quote.sellAmount),
97
+ token_to_address: quote.buyTokenAddress,
98
+ token_to_amount: uint256.bnToUint256(quote.buyAmount),
99
+ token_to_min_amount: uint256.bnToUint256(minAmount),
100
+ beneficiary: taker,
101
+ integrator_fee_amount_bps: integratorFeeBps,
102
+ integrator_fee_recipient: integratorFeeRecipient,
103
+ routes,
104
+ };
105
+
106
+ return swapInfo;
107
+ }
93
108
  }
@@ -0,0 +1,74 @@
1
+ import { ContractAddr, Web3Number } from "@/dataTypes";
2
+ import { logger } from "@/global";
3
+ import { IConfig } from "@/interfaces";
4
+ import { assert } from "@/utils";
5
+ import { Contract } from "starknet";
6
+
7
+ export interface HarvestInfo {
8
+ rewardsContract: ContractAddr,
9
+ token: ContractAddr,
10
+ startDate: Date,
11
+ endDate: Date,
12
+ claim: {
13
+ id: number,
14
+ amount: Web3Number,
15
+ claimee: ContractAddr
16
+ },
17
+ proof: string[]
18
+ }
19
+
20
+ export class Harvests {
21
+ constructor(private readonly config: IConfig) {
22
+
23
+ }
24
+
25
+ getHarvests(addr: ContractAddr): Promise<HarvestInfo[]> {
26
+ throw new Error("Not implemented");
27
+ }
28
+
29
+ async getUnHarvestedRewards(addr: ContractAddr) {
30
+ const rewards = await this.getHarvests(addr);
31
+ if (rewards.length == 0) return [];
32
+
33
+ const unClaimed: HarvestInfo[] = [];
34
+
35
+ const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
36
+ for (let reward of rewards) {
37
+ const contract = new Contract(cls.abi, reward.rewardsContract.address, this.config.provider);
38
+ const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
39
+ logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
40
+ if (isClaimed)
41
+ return unClaimed;
42
+ unClaimed.unshift(reward); // to ensure older harvest is first
43
+ }
44
+ return unClaimed;
45
+ }
46
+ }
47
+
48
+ export class EkuboHarvests extends Harvests {
49
+ async getHarvests(addr: ContractAddr) {
50
+ const STRK = '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d';
51
+ const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`
52
+ const resultEkubo = await fetch(EKUBO_API);
53
+ const items = (await resultEkubo.json());
54
+
55
+ const rewards: HarvestInfo[] = [];
56
+ for (let i=0; i<items.length; ++i) {
57
+ const info = items[i];
58
+ assert(info.token == STRK, 'expected strk token only')
59
+ rewards.push({
60
+ rewardsContract: ContractAddr.from(info.contract_address),
61
+ token: ContractAddr.from(STRK),
62
+ startDate: new Date(info.start_date),
63
+ endDate: new Date(info.end_date),
64
+ claim: {
65
+ id: info.claim.id,
66
+ amount: Web3Number.fromWei(info.claim.amount, 18),
67
+ claimee: ContractAddr.from(info.claim.claimee)
68
+ },
69
+ proof: info.proof
70
+ });
71
+ }
72
+ return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
73
+ }
74
+ }
@@ -12,8 +12,8 @@ export class PricerFromApi extends PricerBase {
12
12
  async getPrice(tokenSymbol: string): Promise<PriceInfo> {
13
13
  try {
14
14
  return await this.getPriceFromMyAPI(tokenSymbol);
15
- } catch (e) {
16
- logger.warn('getPriceFromMyAPI error', e);
15
+ } catch (e: any) {
16
+ logger.warn('getPriceFromMyAPI error', JSON.stringify(e.message || e));
17
17
  }
18
18
  logger.log('getPrice coinbase', tokenSymbol);
19
19
  let retry = 0;
@@ -32,8 +32,8 @@ export class PricerFromApi extends PricerBase {
32
32
  price,
33
33
  timestamp: new Date()
34
34
  }
35
- } catch (e) {
36
- logger.warn('getPrice coinbase error', e, retry);
35
+ } catch (e: any) {
36
+ logger.warn('getPrice coinbase error', JSON.stringify(e.message || e));
37
37
  await new Promise((resolve) => setTimeout(resolve, retry * 1000));
38
38
  }
39
39
  }
@@ -48,10 +48,11 @@ export class PricerFromApi extends PricerBase {
48
48
  const priceInfo = await priceInfoRes.json();
49
49
  const now = new Date();
50
50
  const priceTime = new Date(priceInfo.timestamp);
51
- if (now.getTime() - priceTime.getTime() > 900000) {
52
- // 15 mins
53
- throw new Error('Price is stale');
54
- }
51
+ // if (now.getTime() - priceTime.getTime() > 900000) {
52
+ // // 15 mins
53
+ // logger.verbose(`Price is stale: ${tokenSymbol}, timestamp: ${priceInfo.timestamp}, price: ${priceInfo.price}`);
54
+ // throw new Error('Price is stale');
55
+ // }
55
56
  const price = Number(priceInfo.price);
56
57
  return {
57
58
  price,
@@ -40,7 +40,8 @@ export class ZkLend extends ILending implements ILending {
40
40
  logo: '',
41
41
  decimals: pool.token.decimals,
42
42
  borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
43
- collareralFactor
43
+ collareralFactor,
44
+ displayDecimals: 2
44
45
  }
45
46
  this.tokens.push(token);
46
47
  });
@@ -16,7 +16,7 @@ export interface DualActionAmount {
16
16
  token1: SingleActionAmount
17
17
  }
18
18
  export interface DualTokenInfo {
19
- netUsdValue: number,
19
+ usdValue: number,
20
20
  token0: SingleTokenInfo,
21
21
  token1: SingleTokenInfo
22
22
  }
@@ -36,11 +36,11 @@ export class BaseStrategy<TVLInfo, ActionInfo> {
36
36
  throw new Error("Not implemented");
37
37
  }
38
38
 
39
- depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Call[] {
39
+ async depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Promise<Call[]> {
40
40
  throw new Error("Not implemented");
41
41
  }
42
42
 
43
- withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Call[] {
43
+ async withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
44
44
  throw new Error("Not implemented");
45
45
  }
46
46