@strkfarm/sdk 1.0.29 → 1.0.31
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/cli.js +4 -6
- package/dist/cli.mjs +7 -9
- package/dist/index.browser.global.js +9179 -64
- package/dist/index.browser.mjs +9191 -76
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9203 -88
- package/dist/index.mjs +9193 -78
- package/package.json +1 -1
- package/src/data/vesu_pools.json +9019 -0
- package/src/modules/avnu.ts +7 -3
- package/src/modules/harvests.ts +42 -5
- package/src/strategies/ekubo-cl-vault.ts +1 -1
- package/src/strategies/vesu-rebalance.ts +81 -13
package/src/modules/avnu.ts
CHANGED
|
@@ -62,7 +62,7 @@ export class AvnuWrapper {
|
|
|
62
62
|
taker: string,
|
|
63
63
|
integratorFeeBps: number,
|
|
64
64
|
integratorFeeRecipient: string,
|
|
65
|
-
minAmount
|
|
65
|
+
minAmount?: string
|
|
66
66
|
) {
|
|
67
67
|
const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
|
|
68
68
|
// its the multi swap function call
|
|
@@ -91,12 +91,16 @@ export class AvnuWrapper {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// swapInfo as expected by the strategy
|
|
94
|
+
// fallback, max 1% slippage
|
|
95
|
+
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
96
|
+
logger.verbose(`${AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
|
|
97
|
+
logger.verbose(`${AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
|
|
94
98
|
const swapInfo: SwapInfo = {
|
|
95
99
|
token_from_address: quote.sellTokenAddress,
|
|
96
100
|
token_from_amount: uint256.bnToUint256(quote.sellAmount),
|
|
97
101
|
token_to_address: quote.buyTokenAddress,
|
|
98
|
-
token_to_amount: uint256.bnToUint256(
|
|
99
|
-
token_to_min_amount: uint256.bnToUint256(
|
|
102
|
+
token_to_amount: uint256.bnToUint256(_minAmount),
|
|
103
|
+
token_to_min_amount: uint256.bnToUint256(_minAmount),
|
|
100
104
|
beneficiary: taker,
|
|
101
105
|
integrator_fee_amount_bps: integratorFeeBps,
|
|
102
106
|
integrator_fee_recipient: integratorFeeRecipient,
|
package/src/modules/harvests.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
|
2
2
|
import { logger } from "@/global";
|
|
3
3
|
import { IConfig } from "@/interfaces";
|
|
4
4
|
import { assert } from "@/utils";
|
|
5
|
-
import { Contract } from "starknet";
|
|
5
|
+
import { Contract, num } from "starknet";
|
|
6
6
|
|
|
7
7
|
export interface HarvestInfo {
|
|
8
8
|
rewardsContract: ContractAddr,
|
|
@@ -14,13 +14,12 @@ export interface HarvestInfo {
|
|
|
14
14
|
amount: Web3Number,
|
|
15
15
|
claimee: ContractAddr
|
|
16
16
|
},
|
|
17
|
+
actualReward: Web3Number,
|
|
17
18
|
proof: string[]
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export class Harvests {
|
|
21
|
-
constructor(
|
|
22
|
-
|
|
23
|
-
}
|
|
22
|
+
constructor(protected readonly config: IConfig) {}
|
|
24
23
|
|
|
25
24
|
getHarvests(addr: ContractAddr): Promise<HarvestInfo[]> {
|
|
26
25
|
throw new Error("Not implemented");
|
|
@@ -45,9 +44,10 @@ export class Harvests {
|
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
const STRK = '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d';
|
|
48
|
+
|
|
48
49
|
export class EkuboHarvests extends Harvests {
|
|
49
50
|
async getHarvests(addr: ContractAddr) {
|
|
50
|
-
const STRK = '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d';
|
|
51
51
|
const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`
|
|
52
52
|
const resultEkubo = await fetch(EKUBO_API);
|
|
53
53
|
const items = (await resultEkubo.json());
|
|
@@ -66,9 +66,46 @@ export class EkuboHarvests extends Harvests {
|
|
|
66
66
|
amount: Web3Number.fromWei(info.claim.amount, 18),
|
|
67
67
|
claimee: ContractAddr.from(info.claim.claimee)
|
|
68
68
|
},
|
|
69
|
+
actualReward: Web3Number.fromWei(info.claim.amount, 18),
|
|
69
70
|
proof: info.proof
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
73
74
|
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class VesuHarvests extends Harvests {
|
|
78
|
+
async getHarvests(addr: ContractAddr): Promise<HarvestInfo[]> {
|
|
79
|
+
const result = await fetch(`https://api.vesu.xyz/users/${addr.address}/strk-rewards/calldata`);
|
|
80
|
+
const data = await result.json();
|
|
81
|
+
const rewardsContract = ContractAddr.from('0x0387f3eb1d98632fbe3440a9f1385Aec9d87b6172491d3Dd81f1c35A7c61048F');
|
|
82
|
+
|
|
83
|
+
// get already claimed amount
|
|
84
|
+
const cls = await this.config.provider.getClassAt(rewardsContract.address);
|
|
85
|
+
const contract = new Contract(cls.abi, rewardsContract.address, this.config.provider);
|
|
86
|
+
const _claimed_amount: any = await contract.call('amount_already_claimed', [addr.address]);
|
|
87
|
+
const claimed_amount = Web3Number.fromWei(_claimed_amount.toString(), 18);
|
|
88
|
+
logger.verbose(`${VesuHarvests.name}: claimed_amount: ${claimed_amount.toString()}`);
|
|
89
|
+
|
|
90
|
+
// get the actual reward
|
|
91
|
+
const actualReward = Web3Number.fromWei(data.data.amount, 18).minus(claimed_amount);
|
|
92
|
+
logger.verbose(`${VesuHarvests.name}: actualReward: ${actualReward.toString()}`);
|
|
93
|
+
return [{
|
|
94
|
+
rewardsContract,
|
|
95
|
+
token: ContractAddr.from(STRK),
|
|
96
|
+
startDate: new Date(0),
|
|
97
|
+
endDate: new Date(0),
|
|
98
|
+
claim: {
|
|
99
|
+
id: 0,
|
|
100
|
+
amount: Web3Number.fromWei(num.getDecimalString(data.data.amount), 18),
|
|
101
|
+
claimee: addr
|
|
102
|
+
},
|
|
103
|
+
actualReward,
|
|
104
|
+
proof: data.data.proof
|
|
105
|
+
}]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getUnHarvestedRewards(addr: ContractAddr) {
|
|
109
|
+
return await this.getHarvests(addr);
|
|
110
|
+
}
|
|
74
111
|
}
|
|
@@ -275,7 +275,7 @@ export class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
async _getTVL(blockIdentifier: BlockIdentifier = 'pending') {
|
|
279
279
|
const result = await this.contract.call('total_liquidity', [], {
|
|
280
280
|
blockIdentifier
|
|
281
281
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
2
|
import { FlowChartColors, getNoRiskTags, IConfig, IInvestmentFlow, IProtocol, IStrategyMetadata, RiskFactor, RiskType } from "@/interfaces";
|
|
3
|
-
import { Pricer } from "@/modules";
|
|
4
|
-
import { CairoCustomEnum, Contract, num, uint256 } from "starknet";
|
|
3
|
+
import { AvnuWrapper, Pricer, SwapInfo } from "@/modules";
|
|
4
|
+
import { Account, CairoCustomEnum, Contract, num, uint256 } from "starknet";
|
|
5
5
|
import VesuRebalanceAbi from '@/data/vesu-rebalance.abi.json';
|
|
6
6
|
import { Global, logger } from "@/global";
|
|
7
7
|
import { assert } from "@/utils";
|
|
@@ -9,6 +9,8 @@ import axios from "axios";
|
|
|
9
9
|
import { PricerBase } from "@/modules/pricerBase";
|
|
10
10
|
import { BaseStrategy, SingleActionAmount, SingleTokenInfo } from "./base-strategy";
|
|
11
11
|
import { getAPIUsingHeadlessBrowser } from "@/node/headless";
|
|
12
|
+
import { VesuHarvests } from "@/modules/harvests";
|
|
13
|
+
import VesuPoolIDs from "@/data/vesu_pools.json";
|
|
12
14
|
|
|
13
15
|
interface PoolProps {
|
|
14
16
|
pool_id: ContractAddr;
|
|
@@ -189,9 +191,10 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
189
191
|
logger.verbose(typeof _pool);
|
|
190
192
|
logger.verbose(`name: ${_pool?.name}`);
|
|
191
193
|
const name = _pool?.name;
|
|
192
|
-
logger.verbose(`name2: ${name}`);
|
|
194
|
+
logger.verbose(`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`);
|
|
193
195
|
const assetInfo = _pool?.assets.find((d: any) => this.asset().address.eqString(d.address));
|
|
194
196
|
if (!name) {
|
|
197
|
+
logger.verbose(`Pool not found`);
|
|
195
198
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
196
199
|
}
|
|
197
200
|
if (!assetInfo) {
|
|
@@ -200,6 +203,11 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
200
203
|
let vTokenContract = new Contract(VesuRebalanceAbi, p.v_token.address, this.config.provider);
|
|
201
204
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
202
205
|
const assets = await vTokenContract.convert_to_assets(uint256.bnToUint256(bal.toString()));
|
|
206
|
+
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
207
|
+
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
208
|
+
logger.verbose(`defiSpringSupplyApr: ${JSON.stringify(assetInfo?.stats.defiSpringSupplyApr)}`);
|
|
209
|
+
logger.verbose(`currentUtilization: ${JSON.stringify(assetInfo?.stats.currentUtilization)}`);
|
|
210
|
+
logger.verbose(`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`);
|
|
203
211
|
const item = {
|
|
204
212
|
pool_id: p.pool_id,
|
|
205
213
|
pool_name: _pool?.name,
|
|
@@ -214,7 +222,7 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
214
222
|
netApy: 0,
|
|
215
223
|
} : {
|
|
216
224
|
baseApy: Number(Web3Number.fromWei(assetInfo.stats.supplyApy.value, assetInfo.stats.supplyApy.decimals).toFixed(6)),
|
|
217
|
-
defiSpringApy: Number(Web3Number.fromWei(assetInfo.stats.defiSpringSupplyApr.value, assetInfo.stats.defiSpringSupplyApr.decimals).toFixed(6)),
|
|
225
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(Web3Number.fromWei(assetInfo.stats.defiSpringSupplyApr.value, assetInfo.stats.defiSpringSupplyApr.decimals).toFixed(6)) : 0,
|
|
218
226
|
netApy: 0,
|
|
219
227
|
},
|
|
220
228
|
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(Web3Number.fromWei(assetInfo.stats.currentUtilization.value, assetInfo.stats.currentUtilization.decimals).toFixed(6)),
|
|
@@ -302,15 +310,7 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
302
310
|
}
|
|
303
311
|
|
|
304
312
|
|
|
305
|
-
let isErrorPoolsAPI =
|
|
306
|
-
let pools: any[] = [];
|
|
307
|
-
try {
|
|
308
|
-
const data = await getAPIUsingHeadlessBrowser('https://api.vesu.xyz/pools');
|
|
309
|
-
pools = data.data;
|
|
310
|
-
} catch (e) {
|
|
311
|
-
console.error(`${VesuRebalance.name}: Error fetching pools for ${this.address.address}`, e);
|
|
312
|
-
isErrorPoolsAPI = true;
|
|
313
|
-
}
|
|
313
|
+
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
314
314
|
|
|
315
315
|
const totalAssets = (await this.getTVL()).amount;
|
|
316
316
|
|
|
@@ -324,6 +324,34 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
async getVesuPools(retry = 0): Promise<{pools: any[], isErrorPoolsAPI: boolean}> {
|
|
328
|
+
let isErrorPoolsAPI = false;
|
|
329
|
+
let pools: any[] = [];
|
|
330
|
+
try {
|
|
331
|
+
const data = await getAPIUsingHeadlessBrowser('https://api.vesu.xyz/pools');
|
|
332
|
+
pools = data.data;
|
|
333
|
+
|
|
334
|
+
// Vesu API is unstable sometimes, some Pools may be missing sometimes
|
|
335
|
+
for (const pool of VesuPoolIDs.data) {
|
|
336
|
+
const found = pools.find((d: any) => d.id === pool.id);
|
|
337
|
+
if (!found) {
|
|
338
|
+
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
339
|
+
logger.verbose(`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`);
|
|
340
|
+
throw new Error('pool not found [sanity check]')
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch (e) {
|
|
344
|
+
logger.error(`${VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`, e);
|
|
345
|
+
isErrorPoolsAPI = true;
|
|
346
|
+
if (retry < 10) {
|
|
347
|
+
await new Promise((resolve) => setTimeout(resolve, 5000 * (retry + 1)));
|
|
348
|
+
return await this.getVesuPools(retry + 1);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return { pools, isErrorPoolsAPI };
|
|
353
|
+
}
|
|
354
|
+
|
|
327
355
|
/**
|
|
328
356
|
* Calculates the weighted average APY across all pools based on USD value.
|
|
329
357
|
* @returns {Promise<number>} The weighted average APY across all pools
|
|
@@ -497,6 +525,46 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
497
525
|
});
|
|
498
526
|
return [baseFlow];
|
|
499
527
|
}
|
|
528
|
+
|
|
529
|
+
async harvest(acc: Account) {
|
|
530
|
+
const vesuHarvest = new VesuHarvests(this.config);
|
|
531
|
+
const harvests = await vesuHarvest.getUnHarvestedRewards(this.address);
|
|
532
|
+
const harvest = harvests[0];
|
|
533
|
+
const avnu = new AvnuWrapper();
|
|
534
|
+
let swapInfo: SwapInfo = {
|
|
535
|
+
token_from_address: harvest.token.address,
|
|
536
|
+
token_from_amount: uint256.bnToUint256(harvest.actualReward.toWei()),
|
|
537
|
+
token_to_address: this.asset().address.address,
|
|
538
|
+
token_to_amount: uint256.bnToUint256(0),
|
|
539
|
+
token_to_min_amount: uint256.bnToUint256(0),
|
|
540
|
+
beneficiary: this.address.address,
|
|
541
|
+
integrator_fee_amount_bps: 0,
|
|
542
|
+
integrator_fee_recipient: this.address.address,
|
|
543
|
+
routes: []
|
|
544
|
+
}
|
|
545
|
+
if (!this.asset().address.eqString(harvest.token.address)) {
|
|
546
|
+
const quote = await avnu.getQuotes(
|
|
547
|
+
harvest.token.address,
|
|
548
|
+
this.asset().address.address,
|
|
549
|
+
harvest.actualReward.toWei(),
|
|
550
|
+
this.address.address
|
|
551
|
+
);
|
|
552
|
+
swapInfo = await avnu.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return [
|
|
556
|
+
this.contract.populate('harvest', [
|
|
557
|
+
harvest.rewardsContract.address,
|
|
558
|
+
{
|
|
559
|
+
id: harvest.claim.id,
|
|
560
|
+
amount: harvest.claim.amount.toWei(),
|
|
561
|
+
claimee: harvest.claim.claimee.address
|
|
562
|
+
},
|
|
563
|
+
harvest.proof,
|
|
564
|
+
swapInfo
|
|
565
|
+
])
|
|
566
|
+
]
|
|
567
|
+
}
|
|
500
568
|
}
|
|
501
569
|
|
|
502
570
|
const _description = 'Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.'
|