@strkfarm/sdk 1.0.62 → 1.1.0

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.
@@ -1,5 +1,5 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
- import { BlockIdentifier, constants, RpcProvider } from "starknet";
2
+ import { BlockIdentifier, BlockTag, constants, RpcProvider } from "starknet";
3
3
  import React, { ReactNode } from "react";
4
4
 
5
5
  export enum RiskType {
@@ -107,7 +107,7 @@ export interface IInvestmentFlow {
107
107
 
108
108
  export function getMainnetConfig(
109
109
  rpcUrl: string = 'https://starknet-mainnet.public.blastapi.io',
110
- blockIdentifier: BlockIdentifier = "pending",
110
+ blockIdentifier: BlockIdentifier = BlockTag.LATEST
111
111
  // specVersion = constants.SupportedRpcVersion.v0_8_1
112
112
  ): IConfig {
113
113
  return {
@@ -12,7 +12,7 @@ export class ERC20 {
12
12
 
13
13
  contract(addr: string | ContractAddr) {
14
14
  const _addr = typeof addr === 'string' ? addr : addr.address;
15
- return new Contract(ERC20Abi, _addr, this.config.provider);
15
+ return new Contract({abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider});
16
16
  }
17
17
 
18
18
  async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
@@ -34,7 +34,7 @@ export class Harvests {
34
34
 
35
35
  const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
36
36
  for (let reward of rewards) {
37
- const contract = new Contract(cls.abi, reward.rewardsContract.address, this.config.provider);
37
+ const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
38
38
  const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
39
39
  logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
40
40
  if (isClaimed) {
@@ -83,15 +83,17 @@ export class EkuboHarvests extends Harvests {
83
83
  }
84
84
  }
85
85
 
86
+ export const VESU_REWARDS_CONTRACT = ContractAddr.from('0x0387f3eb1d98632fbe3440a9f1385Aec9d87b6172491d3Dd81f1c35A7c61048F');
87
+
86
88
  export class VesuHarvests extends Harvests {
87
89
  async getHarvests(addr: ContractAddr): Promise<HarvestInfo[]> {
88
90
  const result = await fetch(`https://api.vesu.xyz/users/${addr.address}/strk-rewards/calldata`);
89
91
  const data = await result.json();
90
- const rewardsContract = ContractAddr.from('0x0387f3eb1d98632fbe3440a9f1385Aec9d87b6172491d3Dd81f1c35A7c61048F');
91
-
92
+ const rewardsContract = VESU_REWARDS_CONTRACT;
93
+
92
94
  // get already claimed amount
93
95
  const cls = await this.config.provider.getClassAt(rewardsContract.address);
94
- const contract = new Contract(cls.abi, rewardsContract.address, this.config.provider);
96
+ const contract = new Contract({abi: cls.abi, address: rewardsContract.address, providerOrAccount: this.config.provider});
95
97
  const _claimed_amount: any = await contract.call('amount_already_claimed', [addr.address]);
96
98
  const claimed_amount = Web3Number.fromWei(_claimed_amount.toString(), 18);
97
99
  logger.verbose(`${VesuHarvests.name}: claimed_amount: ${claimed_amount.toString()}`);
@@ -7,7 +7,7 @@ export class Pragma {
7
7
  readonly contract: Contract;
8
8
 
9
9
  constructor(provider: RpcProvider) {
10
- this.contract = new Contract(PragmaAbi, this.contractAddr, provider);
10
+ this.contract = new Contract({abi: PragmaAbi, address: this.contractAddr, providerOrAccount: provider});
11
11
  }
12
12
 
13
13
  async getPrice(tokenAddr: string) {
@@ -127,7 +127,7 @@ export class Pricer extends PricerBase {
127
127
 
128
128
  async _getPrice(token: TokenInfo, defaultMethod = 'all'): Promise<number> {
129
129
  const methodToUse: string = this.methodToUse[token.symbol] || defaultMethod; // default start with coinbase
130
- logger.info(`Fetching price of ${token.symbol} using ${methodToUse}`);
130
+ logger.verbose(`Fetching price of ${token.symbol} using ${methodToUse}`);
131
131
  switch (methodToUse) {
132
132
  case 'Coinbase':
133
133
  try {
@@ -1,5 +1,5 @@
1
1
  import assert from 'assert'
2
- import {Account, Call, RawArgs, RpcProvider, TransactionExecutionStatus, constants, extractContractHashes, hash, json, num, provider, transaction} from 'starknet'
2
+ import {Account, Call, RawArgs, Deployer as SnDeployer, RpcProvider, TransactionExecutionStatus, constants, extractContractHashes, hash, json, num, provider, transaction} from 'starknet'
3
3
  import { readFileSync, existsSync, writeFileSync } from 'fs'
4
4
  import { IConfig } from '../interfaces';
5
5
  import { Store, getDefaultStoreConfig } from '../utils/store';
@@ -136,7 +136,7 @@ async function prepareMultiDeployContracts(
136
136
  const declaredInfo = await myDeclare(contract_name, package_name, config, acc);
137
137
  const classHash = declaredInfo.class_hash;
138
138
 
139
- const {calls, addresses} = transaction.buildUDCCall({
139
+ const {calls, addresses} = (new SnDeployer()).buildDeployerCall({
140
140
  classHash,
141
141
  constructorCalldata: constructorData,
142
142
  }, acc.address);
@@ -31,7 +31,7 @@ export class AutoCompounderSTRK {
31
31
  async init() {
32
32
  const provider: RpcProvider = this.config.provider;
33
33
  const cls = await provider.getClassAt(this.addr.address);
34
- this.contract = new Contract(cls.abi, this.addr.address, provider);
34
+ this.contract = new Contract({abi: cls.abi, address: this.addr.address, providerOrAccount: provider});
35
35
  this.initialized = true;
36
36
  }
37
37
 
@@ -113,17 +113,17 @@ export class EkuboCLVault extends BaseStrategy<
113
113
  this.metadata = metadata;
114
114
  this.address = metadata.address;
115
115
 
116
- this.contract = new Contract(
117
- CLVaultAbi,
118
- this.address.address,
119
- this.config.provider
120
- );
116
+ this.contract = new Contract({
117
+ abi: CLVaultAbi,
118
+ address: this.address.address,
119
+ providerOrAccount: this.config.provider
120
+ });
121
121
  if (this.metadata.additionalInfo.lstContract) {
122
- this.lstContract = new Contract(
123
- ERC4626Abi,
124
- this.metadata.additionalInfo.lstContract.address,
125
- this.config.provider
126
- );
122
+ this.lstContract = new Contract({
123
+ abi: ERC4626Abi,
124
+ address: this.metadata.additionalInfo.lstContract.address,
125
+ providerOrAccount: this.config.provider
126
+ });
127
127
  } else {
128
128
  this.lstContract = null;
129
129
  }
@@ -131,18 +131,18 @@ export class EkuboCLVault extends BaseStrategy<
131
131
  // ekubo positions contract
132
132
  const EKUBO_POSITION =
133
133
  "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
134
- this.ekuboPositionsContract = new Contract(
135
- EkuboPositionsAbi,
136
- EKUBO_POSITION,
137
- this.config.provider
138
- );
134
+ this.ekuboPositionsContract = new Contract({
135
+ abi: EkuboPositionsAbi,
136
+ address: EKUBO_POSITION,
137
+ providerOrAccount: this.config.provider
138
+ });
139
139
  const EKUBO_MATH =
140
140
  "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
141
- this.ekuboMathContract = new Contract(
142
- EkuboMathAbi,
143
- EKUBO_MATH,
144
- this.config.provider
145
- );
141
+ this.ekuboMathContract = new Contract({
142
+ abi: EkuboMathAbi,
143
+ address: EKUBO_MATH,
144
+ providerOrAccount: this.config.provider
145
+ });
146
146
 
147
147
  this.avnu = new AvnuWrapper();
148
148
  }
@@ -207,16 +207,16 @@ export class EkuboCLVault extends BaseStrategy<
207
207
  const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
208
208
  // Technically its not erc4626 abi, but we just need approve call
209
209
  // so, its ok to use it
210
- const token0Contract = new Contract(
211
- ERC4626Abi,
212
- amountInfo.token0.tokenInfo.address.address,
213
- this.config.provider
214
- );
215
- const token1Contract = new Contract(
216
- ERC4626Abi,
217
- amountInfo.token1.tokenInfo.address.address,
218
- this.config.provider
219
- );
210
+ const token0Contract = new Contract({
211
+ abi: ERC4626Abi,
212
+ address: amountInfo.token0.tokenInfo.address.address,
213
+ providerOrAccount: this.config.provider
214
+ });
215
+ const token1Contract = new Contract({
216
+ abi: ERC4626Abi,
217
+ address: amountInfo.token1.tokenInfo.address.address,
218
+ providerOrAccount: this.config.provider
219
+ });
220
220
  const call1 = token0Contract.populate("approve", [
221
221
  this.address.address,
222
222
  uint256.bnToUint256(updateAmountInfo.token0.amount.toWei()),
@@ -286,7 +286,7 @@ export class EkuboCLVault extends BaseStrategy<
286
286
  * @returns {Promise<number>} The weighted average APY across all pools
287
287
  */
288
288
  async netAPY(
289
- blockIdentifier: BlockIdentifier = "pending",
289
+ blockIdentifier: BlockIdentifier = "pre_confirmed",
290
290
  sinceBlocks = 20000
291
291
  ): Promise<number> {
292
292
  // no special provisions required to account for defi spring rewards
@@ -376,7 +376,7 @@ export class EkuboCLVault extends BaseStrategy<
376
376
 
377
377
  async balanceOf(
378
378
  user: ContractAddr,
379
- blockIdentifier: BlockIdentifier = "pending"
379
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
380
380
  ): Promise<Web3Number> {
381
381
  let bal = await this.contract.call("balance_of", [user.address], {
382
382
  blockIdentifier,
@@ -386,7 +386,7 @@ export class EkuboCLVault extends BaseStrategy<
386
386
 
387
387
  async getUserTVL(
388
388
  user: ContractAddr,
389
- blockIdentifier: BlockIdentifier = "pending"
389
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
390
390
  ): Promise<DualTokenInfo> {
391
391
  let bal = await this.balanceOf(user, blockIdentifier);
392
392
  const assets: any = await this.contract.call(
@@ -428,7 +428,7 @@ export class EkuboCLVault extends BaseStrategy<
428
428
  };
429
429
  }
430
430
 
431
- async _getTVL(blockIdentifier: BlockIdentifier = "pending") {
431
+ async _getTVL(blockIdentifier: BlockIdentifier = "pre_confirmed") {
432
432
  const result = await this.contract.call("total_liquidity", [], {
433
433
  blockIdentifier,
434
434
  });
@@ -443,7 +443,7 @@ export class EkuboCLVault extends BaseStrategy<
443
443
  }
444
444
 
445
445
  async totalSupply(
446
- blockIdentifier: BlockIdentifier = "pending"
446
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
447
447
  ): Promise<Web3Number> {
448
448
  const res = await this.contract.call("total_supply", [], {
449
449
  blockIdentifier,
@@ -464,7 +464,7 @@ export class EkuboCLVault extends BaseStrategy<
464
464
  }
465
465
 
466
466
  async getTVL(
467
- blockIdentifier: BlockIdentifier = "pending"
467
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
468
468
  ): Promise<DualTokenInfo> {
469
469
  const { amount0, amount1 } = await this._getTVL(blockIdentifier);
470
470
  const poolKey = await this.getPoolKey(blockIdentifier);
@@ -561,14 +561,14 @@ export class EkuboCLVault extends BaseStrategy<
561
561
  throw new Error("No true price available");
562
562
  }
563
563
 
564
- async getCurrentPrice(blockIdentifier: BlockIdentifier = "pending") {
564
+ async getCurrentPrice(blockIdentifier: BlockIdentifier = "pre_confirmed") {
565
565
  const poolKey = await this.getPoolKey(blockIdentifier);
566
566
  return this._getCurrentPrice(poolKey, blockIdentifier);
567
567
  }
568
568
 
569
569
  async _getCurrentPrice(
570
570
  poolKey: EkuboPoolKey,
571
- blockIdentifier: BlockIdentifier = "pending"
571
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
572
572
  ) {
573
573
  const priceInfo: any = await this.ekuboPositionsContract.call(
574
574
  "get_pool_price",
@@ -606,7 +606,7 @@ export class EkuboCLVault extends BaseStrategy<
606
606
  }
607
607
 
608
608
  async getCurrentBounds(
609
- blockIdentifier: BlockIdentifier = "pending"
609
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
610
610
  ): Promise<EkuboBounds> {
611
611
  const result: any = await this.contract.call("get_position_key", [], {
612
612
  blockIdentifier,
@@ -632,7 +632,7 @@ export class EkuboCLVault extends BaseStrategy<
632
632
  }
633
633
 
634
634
  async getPoolKey(
635
- blockIdentifier: BlockIdentifier = "pending"
635
+ blockIdentifier: BlockIdentifier = "pre_confirmed"
636
636
  ): Promise<EkuboPoolKey> {
637
637
  if (this.poolKey) {
638
638
  return this.poolKey;
@@ -1345,7 +1345,7 @@ export class EkuboCLVault extends BaseStrategy<
1345
1345
  async getLiquidityToAmounts(
1346
1346
  liquidity: Web3Number,
1347
1347
  bounds: EkuboBounds,
1348
- blockIdentifier: BlockIdentifier = "pending",
1348
+ blockIdentifier: BlockIdentifier = "pre_confirmed",
1349
1349
  _poolKey: EkuboPoolKey | null = null,
1350
1350
  _currentPrice: {
1351
1351
  price: number;
@@ -30,7 +30,7 @@ export class SenseiVault extends BaseStrategy<
30
30
  this.address = metadata.address;
31
31
  this.pricer = pricer;
32
32
  this.metadata = metadata;
33
- this.contract = new Contract(SenseiABI, this.address.address, this.config.provider);
33
+ this.contract = new Contract({abi: SenseiABI, address: this.address.address, providerOrAccount: this.config.provider});
34
34
 
35
35
  if (metadata.depositTokens.length === 0) {
36
36
  throw new Error("Deposit tokens are not defined in metadata");
@@ -90,11 +90,11 @@ export class SenseiVault extends BaseStrategy<
90
90
  }
91
91
 
92
92
  async depositCall(amountInfo: SingleActionAmount, receiver: ContractAddr): Promise<Call[]> {
93
- const mainTokenContract = new Contract(
94
- ERC20ABI,
95
- this.metadata.depositTokens[0].address.address,
96
- this.config.provider,
97
- );
93
+ const mainTokenContract = new Contract({
94
+ abi: ERC20ABI,
95
+ address: this.metadata.depositTokens[0].address.address,
96
+ providerOrAccount: this.config.provider,
97
+ });
98
98
  const call1 = mainTokenContract.populate('approve', [
99
99
  this.address.address,
100
100
  uint256.bnToUint256(amountInfo.amount.toWei()),
@@ -1,6 +1,8 @@
1
1
  import { ContractAddr } from "@/dataTypes";
2
2
 
3
- export const SIMPLE_SANITIZER = ContractAddr.from('0x3798dc4f83fdfad199e5236e3656cf2fb79bc50c00504d0dd41522e0f042072');
3
+ export const SIMPLE_SANITIZER = ContractAddr.from('0x5a2e3ceb3da368b983a8717898427ab7b6daf04014b70f321e777f9aad940b4');
4
+ export const PRICE_ROUTER = ContractAddr.from('0x05e83Fa38D791d2dba8E6f487758A9687FfEe191A6Cf8a6c5761ab0a110DB837');
5
+ export const AVNU_MIDDLEWARE = ContractAddr.from('0x4a7972ed3f5d1e74a6d6c4a8f467666953d081c8f2270390cc169d50d17cb0d');
4
6
 
5
7
  export function toBigInt(value: string | number): bigint {
6
8
  if (typeof value === 'string') {
@@ -1,8 +1,9 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { LeafData } from "@/utils";
3
3
  import { Call, hash, num, shortString, uint256 } from "starknet";
4
- import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
4
+ import { AVNU_MIDDLEWARE, SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
5
5
  import { AdapterLeafType, BaseAdapter, GenerateCallFn, LeafAdapterFn, ManageCall } from "./baseAdapter";
6
+ import { SwapInfo } from "@/modules";
6
7
 
7
8
  export interface FlashloanCallParams {
8
9
  amount: Web3Number,
@@ -11,6 +12,9 @@ export interface FlashloanCallParams {
11
12
  export interface ApproveCallParams {
12
13
  amount: Web3Number,
13
14
  }
15
+ export interface AvnuSwapCallParams {
16
+ props: SwapInfo
17
+ }
14
18
 
15
19
  export interface CommonAdapterConfig {
16
20
  id: string,
@@ -124,4 +128,56 @@ export class CommonAdapter extends BaseAdapter {
124
128
  }
125
129
  }
126
130
  }
131
+
132
+ getAvnuAdapter(fromToken: ContractAddr, toToken: ContractAddr, id: string): () => AdapterLeafType<AvnuSwapCallParams> {
133
+ return () => ({
134
+ leaf: this.constructSimpleLeafData({
135
+ id: id,
136
+ target: AVNU_MIDDLEWARE,
137
+ method: 'multi_route_swap',
138
+ packedArguments: [
139
+ fromToken.toBigInt(),
140
+ toToken.toBigInt(),
141
+ this.config.vaultAllocator.toBigInt(),
142
+ ]
143
+ }),
144
+ callConstructor: this.getAvnuCall(fromToken, toToken).bind(this)
145
+ });
146
+ }
147
+
148
+ getAvnuCall(fromToken: ContractAddr, toToken: ContractAddr): GenerateCallFn<AvnuSwapCallParams> {
149
+ return (params: AvnuSwapCallParams): ManageCall => {
150
+ return {
151
+ sanitizer: SIMPLE_SANITIZER,
152
+ call: {
153
+ contractAddress: AVNU_MIDDLEWARE,
154
+ selector: hash.getSelectorFromName('multi_route_swap'),
155
+ calldata: [
156
+ fromToken.toBigInt(), // sell_token_address
157
+ toBigInt(params.props.token_from_amount.low.toString()), // sell_token_amount low
158
+ toBigInt(params.props.token_from_amount.high.toString()), // sell_token_amount high
159
+ toToken.toBigInt(), // buy_token_address
160
+ toBigInt(params.props.token_to_amount.low.toString()), // buy_token_amount low
161
+ toBigInt(params.props.token_to_amount.high.toString()), // buy_token_amount high
162
+ toBigInt(params.props.token_to_min_amount.low.toString()), // buy_token_min_amount low
163
+ toBigInt(params.props.token_to_min_amount.high.toString()), // buy_token_min_amount high
164
+ this.config.vaultAllocator.toBigInt(), // beneficiary
165
+ toBigInt(0), // integrator_fee_amount_bps
166
+ this.config.vaultAllocator.toBigInt(), // integrator_fee_recipient
167
+
168
+ // unpack routes
169
+ BigInt(params.props.routes.length),
170
+ ...params.props.routes.map(r => ([
171
+ BigInt(num.hexToDecimalString(r.token_from)),
172
+ BigInt(num.hexToDecimalString(r.token_to)),
173
+ BigInt(num.hexToDecimalString(r.exchange_address)),
174
+ BigInt(r.percent),
175
+ BigInt(r.additional_swap_params.length),
176
+ ...r.additional_swap_params.map(p => BigInt(num.hexToDecimalString(p)))
177
+ ])).flat()
178
+ ]
179
+ }
180
+ }
181
+ }
182
+ }
127
183
  }
@@ -9,6 +9,7 @@ import { PricerBase } from "@/modules/pricerBase";
9
9
  import VesuPoolIDs from "@/data/vesu_pools.json";
10
10
  import { getAPIUsingHeadlessBrowser } from "@/node/headless";
11
11
  import { Global } from "@/global";
12
+ import { VESU_REWARDS_CONTRACT } from "@/modules/harvests";
12
13
 
13
14
  interface VesuPoolsInfo { pools: any[]; isErrorPoolsAPI: boolean };
14
15
 
@@ -38,6 +39,11 @@ export interface VesuModifyPositionCallParams {
38
39
  debtAmount: VesuAmount
39
40
  }
40
41
 
42
+ export interface VesuDefiSpringRewardsCallParams {
43
+ amount: Web3Number,
44
+ proofs: string[],
45
+ }
46
+
41
47
  export interface VesuAdapterConfig {
42
48
  poolId: ContractAddr,
43
49
  collateral: TokenInfo,
@@ -133,7 +139,7 @@ export class VesuAdapter extends BaseAdapter {
133
139
  }
134
140
  };
135
141
  logger.verbose(`VesuAdapter::ConstructingModify::Debt::${JSON.stringify(_debt)}`)
136
- const singletonContract = new Contract(VesuSingletonAbi, this.VESU_SINGLETON.toString(), new RpcProvider({nodeUrl: ''}));
142
+ const singletonContract = new Contract({abi: VesuSingletonAbi, address: this.VESU_SINGLETON.toString(), providerOrAccount: new RpcProvider({nodeUrl: ''})});
137
143
  const call = singletonContract.populate('modify_position', {
138
144
  params: {
139
145
  pool_id: this.config.poolId.toBigInt(),
@@ -156,6 +162,39 @@ export class VesuAdapter extends BaseAdapter {
156
162
  }
157
163
  }
158
164
  }
165
+
166
+ getDefispringRewardsAdapter = (id: string): () => AdapterLeafType<VesuDefiSpringRewardsCallParams> => {
167
+ return () => {
168
+ const packedArguments: bigint[] = [];
169
+ const output = {
170
+ id: BigInt(num.getDecimalString(shortString.encodeShortString(id))),
171
+ readableId: id,
172
+ data: [
173
+ SIMPLE_SANITIZER.toBigInt(), // sanitizer address
174
+ VESU_REWARDS_CONTRACT.toBigInt(), // contract
175
+ toBigInt(hash.getSelectorFromName("claim")), // method name
176
+ BigInt(packedArguments.length),
177
+ ...packedArguments
178
+ ]
179
+ };
180
+ return { leaf: output, callConstructor: this.getDefiSpringClaimCall().bind(this) };
181
+ }
182
+ }
183
+
184
+ getDefiSpringClaimCall = (): GenerateCallFn<VesuDefiSpringRewardsCallParams> => {
185
+ return (params: VesuDefiSpringRewardsCallParams) => ({
186
+ sanitizer: SIMPLE_SANITIZER,
187
+ call: {
188
+ contractAddress: VESU_REWARDS_CONTRACT,
189
+ selector: hash.getSelectorFromName('claim'),
190
+ calldata: [
191
+ BigInt(params.amount.toWei()),
192
+ BigInt(params.proofs.length),
193
+ ...params.proofs.map(proof => BigInt(num.hexToDecimalString(proof)))
194
+ ]
195
+ }
196
+ })
197
+ }
159
198
 
160
199
  formatAmountTypeEnum(amountType: VesuAmountType) {
161
200
  switch(amountType) {
@@ -178,7 +217,7 @@ export class VesuAdapter extends BaseAdapter {
178
217
  }
179
218
 
180
219
  getVesuSingletonContract(config: IConfig) {
181
- return new Contract(VesuSingletonAbi, this.VESU_SINGLETON.address, config.provider);
220
+ return new Contract({abi: VesuSingletonAbi, address: this.VESU_SINGLETON.address, providerOrAccount: config.provider});
182
221
  }
183
222
 
184
223
  async getLTVConfig(config: IConfig) {
@@ -7,9 +7,11 @@ import { VesuRebalanceSettings } from "./vesu-rebalance";
7
7
  import { assert, LeafData, logger, StandardMerkleTree } from "@/utils";
8
8
  import UniversalVaultAbi from '../data/universal-vault.abi.json';
9
9
  import ManagerAbi from '../data/vault-manager.abi.json';
10
- import { ApproveCallParams, BaseAdapter, CommonAdapter, FlashloanCallParams, GenerateCallFn, LeafAdapterFn, ManageCall, VesuAdapter, VesuModifyPositionCallParams, VesuPools } from "./universal-adapters";
10
+ import { ApproveCallParams, AvnuSwapCallParams, BaseAdapter, CommonAdapter, FlashloanCallParams, GenerateCallFn, LeafAdapterFn, ManageCall, VesuAdapter, VesuDefiSpringRewardsCallParams, VesuModifyPositionCallParams, VesuPools } from "./universal-adapters";
11
11
  import { Global } from "@/global";
12
- import { ERC20 } from "@/modules";
12
+ import { AvnuWrapper, ERC20 } from "@/modules";
13
+ import { AVNU_MIDDLEWARE } from "./universal-adapters/adapter-utils";
14
+ import { VesuHarvests } from "@/modules/harvests";
13
15
 
14
16
  export interface UniversalStrategySettings {
15
17
  vaultAddress: ContractAddr,
@@ -61,16 +63,16 @@ export class UniversalStrategy<
61
63
  this.metadata = metadata;
62
64
  this.address = metadata.address;
63
65
 
64
- this.contract = new Contract(
65
- UniversalVaultAbi,
66
- this.address.address,
67
- this.config.provider
68
- );
69
- this.managerContract = new Contract(
70
- ManagerAbi,
71
- this.metadata.additionalInfo.manager.address,
72
- this.config.provider
73
- );
66
+ this.contract = new Contract({
67
+ abi: UniversalVaultAbi,
68
+ address: this.address.address,
69
+ providerOrAccount: this.config.provider
70
+ });
71
+ this.managerContract = new Contract({
72
+ abi: ManagerAbi,
73
+ address: this.metadata.additionalInfo.manager.address,
74
+ providerOrAccount: this.config.provider
75
+ });
74
76
  }
75
77
 
76
78
  getMerkleTree() {
@@ -130,11 +132,11 @@ export class UniversalStrategy<
130
132
  amountInfo.tokenInfo.address.eq(this.asset().address),
131
133
  "Deposit token mismatch"
132
134
  );
133
- const assetContract = new Contract(
134
- UniversalVaultAbi,
135
- this.asset().address.address,
136
- this.config.provider
137
- );
135
+ const assetContract = new Contract({
136
+ abi: UniversalVaultAbi,
137
+ address: this.asset().address.address,
138
+ providerOrAccount: this.config.provider
139
+ });
138
140
  const call1 = assetContract.populate("approve", [
139
141
  this.address.address,
140
142
  uint256.bnToUint256(amountInfo.amount.toWei())
@@ -312,7 +314,8 @@ export class UniversalStrategy<
312
314
 
313
315
  // calculate estimated growth from strk rewards
314
316
  const netAPY = await this.netAPY();
315
- const defispringAPY = netAPY.splits.find(s => s.id === 'defispring')?.apy || 0;
317
+ // account only 80% of value
318
+ const defispringAPY = (netAPY.splits.find(s => s.id === 'defispring')?.apy || 0) * 0.8;
316
319
  if (!defispringAPY) throw new Error('DefiSpring APY not found');
317
320
 
318
321
  const timeDiff = (Math.round(Date.now() / 1000) - Number(lastReportTime));
@@ -605,6 +608,61 @@ export class UniversalStrategy<
605
608
  return manageCall;
606
609
  }
607
610
 
611
+ async getHarvestCall() {
612
+ const vesuHarvest = new VesuHarvests(this.config);
613
+ const harvestInfo = await vesuHarvest.getUnHarvestedRewards(this.metadata.additionalInfo.vaultAllocator);
614
+ if (harvestInfo.length != 1) {
615
+ throw new Error(`Expected 1 harvest info, got ${harvestInfo.length}`);
616
+ }
617
+
618
+ const amount = harvestInfo[0].claim.amount;
619
+ const actualReward = harvestInfo[0].actualReward;
620
+ const proofs = harvestInfo[0].proof;
621
+ if (actualReward.isZero()) {
622
+ throw new Error(`Expected non-zero actual reward, got ${harvestInfo[0].actualReward}`);
623
+ }
624
+
625
+ const manage1Info = this.getProofs<VesuDefiSpringRewardsCallParams>(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS);
626
+ const manageCall1 = manage1Info.callConstructor({
627
+ amount,
628
+ proofs
629
+ });
630
+ const proofIds: string[] = [UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS];
631
+ const manageCalls: ManageCall[] = [manageCall1];
632
+
633
+ // swap rewards for underlying
634
+ const STRK = Global.getDefaultTokens().find(t => t.symbol === 'STRK')!;
635
+ if (this.asset().symbol != 'STRK') {
636
+ // approve
637
+ const manage2Info = this.getProofs<ApproveCallParams>(UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1);
638
+ const manageCall2 = manage2Info.callConstructor({
639
+ amount: actualReward
640
+ });
641
+
642
+ // swap
643
+ const avnuModule = new AvnuWrapper();
644
+ const quote = await avnuModule.getQuotes(
645
+ STRK.address.address,
646
+ this.asset().address.address,
647
+ actualReward.toWei(),
648
+ this.address.address
649
+ );
650
+ const swapInfo = await avnuModule.getSwapInfo(quote, this.address.address, 0, this.address.address);
651
+ const manage3Info = this.getProofs<AvnuSwapCallParams>(UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS);
652
+ const manageCall3 = manage3Info.callConstructor({
653
+ props: swapInfo
654
+ });
655
+ proofIds.push(UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1);
656
+ proofIds.push(UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS);
657
+
658
+ manageCalls.push(manageCall2);
659
+ manageCalls.push(manageCall3);
660
+ }
661
+
662
+ const manageCall = this.getManageCall(proofIds, manageCalls);
663
+ return { call: manageCall, reward: actualReward, tokenInfo: STRK };
664
+ }
665
+
608
666
  async getRebalanceCall(params: {
609
667
  isLeg1toLeg2: boolean,
610
668
  amount: Web3Number
@@ -646,7 +704,14 @@ export enum UNIVERSAL_MANAGE_IDS {
646
704
  APPROVE_TOKEN1 = 'approve_token1',
647
705
  APPROVE_TOKEN2 = 'approve_token2',
648
706
  APPROVE_BRING_LIQUIDITY = 'approve_bring_liquidity',
649
- BRING_LIQUIDITY = 'bring_liquidity'
707
+ BRING_LIQUIDITY = 'bring_liquidity',
708
+
709
+ // defi spring claim
710
+ DEFISPRING_REWARDS = 'defispring_rewards',
711
+
712
+ // avnu swaps
713
+ APPROVE_SWAP_TOKEN1 = 'approve_swap_token1',
714
+ AVNU_SWAP_REWARDS = 'avnu_swap_rewards'
650
715
  }
651
716
 
652
717
  export enum UNIVERSAL_ADAPTERS {
@@ -707,7 +772,15 @@ function getLooperSettings(
707
772
  // to bridge liquidity back to vault (used by bring_liquidity)
708
773
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
709
774
  vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
710
- return vaultSettings;
775
+
776
+ // claim rewards
777
+ vaultSettings.leafAdapters.push(vesuAdapterUSDCETH.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterUSDCETH));
778
+
779
+ // avnu swap
780
+ const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
781
+ vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_MIDDLEWARE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
782
+ vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(STRKToken.address, USDCToken.address, UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS).bind(commonAdapter));
783
+ return vaultSettings;
711
784
  }
712
785
 
713
786
  const _riskFactor: RiskFactor[] = [