@strkfarm/sdk 1.0.29 → 1.0.30
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 +9173 -63
- package/dist/index.browser.mjs +9185 -75
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9197 -87
- package/dist/index.mjs +9187 -77
- 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 +75 -12
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) {
|
|
@@ -302,15 +305,7 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
|
|
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
|
-
}
|
|
308
|
+
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
314
309
|
|
|
315
310
|
const totalAssets = (await this.getTVL()).amount;
|
|
316
311
|
|
|
@@ -324,6 +319,34 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
324
319
|
}
|
|
325
320
|
}
|
|
326
321
|
|
|
322
|
+
async getVesuPools(retry = 0): Promise<{pools: any[], isErrorPoolsAPI: boolean}> {
|
|
323
|
+
let isErrorPoolsAPI = false;
|
|
324
|
+
let pools: any[] = [];
|
|
325
|
+
try {
|
|
326
|
+
const data = await getAPIUsingHeadlessBrowser('https://api.vesu.xyz/pools');
|
|
327
|
+
pools = data.data;
|
|
328
|
+
|
|
329
|
+
// Vesu API is unstable sometimes, some Pools may be missing sometimes
|
|
330
|
+
for (const pool of VesuPoolIDs.data) {
|
|
331
|
+
const found = pools.find((d: any) => d.id === pool.id);
|
|
332
|
+
if (!found) {
|
|
333
|
+
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
334
|
+
logger.verbose(`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`);
|
|
335
|
+
throw new Error('pool not found [sanity check]')
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} catch (e) {
|
|
339
|
+
logger.error(`${VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`, e);
|
|
340
|
+
isErrorPoolsAPI = true;
|
|
341
|
+
if (retry < 10) {
|
|
342
|
+
await new Promise((resolve) => setTimeout(resolve, 5000 * (retry + 1)));
|
|
343
|
+
return await this.getVesuPools(retry + 1);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return { pools, isErrorPoolsAPI };
|
|
348
|
+
}
|
|
349
|
+
|
|
327
350
|
/**
|
|
328
351
|
* Calculates the weighted average APY across all pools based on USD value.
|
|
329
352
|
* @returns {Promise<number>} The weighted average APY across all pools
|
|
@@ -497,6 +520,46 @@ export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmo
|
|
|
497
520
|
});
|
|
498
521
|
return [baseFlow];
|
|
499
522
|
}
|
|
523
|
+
|
|
524
|
+
async harvest(acc: Account) {
|
|
525
|
+
const vesuHarvest = new VesuHarvests(this.config);
|
|
526
|
+
const harvests = await vesuHarvest.getUnHarvestedRewards(this.address);
|
|
527
|
+
const harvest = harvests[0];
|
|
528
|
+
const avnu = new AvnuWrapper();
|
|
529
|
+
let swapInfo: SwapInfo = {
|
|
530
|
+
token_from_address: harvest.token.address,
|
|
531
|
+
token_from_amount: uint256.bnToUint256(harvest.actualReward.toWei()),
|
|
532
|
+
token_to_address: this.asset().address.address,
|
|
533
|
+
token_to_amount: uint256.bnToUint256(0),
|
|
534
|
+
token_to_min_amount: uint256.bnToUint256(0),
|
|
535
|
+
beneficiary: this.address.address,
|
|
536
|
+
integrator_fee_amount_bps: 0,
|
|
537
|
+
integrator_fee_recipient: this.address.address,
|
|
538
|
+
routes: []
|
|
539
|
+
}
|
|
540
|
+
if (!this.asset().address.eqString(harvest.token.address)) {
|
|
541
|
+
const quote = await avnu.getQuotes(
|
|
542
|
+
harvest.token.address,
|
|
543
|
+
this.asset().address.address,
|
|
544
|
+
harvest.actualReward.toWei(),
|
|
545
|
+
this.address.address
|
|
546
|
+
);
|
|
547
|
+
swapInfo = await avnu.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return [
|
|
551
|
+
this.contract.populate('harvest', [
|
|
552
|
+
harvest.rewardsContract.address,
|
|
553
|
+
{
|
|
554
|
+
id: harvest.claim.id,
|
|
555
|
+
amount: harvest.claim.amount.toWei(),
|
|
556
|
+
claimee: harvest.claim.claimee.address
|
|
557
|
+
},
|
|
558
|
+
harvest.proof,
|
|
559
|
+
swapInfo
|
|
560
|
+
])
|
|
561
|
+
]
|
|
562
|
+
}
|
|
500
563
|
}
|
|
501
564
|
|
|
502
565
|
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.'
|