@strkfarm/sdk 1.1.17 → 1.1.19
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 +2514 -542
- package/dist/index.browser.mjs +2256 -284
- package/dist/index.d.ts +16 -4
- package/dist/index.js +2257 -284
- package/dist/index.mjs +2256 -284
- package/package.json +2 -1
- package/src/dataTypes/_bignumber.ts +29 -11
- package/src/modules/pricer-from-api.ts +1 -1
- package/src/strategies/base-strategy.ts +5 -1
- package/src/strategies/constants.ts +1 -1
- package/src/strategies/ekubo-cl-vault.tsx +45 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
- package/src/strategies/universal-adapters/vesu-adapter.ts +63 -28
- package/src/strategies/universal-lst-muliplier-strategy.tsx +65 -24
- package/src/strategies/universal-strategy.tsx +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strkfarm/sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.19",
|
|
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",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"@noble/curves": "^1.0.0",
|
|
63
63
|
"@noble/hashes": "^2.0.0",
|
|
64
64
|
"@scure/starknet": "^2.0.0",
|
|
65
|
+
"@strkfarm/sdk": "link:",
|
|
65
66
|
"bignumber.js": "4.0.4",
|
|
66
67
|
"browser-assert": "^1.2.1",
|
|
67
68
|
"chalk": "^4.1.2",
|
|
@@ -10,27 +10,42 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
toWei() {
|
|
13
|
-
return
|
|
13
|
+
return super.mul(10 ** this.decimals).toFixed(0);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
multipliedBy(value: string | number | T): T {
|
|
17
17
|
const _value = this.getStandardString(value);
|
|
18
|
-
return this.construct(
|
|
18
|
+
return this.construct(super.mul(_value).toString(), this.decimals);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
dividedBy(value: string | number | T): T {
|
|
22
22
|
const _value = this.getStandardString(value);
|
|
23
|
-
return this.construct(
|
|
23
|
+
return this.construct(super.dividedBy(_value).toString(), this.decimals);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
plus(value: string | number | T): T {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// Convert to raw number/string to avoid calling overridden methods
|
|
28
|
+
const rawValue = this.getStandardString(value);
|
|
29
|
+
|
|
30
|
+
// Create a fresh BigNumber instance to do the math
|
|
31
|
+
const thisBN = new BigNumber(this.toString());
|
|
32
|
+
const result = thisBN.plus(rawValue);
|
|
33
|
+
|
|
34
|
+
return this.construct(result.toString(), this.decimals);
|
|
35
|
+
// const _value = this.getStandardString(value);
|
|
36
|
+
// return this.construct(super.plus(_value).toString(), this.decimals);
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
minus(n: number | string | T, base?: number): T {
|
|
32
|
-
const
|
|
33
|
-
|
|
40
|
+
const rawValue = this.getStandardString(n);
|
|
41
|
+
|
|
42
|
+
// Create a fresh BigNumber instance to do the math
|
|
43
|
+
const thisBN = new BigNumber(this.toString());
|
|
44
|
+
const result = thisBN.minus(rawValue, base);
|
|
45
|
+
|
|
46
|
+
return this.construct(result.toString(), this.decimals);
|
|
47
|
+
// const _value = this.getStandardString(n);
|
|
48
|
+
// return this.construct(super.minus(_value, base).toString(), this.decimals);
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
protected construct(value: string | number, decimals: number): T {
|
|
@@ -56,10 +71,15 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
private getStandardString(value: string | number | T): string {
|
|
59
|
-
if (typeof value
|
|
74
|
+
if (typeof value === "string") {
|
|
60
75
|
return value;
|
|
61
76
|
}
|
|
62
|
-
|
|
77
|
+
if (typeof value === "number") {
|
|
78
|
+
return value.toString();
|
|
79
|
+
}
|
|
80
|
+
// For _Web3Number instances, use the parent BigNumber's toString
|
|
81
|
+
// to avoid triggering our overridden methods
|
|
82
|
+
return BigNumber.prototype.toString.call(value);
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
minimum(value: string | number | T): T {
|
|
@@ -72,13 +92,11 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
|
72
92
|
maximum(value: string | number | T): T {
|
|
73
93
|
const _value = new BigNumber(value);
|
|
74
94
|
const _valueMe = new BigNumber(this.toString());
|
|
75
|
-
console.warn(`maximum: _value: ${_value.toString()}, _valueMe: ${_valueMe.toString()}`);
|
|
76
95
|
const answer = _value.greaterThanOrEqualTo(_valueMe) ? _value : _valueMe;
|
|
77
96
|
return this.construct(answer.toString(), this.decimals);
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
abs(): T {
|
|
81
|
-
console.warn(`abs: this: ${this}`);
|
|
82
100
|
return this.construct(Math.abs(this.toNumber()).toFixed(12), this.decimals);
|
|
83
101
|
}
|
|
84
102
|
|
|
@@ -42,7 +42,7 @@ export class PricerFromApi extends PricerBase {
|
|
|
42
42
|
|
|
43
43
|
async getPriceFromMyAPI(tokenSymbol: string) {
|
|
44
44
|
logger.verbose(`getPrice from redis: ${tokenSymbol}`);
|
|
45
|
-
const endpoint = 'https://
|
|
45
|
+
const endpoint = 'https://proxy.api.troves.fi'
|
|
46
46
|
const url = `${endpoint}/api/price/${tokenSymbol}`;
|
|
47
47
|
const priceInfoRes = await fetch(url);
|
|
48
48
|
const priceInfo = await priceInfoRes.json();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
-
import { IConfig, TokenInfo } from "@/interfaces";
|
|
2
|
+
import { IConfig, TokenInfo, VaultPosition } from "@/interfaces";
|
|
3
3
|
import { CacheClass } from "@/utils/cacheClass";
|
|
4
4
|
import { Call } from "starknet";
|
|
5
5
|
|
|
@@ -51,4 +51,8 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
|
|
|
51
51
|
async withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
|
|
52
52
|
throw new Error("Not implemented");
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
async getVaultPositions(): Promise<VaultPosition[]> {
|
|
56
|
+
throw new Error("Not implemented");
|
|
57
|
+
}
|
|
54
58
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
RiskFactor,
|
|
12
12
|
RiskType,
|
|
13
13
|
TokenInfo,
|
|
14
|
+
VaultPosition,
|
|
14
15
|
} from "@/interfaces";
|
|
15
16
|
import { PricerBase } from "@/modules/pricerBase";
|
|
16
17
|
import { assert } from "@/utils";
|
|
@@ -634,6 +635,50 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
634
635
|
return this.tickToi129(tick);
|
|
635
636
|
}
|
|
636
637
|
|
|
638
|
+
async getVaultPositions(): Promise<VaultPosition[]> {
|
|
639
|
+
const tvlInfo = await this.getTVL();
|
|
640
|
+
const fees = await this.getUncollectedFees();
|
|
641
|
+
const unusedBalance = await this.unusedBalances();
|
|
642
|
+
return [
|
|
643
|
+
{
|
|
644
|
+
amount: tvlInfo.token0.amount,
|
|
645
|
+
usdValue: tvlInfo.token0.usdValue,
|
|
646
|
+
token: tvlInfo.token0.tokenInfo,
|
|
647
|
+
remarks: `Liquidity in Ekubo`
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
amount: fees.token0.amount,
|
|
651
|
+
usdValue: fees.token0.usdValue,
|
|
652
|
+
token: fees.token0.tokenInfo,
|
|
653
|
+
remarks: "Uncollected Fees in Ekubo"
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
amount: unusedBalance.token0.amount,
|
|
657
|
+
usdValue: unusedBalance.token0.usdValue,
|
|
658
|
+
token: unusedBalance.token0.tokenInfo,
|
|
659
|
+
remarks: "Unused Balance in the Vault"
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
amount: tvlInfo.token1.amount,
|
|
663
|
+
usdValue: tvlInfo.token1.usdValue,
|
|
664
|
+
token: tvlInfo.token1.tokenInfo,
|
|
665
|
+
remarks: "Liquidity in Ekubo"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
amount: fees.token1.amount,
|
|
669
|
+
usdValue: fees.token1.usdValue,
|
|
670
|
+
token: fees.token1.tokenInfo,
|
|
671
|
+
remarks: "Uncollected Fees in Ekubo"
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
amount: unusedBalance.token1.amount,
|
|
675
|
+
usdValue: unusedBalance.token1.usdValue,
|
|
676
|
+
token: unusedBalance.token1.tokenInfo,
|
|
677
|
+
remarks: "Unused Balance in the Vault"
|
|
678
|
+
}
|
|
679
|
+
];
|
|
680
|
+
}
|
|
681
|
+
|
|
637
682
|
async getPoolKey(
|
|
638
683
|
blockIdentifier: BlockIdentifier = "latest"
|
|
639
684
|
): Promise<EkuboPoolKey> {
|
|
@@ -3,10 +3,11 @@ import { ContractAddr } from "@/dataTypes";
|
|
|
3
3
|
// Zellic audited
|
|
4
4
|
export const SIMPLE_SANITIZER = ContractAddr.from('0x5a2e3ceb3da368b983a8717898427ab7b6daf04014b70f321e777f9aad940b4');
|
|
5
5
|
// Without flashloan options
|
|
6
|
-
export const SIMPLE_SANITIZER_V2 = ContractAddr.from('
|
|
6
|
+
export const SIMPLE_SANITIZER_V2 = ContractAddr.from('0x7b6f98311af8aa425278570e62abf523e6462eaa01a38c1feab9b2f416492e2');
|
|
7
7
|
export const PRICE_ROUTER = ContractAddr.from('0x05e83Fa38D791d2dba8E6f487758A9687FfEe191A6Cf8a6c5761ab0a110DB837');
|
|
8
8
|
export const AVNU_MIDDLEWARE = ContractAddr.from('0x4a7972ed3f5d1e74a6d6c4a8f467666953d081c8f2270390cc169d50d17cb0d');
|
|
9
9
|
|
|
10
|
+
export const VESU_SINGLETON = ContractAddr.from('0x000d8d6dfec4d33bfb6895de9f3852143a17c6f92fd2a21da3d6924d34870160');
|
|
10
11
|
export function toBigInt(value: string | number): bigint {
|
|
11
12
|
if (typeof value === 'string') {
|
|
12
13
|
return BigInt(value);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LeafData, logger } from "@/utils"
|
|
2
2
|
import { CairoCustomEnum, Contract, hash, num, RpcProvider, shortString, uint256, Uint256 } from "starknet";
|
|
3
|
-
import { SIMPLE_SANITIZER, SIMPLE_SANITIZER_V2, toBigInt } from "./adapter-utils";
|
|
3
|
+
import { SIMPLE_SANITIZER, SIMPLE_SANITIZER_V2, toBigInt, VESU_SINGLETON } from "./adapter-utils";
|
|
4
4
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
5
|
import { AdapterLeafType, BaseAdapter, GenerateCallFn, LeafAdapterFn, ManageCall } from "./baseAdapter";
|
|
6
6
|
import VesuSingletonAbi from '../../data/vesu-singleton.abi.json';
|
|
7
|
-
import { IConfig, TokenInfo, VaultPosition } from "@/interfaces";
|
|
7
|
+
import { getMainnetConfig, IConfig, TokenInfo, VaultPosition } from "@/interfaces";
|
|
8
8
|
import { PricerBase } from "@/modules/pricerBase";
|
|
9
9
|
import VesuPoolIDs from "@/data/vesu_pools.json";
|
|
10
10
|
import { getAPIUsingHeadlessBrowser } from "@/node/headless";
|
|
@@ -13,6 +13,7 @@ import { VESU_REWARDS_CONTRACT } from "@/modules/harvests";
|
|
|
13
13
|
import { ENDPOINTS } from "../constants";
|
|
14
14
|
import VesuMultiplyAbi from '@/data/vesu-multiple.abi.json';
|
|
15
15
|
import { EkuboPoolKey } from "../ekubo-cl-vault";
|
|
16
|
+
import VesuPoolV2Abi from '@/data/vesu-pool-v2.abi.json';
|
|
16
17
|
|
|
17
18
|
interface VesuPoolsInfo { pools: any[]; isErrorPoolsAPI: boolean };
|
|
18
19
|
|
|
@@ -212,12 +213,20 @@ function getVesuMultiplyParams(isIncrease: boolean, params: IncreaseLeverParams
|
|
|
212
213
|
|
|
213
214
|
export const VesuPools = {
|
|
214
215
|
Genesis: ContractAddr.from('0x4dc4f0ca6ea4961e4c8373265bfd5317678f4fe374d76f3fd7135f57763bf28'),
|
|
215
|
-
Re7xSTRK: ContractAddr.from('0x052fb52363939c3aa848f8f4ac28f0a51379f8d1b971d8444de25fbd77d8f161')
|
|
216
|
+
Re7xSTRK: ContractAddr.from('0x052fb52363939c3aa848f8f4ac28f0a51379f8d1b971d8444de25fbd77d8f161'),
|
|
217
|
+
Re7xBTC: ContractAddr.from('0x3a8416bf20d036df5b1cf3447630a2e1cb04685f6b0c3a70ed7fb1473548ecf')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function getVesuSingletonAddress(vesuPool: ContractAddr) {
|
|
221
|
+
if (vesuPool.eq(VesuPools.Genesis) ||
|
|
222
|
+
vesuPool.eq(VesuPools.Re7xSTRK)) {
|
|
223
|
+
return {addr: VESU_SINGLETON, isV2: false};
|
|
224
|
+
}
|
|
225
|
+
return {addr: vesuPool, isV2: true}; // Vesu v2
|
|
216
226
|
}
|
|
217
227
|
|
|
218
228
|
export class VesuAdapter extends BaseAdapter {
|
|
219
|
-
|
|
220
|
-
VESU_MULTIPLY = ContractAddr.from('0x3630f1f8e5b8f5c4c4ae9b6620f8a570ae55cddebc0276c37550e7c118edf67');
|
|
229
|
+
VESU_MULTIPLY = ContractAddr.from('0x027fef272d0a9a3844767c851a64b36fe4f0115141d81134baade95d2b27b781');
|
|
221
230
|
config: VesuAdapterConfig;
|
|
222
231
|
networkConfig: IConfig | undefined;
|
|
223
232
|
pricer: PricerBase | undefined;
|
|
@@ -239,7 +248,7 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
239
248
|
];
|
|
240
249
|
const output = this.constructSimpleLeafData({
|
|
241
250
|
id: this.config.id,
|
|
242
|
-
target: this.
|
|
251
|
+
target: getVesuSingletonAddress(this.config.poolId).addr,
|
|
243
252
|
method: 'modify_position',
|
|
244
253
|
packedArguments
|
|
245
254
|
});
|
|
@@ -299,9 +308,15 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
299
308
|
}
|
|
300
309
|
};
|
|
301
310
|
logger.verbose(`VesuAdapter::ConstructingModify::Debt::${JSON.stringify(_debt)}`)
|
|
302
|
-
const
|
|
303
|
-
const call =
|
|
304
|
-
params: {
|
|
311
|
+
const { contract, isV2 } = this.getVesuSingletonContract(getMainnetConfig(), this.config.poolId);
|
|
312
|
+
const call = contract.populate('modify_position', {
|
|
313
|
+
params: isV2 ? {
|
|
314
|
+
collateral_asset: this.config.collateral.address.toBigInt(),
|
|
315
|
+
debt_asset: this.config.debt.address.toBigInt(),
|
|
316
|
+
user: this.config.vaultAllocator.toBigInt(),
|
|
317
|
+
collateral: _collateral,
|
|
318
|
+
debt: _debt,
|
|
319
|
+
} : {
|
|
305
320
|
pool_id: this.config.poolId.toBigInt(),
|
|
306
321
|
collateral_asset: this.config.collateral.address.toBigInt(),
|
|
307
322
|
debt_asset: this.config.debt.address.toBigInt(),
|
|
@@ -314,7 +329,7 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
314
329
|
return {
|
|
315
330
|
sanitizer: SIMPLE_SANITIZER,
|
|
316
331
|
call: {
|
|
317
|
-
contractAddress:
|
|
332
|
+
contractAddress: ContractAddr.from(contract.address),
|
|
318
333
|
selector: hash.getSelectorFromName('modify_position'),
|
|
319
334
|
calldata: [
|
|
320
335
|
...call.calldata as bigint[]
|
|
@@ -372,12 +387,11 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
372
387
|
getVesuModifyDelegationAdapter = (id: string): LeafAdapterFn<VesuModifyDelegationCallParams> => {
|
|
373
388
|
return () => {
|
|
374
389
|
const packedArguments: bigint[] = [
|
|
375
|
-
toBigInt(this.config.poolId.toString()), // pool id
|
|
376
390
|
toBigInt(this.VESU_MULTIPLY.toString()), // vault allocator
|
|
377
391
|
];
|
|
378
392
|
const output = this.constructSimpleLeafData({
|
|
379
393
|
id: id,
|
|
380
|
-
target: this.
|
|
394
|
+
target: getVesuSingletonAddress(this.config.poolId).addr,
|
|
381
395
|
method: 'modify_delegation',
|
|
382
396
|
packedArguments
|
|
383
397
|
}, SIMPLE_SANITIZER_V2);
|
|
@@ -387,8 +401,12 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
387
401
|
}
|
|
388
402
|
|
|
389
403
|
getVesuModifyDelegationCall = (params: VesuModifyDelegationCallParams): ManageCall => {
|
|
390
|
-
const
|
|
391
|
-
const
|
|
404
|
+
const VESU_SINGLETON = getVesuSingletonAddress(this.config.poolId).addr;
|
|
405
|
+
const { contract, isV2 } = this.getVesuSingletonContract(getMainnetConfig(), this.config.poolId);
|
|
406
|
+
const call = contract.populate('modify_delegation', isV2 ? {
|
|
407
|
+
delegatee: this.VESU_MULTIPLY.toBigInt(),
|
|
408
|
+
delegation: params.delegation,
|
|
409
|
+
} : {
|
|
392
410
|
pool_id: this.config.poolId.toBigInt(),
|
|
393
411
|
delegatee: this.VESU_MULTIPLY.toBigInt(),
|
|
394
412
|
delegation: params.delegation,
|
|
@@ -396,7 +414,7 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
396
414
|
return {
|
|
397
415
|
sanitizer: SIMPLE_SANITIZER_V2,
|
|
398
416
|
call: {
|
|
399
|
-
contractAddress:
|
|
417
|
+
contractAddress: VESU_SINGLETON,
|
|
400
418
|
selector: hash.getSelectorFromName('modify_delegation'),
|
|
401
419
|
calldata: [
|
|
402
420
|
...call.calldata as bigint[]
|
|
@@ -458,8 +476,13 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
458
476
|
throw new Error(`Unknown VesuAmountDenomination: ${denomination}`);
|
|
459
477
|
}
|
|
460
478
|
|
|
461
|
-
getVesuSingletonContract(config: IConfig) {
|
|
462
|
-
|
|
479
|
+
getVesuSingletonContract(config: IConfig, poolId: ContractAddr) {
|
|
480
|
+
const { addr: VESU_SINGLETON, isV2 } = getVesuSingletonAddress(poolId);
|
|
481
|
+
const ABI = isV2 ? VesuPoolV2Abi : VesuSingletonAbi;
|
|
482
|
+
return {
|
|
483
|
+
contract: new Contract({abi: ABI, address: VESU_SINGLETON.address, providerOrAccount: config.provider}),
|
|
484
|
+
isV2
|
|
485
|
+
}
|
|
463
486
|
}
|
|
464
487
|
|
|
465
488
|
async getLTVConfig(config: IConfig) {
|
|
@@ -468,9 +491,16 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
468
491
|
if (cacheData) {
|
|
469
492
|
return cacheData as number;
|
|
470
493
|
}
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
494
|
+
const { contract, isV2 } = await this.getVesuSingletonContract(config, this.config.poolId);
|
|
495
|
+
let ltv = 0;
|
|
496
|
+
if (isV2) {
|
|
497
|
+
const output: any = await contract.call('pair_config', [this.config.collateral.address.address, this.config.debt.address.address]);
|
|
498
|
+
ltv = Number(output.max_ltv) / 1e18;
|
|
499
|
+
} else {
|
|
500
|
+
const output: any = await contract.call('ltv_config', [this.config.poolId.address, this.config.collateral.address.address, this.config.debt.address.address]);
|
|
501
|
+
ltv = Number(output.max_ltv) / 1e18;
|
|
502
|
+
}
|
|
503
|
+
this.setCache(CACHE_KEY, ltv, 300000); // ttl: 5min
|
|
474
504
|
return this.getCache<number>(CACHE_KEY) as number;
|
|
475
505
|
}
|
|
476
506
|
|
|
@@ -484,9 +514,11 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
484
514
|
if (cacheData) {
|
|
485
515
|
return cacheData;
|
|
486
516
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
517
|
+
|
|
518
|
+
const { contract, isV2} = this.getVesuSingletonContract(config, this.config.poolId);
|
|
519
|
+
const output: any = await contract
|
|
520
|
+
.call(isV2 ? 'position' : 'position_unsafe', [...(isV2 ?
|
|
521
|
+
[]: [this.config.poolId.address]), // exclude pool id in v2
|
|
490
522
|
this.config.collateral.address.address,
|
|
491
523
|
this.config.debt.address.address,
|
|
492
524
|
this.config.vaultAllocator.address
|
|
@@ -494,7 +526,8 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
494
526
|
|
|
495
527
|
const token1Price = await this.pricer.getPrice(this.config.collateral.symbol);
|
|
496
528
|
const token2Price = await this.pricer.getPrice(this.config.debt.symbol);
|
|
497
|
-
|
|
529
|
+
logger.verbose(`VesuAdapter::getPositions token1Price: ${token1Price.price}, token2Price: ${token2Price.price}`);
|
|
530
|
+
|
|
498
531
|
const collateralAmount = Web3Number.fromWei(output['1'].toString(), this.config.collateral.decimals);
|
|
499
532
|
const debtAmount = Web3Number.fromWei(output['2'].toString(), this.config.debt.decimals);
|
|
500
533
|
const value = [{
|
|
@@ -522,9 +555,11 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
522
555
|
if (cacheData) {
|
|
523
556
|
return cacheData;
|
|
524
557
|
}
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
558
|
+
const { contract, isV2 } = this.getVesuSingletonContract(config, this.config.poolId);
|
|
559
|
+
const output: any = await contract
|
|
560
|
+
.call(isV2 ? 'check_collateralization' : 'check_collateralization_unsafe', [
|
|
561
|
+
...(isV2 ?
|
|
562
|
+
[]: [this.config.poolId.address]), // exclude pool id in v2
|
|
528
563
|
this.config.collateral.address.address,
|
|
529
564
|
this.config.debt.address.address,
|
|
530
565
|
this.config.vaultAllocator.address
|
|
@@ -598,7 +633,7 @@ export class VesuAdapter extends BaseAdapter {
|
|
|
598
633
|
|
|
599
634
|
// Vesu API is unstable sometimes, some Pools may be missing sometimes
|
|
600
635
|
for (const pool of VesuPoolIDs.data) {
|
|
601
|
-
const found = pools.find((d: any) => d.id
|
|
636
|
+
const found = pools.find((d: any) => ContractAddr.from(d.id).eqString(pool.id));
|
|
602
637
|
if (!found) {
|
|
603
638
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
604
639
|
logger.verbose(
|
|
@@ -17,7 +17,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
17
17
|
|
|
18
18
|
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
19
19
|
|
|
20
|
-
constructor(config: IConfig, pricer:
|
|
20
|
+
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<UniversalStrategySettings>) {
|
|
21
21
|
super(config, pricer, metadata);
|
|
22
22
|
|
|
23
23
|
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
@@ -30,8 +30,17 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
asset() {
|
|
34
|
+
const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
|
|
35
|
+
return vesuAdapter1.config.collateral;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getTag() {
|
|
39
|
+
return `${UniversalLstMultiplierStrategy.name}:${this.metadata.name}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
// only one leg is used
|
|
34
|
-
// todo support lending assets of underlying as well
|
|
43
|
+
// todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
|
|
35
44
|
getVesuAdapters() {
|
|
36
45
|
const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
|
|
37
46
|
vesuAdapter1.pricer = this.pricer;
|
|
@@ -42,9 +51,13 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
42
51
|
|
|
43
52
|
// not applicable for this strategy
|
|
44
53
|
// No rewards on collateral or borrowing of LST assets
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
|
|
55
|
+
const lstToken = this.asset();
|
|
56
|
+
if (lstToken.symbol === 'xSTRK') {
|
|
57
|
+
return super.getRewardsAUM(prevAum);
|
|
58
|
+
}
|
|
59
|
+
return Web3Number.fromWei("0", lstToken.decimals);
|
|
60
|
+
}
|
|
48
61
|
|
|
49
62
|
async getLSTDexPrice() {
|
|
50
63
|
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
@@ -74,6 +87,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
74
87
|
}) {
|
|
75
88
|
const [vesuAdapter1] = this.getVesuAdapters();
|
|
76
89
|
const legLTV = await vesuAdapter1.getLTVConfig(this.config);
|
|
90
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
77
91
|
|
|
78
92
|
const existingPositions = await vesuAdapter1.getPositions(this.config);
|
|
79
93
|
const collateralisation = await vesuAdapter1.getCollateralization(this.config);
|
|
@@ -103,8 +117,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
103
117
|
.multipliedBy(params.isDeposit ? 1 : -1)
|
|
104
118
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
|
|
105
119
|
const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
|
|
120
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
|
|
106
121
|
const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
|
|
122
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
|
|
107
123
|
const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
|
|
124
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
|
|
108
125
|
const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
|
|
109
126
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
|
|
110
127
|
logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
|
|
@@ -159,12 +176,20 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
159
176
|
lstDexPriceInUnderlying: number,
|
|
160
177
|
isIncrease: boolean
|
|
161
178
|
}): Promise<Call[]> {
|
|
179
|
+
logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
|
|
162
180
|
assert(!params.marginAmount.isZero() || !params.debtAmount.isZero(), 'Deposit/debt must be non-0');
|
|
163
|
-
|
|
181
|
+
|
|
164
182
|
const [vesuAdapter1] = this.getVesuAdapters();
|
|
165
183
|
const lstTokenInfo = this.asset();
|
|
166
184
|
const lstUnderlyingTokenInfo = vesuAdapter1.config.debt;
|
|
167
185
|
|
|
186
|
+
// todo make it more general
|
|
187
|
+
// 500k STRK (~75k$) or 0.5 BTC (~60k$)
|
|
188
|
+
const maxAmounts = lstTokenInfo.symbol == 'xSTRK' ? 500000 : 0.5;
|
|
189
|
+
if (params.marginAmount.greaterThan(maxAmounts)) {
|
|
190
|
+
throw new Error(`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
168
193
|
const proofsIDs: string[] = [];
|
|
169
194
|
const manageCalls: ManageCall[] = [];
|
|
170
195
|
|
|
@@ -181,13 +206,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
181
206
|
}
|
|
182
207
|
|
|
183
208
|
const lstDexPriceInUnderlying = params.lstDexPriceInUnderlying;
|
|
209
|
+
const lstTrueExchangeRate = await this.getLSTExchangeRate();
|
|
184
210
|
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
185
211
|
|
|
186
|
-
// compute quotes for margin swap
|
|
187
|
-
const marginSwap: Swap[] = [];
|
|
188
|
-
|
|
189
212
|
// compute quotes for lever swap
|
|
190
|
-
const MAX_SLIPPAGE = 0.
|
|
213
|
+
const MAX_SLIPPAGE = 0.002;
|
|
191
214
|
// when increasing, debt is swapped to collateral (LST)
|
|
192
215
|
// when decreasing, collateral is swapped to debt (underlying)
|
|
193
216
|
// but both cases, we denominate amount in underlying. negative for decrease (exact amount out)
|
|
@@ -198,11 +221,29 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
198
221
|
toToken.address.address,
|
|
199
222
|
params.debtAmount // negative for exact amount out
|
|
200
223
|
);
|
|
201
|
-
|
|
224
|
+
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
225
|
+
// Ekubo's price impact can randomly show high numbers sometimes.
|
|
226
|
+
assert(leverSwapQuote.price_impact < 0.01, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
|
|
202
227
|
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
228
|
+
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
229
|
+
|
|
230
|
+
// todo double check this logic
|
|
231
|
+
// is Deposit
|
|
232
|
+
let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
|
|
233
|
+
const minLSTReceivedAsPerTruePrice = params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
|
|
234
|
+
if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
|
|
235
|
+
minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
|
|
236
|
+
}
|
|
237
|
+
logger.verbose(`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
|
|
238
|
+
|
|
239
|
+
// is withdraw
|
|
240
|
+
let maxUsedCollateral = params.debtAmount.abs().dividedBy(lstDexPriceInUnderlying).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
|
|
241
|
+
const maxUsedCollateralInLST = params.debtAmount.abs().dividedBy(lstTrueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
|
|
242
|
+
logger.verbose(`${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
|
|
243
|
+
if (maxUsedCollateralInLST > maxUsedCollateral) {
|
|
244
|
+
maxUsedCollateral = maxUsedCollateralInLST;
|
|
245
|
+
}
|
|
246
|
+
|
|
206
247
|
const STEP2_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON;
|
|
207
248
|
const manage2Info = this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
|
|
208
249
|
const manageCall2 = manage2Info.callConstructor({
|
|
@@ -216,10 +257,10 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
|
|
|
216
257
|
isIncrease: true,
|
|
217
258
|
increaseParams: {
|
|
218
259
|
add_margin: params.marginAmount,
|
|
219
|
-
margin_swap:
|
|
260
|
+
margin_swap: [],
|
|
220
261
|
margin_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
|
|
221
262
|
lever_swap: leverSwap,
|
|
222
|
-
lever_swap_limit_amount:
|
|
263
|
+
lever_swap_limit_amount: minLSTReceived
|
|
223
264
|
}
|
|
224
265
|
} : {
|
|
225
266
|
isIncrease: false,
|
|
@@ -425,7 +466,7 @@ const hyperxWBTC: UniversalStrategySettings = {
|
|
|
425
466
|
manager: ContractAddr.from('0x75866db44c81e6986f06035206ee9c7d15833ddb22d6a22c016cfb5c866a491'),
|
|
426
467
|
vaultAllocator: ContractAddr.from('0x57b5c1bb457b5e840a2714ae53ada87d77be2f3fd33a59b4fe709ef20c020c1'),
|
|
427
468
|
redeemRequestNFT: ContractAddr.from('0x7a5dc288325456f05e70e9616e16bc02ffbe448f4b89f80b47c0970b989c7c'),
|
|
428
|
-
aumOracle: ContractAddr.from(''),
|
|
469
|
+
aumOracle: ContractAddr.from('0x258f8a0ca0d21f542e48ad89d00e92dc4d9db4999084f50ef9c22dfb1e83023'),
|
|
429
470
|
leafAdapters: [],
|
|
430
471
|
adapters: [],
|
|
431
472
|
targetHealthFactor: 1.1,
|
|
@@ -437,7 +478,7 @@ const hyperxtBTC: UniversalStrategySettings = {
|
|
|
437
478
|
manager: ContractAddr.from('0xc4cc3e08029a0ae076f5fdfca70575abb78d23c5cd1c49a957f7e697885401'),
|
|
438
479
|
vaultAllocator: ContractAddr.from('0x50bbd4fe69f841ecb13b2619fe50ebfa4e8944671b5d0ebf7868fd80c61b31e'),
|
|
439
480
|
redeemRequestNFT: ContractAddr.from('0xeac9032f02057779816e38a6cb9185d12d86b3aacc9949b96b36de359c1e3'),
|
|
440
|
-
aumOracle: ContractAddr.from(''),
|
|
481
|
+
aumOracle: ContractAddr.from('0x7e0d05cb7ba3f7db77a36c21c21583b5a524c2e685c08c24b3554911fb4a039'),
|
|
441
482
|
leafAdapters: [],
|
|
442
483
|
adapters: [],
|
|
443
484
|
targetHealthFactor: 1.1,
|
|
@@ -449,7 +490,7 @@ const hyperxsBTC: UniversalStrategySettings = {
|
|
|
449
490
|
manager: ContractAddr.from('0xc9ac023090625b0be3f6532ca353f086746f9c09f939dbc1b2613f09e5f821'),
|
|
450
491
|
vaultAllocator: ContractAddr.from('0x60c2d856936b975459a5b4eb28b8672d91f757bd76cebb6241f8d670185dc01'),
|
|
451
492
|
redeemRequestNFT: ContractAddr.from('0x429e8ee8bc7ecd1ade72630d350a2e0f10f9a2507c45f188ba17fe8f2ab4cf3'),
|
|
452
|
-
aumOracle: ContractAddr.from(''),
|
|
493
|
+
aumOracle: ContractAddr.from('0x149298ade3e79ec6cbdac6cfad289c57504eaf54e590939136ed1ceca60c345'),
|
|
453
494
|
leafAdapters: [],
|
|
454
495
|
adapters: [],
|
|
455
496
|
targetHealthFactor: 1.1,
|
|
@@ -487,7 +528,7 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
|
|
|
487
528
|
launchBlock: 0,
|
|
488
529
|
type: 'Other',
|
|
489
530
|
depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
|
|
490
|
-
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7xSTRK),
|
|
531
|
+
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
|
|
491
532
|
risk: {
|
|
492
533
|
riskFactor: _riskFactor,
|
|
493
534
|
netRisk:
|
|
@@ -507,8 +548,8 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
|
|
|
507
548
|
export const HyperLSTStrategies: IStrategyMetadata<UniversalStrategySettings>[] =
|
|
508
549
|
[
|
|
509
550
|
getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false),
|
|
510
|
-
getStrategySettings('xWBTC', 'WBTC', hyperxWBTC,
|
|
511
|
-
getStrategySettings('xtBTC', 'tBTC', hyperxtBTC,
|
|
512
|
-
getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC,
|
|
513
|
-
getStrategySettings('xLBTC', 'LBTC', hyperxLBTC,
|
|
551
|
+
getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false),
|
|
552
|
+
getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false),
|
|
553
|
+
getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false),
|
|
554
|
+
getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false),
|
|
514
555
|
]
|