@strkfarm/sdk 2.0.0-dev.5 → 2.0.0-dev.50
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 +190 -36
- package/dist/cli.mjs +188 -34
- package/dist/index.browser.global.js +119400 -92821
- package/dist/index.browser.mjs +13293 -11146
- package/dist/index.d.ts +2281 -1938
- package/dist/index.js +13532 -11179
- package/dist/index.mjs +14165 -11836
- package/package.json +59 -60
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/data/redeem-request-nft.abi.json +752 -0
- package/src/data/universal-vault.abi.json +8 -7
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/bignumber.browser.ts +10 -1
- package/src/dataTypes/bignumber.node.ts +10 -1
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +279 -233
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +228 -6
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +21 -12
- package/src/modules/ekubo-pricer.ts +80 -0
- package/src/modules/ekubo-quoter.ts +48 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/index.ts +2 -1
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-avnu-api.ts +114 -0
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +107 -41
- package/src/modules/pricerBase.ts +2 -1
- package/src/modules/zkLend.ts +3 -2
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +3 -1
- package/src/strategies/base-strategy.ts +168 -16
- package/src/strategies/constants.ts +8 -3
- package/src/strategies/ekubo-cl-vault.tsx +1048 -355
- package/src/strategies/factory.ts +199 -0
- package/src/strategies/index.ts +5 -3
- package/src/strategies/registry.ts +262 -0
- package/src/strategies/sensei.ts +354 -10
- package/src/strategies/svk-strategy.ts +292 -31
- package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1261 -0
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +4 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +196 -272
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/index.ts +10 -8
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +511 -0
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +120 -82
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +525 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +866 -860
- package/src/strategies/universal-adapters/vesu-position-common.ts +258 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +895 -416
- package/src/strategies/universal-strategy.tsx +1332 -1173
- package/src/strategies/vesu-rebalance.tsx +254 -153
- package/src/strategies/yoloVault.ts +1096 -0
- package/src/utils/cacheClass.ts +11 -2
- package/src/utils/health-factor-math.ts +33 -1
- package/src/utils/index.ts +3 -1
- package/src/utils/logger.browser.ts +22 -4
- package/src/utils/logger.node.ts +259 -24
- package/src/utils/starknet-call-parser.ts +1036 -0
- package/src/utils/strategy-utils.ts +61 -0
- package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
- package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
- package/src/strategies/universal-adapters/extended-adapter.ts +0 -662
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -372
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1140
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
-
import { IConfig, IStrategyMetadata, TokenInfo, VaultPosition } from "@/interfaces";
|
|
2
|
+
import { IConfig, IStrategyMetadata, Protocols, TokenInfo, VaultPosition } from "@/interfaces";
|
|
3
3
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
4
|
import { ERC20 } from "@/modules";
|
|
5
|
-
import { BaseStrategy, SingleActionAmount, SingleTokenInfo } from "./base-strategy";
|
|
6
|
-
import { Call, Contract, num } from "starknet";
|
|
5
|
+
import { BaseStrategy, SingleActionAmount, SingleTokenInfo, UserPositionCard, UserPositionCardsInput } from "./base-strategy";
|
|
6
|
+
import { BlockIdentifier, Call, Contract, num, uint256 } from "starknet";
|
|
7
7
|
import { assert, LeafData, logger, StandardMerkleTree } from "@/utils";
|
|
8
8
|
import { UniversalStrategySettings } from "./universal-strategy";
|
|
9
9
|
import { UNIVERSAL_MANAGE_IDS } from "./universal-strategy";
|
|
@@ -15,9 +15,9 @@ import { ManageCall, PositionInfo } from "./universal-adapters";
|
|
|
15
15
|
* Base class for all SVK (Starknet Vault Kit) strategies.
|
|
16
16
|
* Contains common functions that are shared across all SVK strategies.
|
|
17
17
|
*/
|
|
18
|
-
export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
18
|
+
export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
19
19
|
extends BaseStrategy<SingleTokenInfo, SingleActionAmount> {
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
/** Contract address of the strategy */
|
|
22
22
|
readonly address: ContractAddr;
|
|
23
23
|
/** Pricer instance for token price calculations */
|
|
@@ -36,13 +36,13 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
36
36
|
this.pricer = pricer;
|
|
37
37
|
this.metadata = metadata;
|
|
38
38
|
this.address = metadata.address;
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
this.contract = new Contract({
|
|
41
41
|
abi: UniversalVaultAbi,
|
|
42
42
|
address: this.address.address,
|
|
43
43
|
providerOrAccount: this.config.provider
|
|
44
44
|
});
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
this.managerContract = new Contract({
|
|
47
47
|
abi: ManagerAbi,
|
|
48
48
|
address: this.metadata.additionalInfo.manager.address,
|
|
@@ -57,14 +57,81 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
57
57
|
return this.metadata.depositTokens[0];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async depositCall(amountInfo: SingleActionAmount, receiver: ContractAddr): Promise<Call[]> {
|
|
61
|
+
// Technically its not erc4626 abi, but we just need approve call
|
|
62
|
+
// so, its ok to use it
|
|
63
|
+
assert(
|
|
64
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
65
|
+
"Deposit token mismatch"
|
|
66
|
+
);
|
|
67
|
+
const assetContract = new Contract({
|
|
68
|
+
abi: UniversalVaultAbi,
|
|
69
|
+
address: this.asset().address.address,
|
|
70
|
+
providerOrAccount: this.config.provider
|
|
71
|
+
});
|
|
72
|
+
const call1 = assetContract.populate("approve", [
|
|
73
|
+
this.address.address,
|
|
74
|
+
uint256.bnToUint256(amountInfo.amount.toWei())
|
|
75
|
+
]);
|
|
76
|
+
const call2 = this.contract.populate("deposit", [
|
|
77
|
+
uint256.bnToUint256(amountInfo.amount.toWei()),
|
|
78
|
+
receiver.address
|
|
79
|
+
]);
|
|
80
|
+
return [call1, call2];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async withdrawCall(amountInfo: SingleActionAmount, receiver: ContractAddr, owner: ContractAddr, isMaxWithdraw: boolean = false): Promise<Call[]> {
|
|
84
|
+
assert(
|
|
85
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
86
|
+
"Withdraw token mismatch"
|
|
87
|
+
);
|
|
88
|
+
const shares = isMaxWithdraw
|
|
89
|
+
? await this.contract.call("balanceOf", [receiver.address])
|
|
90
|
+
: await this.contract.call('convert_to_shares', [uint256.bnToUint256(amountInfo.amount.toWei())]);
|
|
91
|
+
const call = this.contract.populate("request_redeem", [
|
|
92
|
+
uint256.bnToUint256(shares.toString()),
|
|
93
|
+
receiver.address,
|
|
94
|
+
owner.address
|
|
95
|
+
]);
|
|
96
|
+
return [call];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getUserTVL(user: ContractAddr, blockIdentifier: BlockIdentifier = "latest"): Promise<SingleTokenInfo> {
|
|
100
|
+
const shares: any = await this.contract.call("balanceOf", [user.address], { blockIdentifier });
|
|
101
|
+
const assets: any = await this.contract.call(
|
|
102
|
+
"convert_to_assets",
|
|
103
|
+
[uint256.bnToUint256(shares)],
|
|
104
|
+
{ blockIdentifier }
|
|
105
|
+
);
|
|
106
|
+
const amount = Web3Number.fromWei(
|
|
107
|
+
assets.toString(),
|
|
108
|
+
this.metadata.depositTokens[0].decimals
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const blockNumber = typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
112
|
+
? Number(blockIdentifier)
|
|
113
|
+
: undefined;
|
|
114
|
+
|
|
115
|
+
const price = await this.pricer.getPrice(
|
|
116
|
+
this.metadata.depositTokens[0].symbol,
|
|
117
|
+
blockNumber
|
|
118
|
+
);
|
|
119
|
+
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
120
|
+
return {
|
|
121
|
+
tokenInfo: this.asset(),
|
|
122
|
+
amount,
|
|
123
|
+
usdValue
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
60
127
|
/**
|
|
61
128
|
* Returns the unused balance in the vault allocator.
|
|
62
129
|
* Note: This function is common for any SVK strategy.
|
|
63
130
|
*/
|
|
64
131
|
async getUnusedBalance(): Promise<SingleTokenInfo> {
|
|
65
132
|
const balance = await (new ERC20(this.config)).balanceOf(
|
|
66
|
-
this.asset().address,
|
|
67
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
133
|
+
this.asset().address,
|
|
134
|
+
this.metadata.additionalInfo.vaultAllocator,
|
|
68
135
|
this.asset().decimals
|
|
69
136
|
);
|
|
70
137
|
const price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
@@ -76,6 +143,24 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
76
143
|
};
|
|
77
144
|
}
|
|
78
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Builds a manage call from an adapter's proof tree.
|
|
148
|
+
* DRY helper for the repeated getProofs → getManageCall pattern.
|
|
149
|
+
*/
|
|
150
|
+
protected async buildManageCallFromAdapter(
|
|
151
|
+
adapter: { getProofs: (isDeposit: boolean, tree: any) => any },
|
|
152
|
+
isDeposit: boolean,
|
|
153
|
+
amount: Web3Number,
|
|
154
|
+
additionalParams?: Record<string, any>,
|
|
155
|
+
): Promise<Call> {
|
|
156
|
+
const proofsInfo = adapter.getProofs(isDeposit, this.getMerkleTree());
|
|
157
|
+
const manageCalls = await proofsInfo.callConstructor({ amount, ...additionalParams });
|
|
158
|
+
return this.getManageCall(
|
|
159
|
+
this.getProofGroupsForManageCalls(manageCalls),
|
|
160
|
+
manageCalls,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
79
164
|
/**
|
|
80
165
|
* Bridges liquidity from the vault allocator back to the vault.
|
|
81
166
|
* Note: This function is common for any SVK strategy.
|
|
@@ -85,37 +170,54 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
85
170
|
const approveAdapter = this.metadata.additionalInfo.leafAdapters.find(
|
|
86
171
|
adapter => adapter().leaves.find(leaf => leaf.readableId === UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY)
|
|
87
172
|
);
|
|
88
|
-
|
|
173
|
+
|
|
89
174
|
if (!approveAdapter) {
|
|
90
175
|
throw new Error(`${this.getTag()}::getBringLiquidityCall: approve adapter not found`);
|
|
91
176
|
}
|
|
92
|
-
|
|
177
|
+
|
|
93
178
|
// Find bring liquidity leaf adapter
|
|
94
179
|
const bringLiquidityAdapter = this.metadata.additionalInfo.leafAdapters.find(
|
|
95
180
|
adapter => adapter().leaves.find(leaf => leaf.readableId === UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
|
|
96
181
|
);
|
|
97
|
-
|
|
182
|
+
|
|
98
183
|
if (!bringLiquidityAdapter) {
|
|
99
184
|
throw new Error(`${this.getTag()}::getBringLiquidityCall: bring liquidity adapter not found`);
|
|
100
185
|
}
|
|
101
|
-
|
|
102
|
-
// Get proofs
|
|
103
|
-
const tree = this.getMerkleTree();
|
|
104
|
-
const proofGroups: string[][] = [];
|
|
105
|
-
for (const [i, v] of tree.entries()) {
|
|
106
|
-
if (v.readableId === UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY ||
|
|
107
|
-
v.readableId === UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY) {
|
|
108
|
-
proofGroups.push(tree.getProof(i));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
186
|
+
|
|
112
187
|
// Construct calls
|
|
113
188
|
logger.verbose("approve adapter amount", params.amount);
|
|
114
189
|
const approveCalls = await approveAdapter().callConstructor({ amount: params.amount });
|
|
115
190
|
const bringLiquidityCalls = await bringLiquidityAdapter().callConstructor({ amount: params.amount });
|
|
116
|
-
|
|
191
|
+
const manageCalls = [...approveCalls, ...bringLiquidityCalls];
|
|
192
|
+
|
|
117
193
|
// Combine into final call
|
|
118
|
-
return this.getManageCall(
|
|
194
|
+
return this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolves ordered merkle proofs from emitted manage calls.
|
|
199
|
+
* This ensures only runtime-required proofs are passed to manager calls.
|
|
200
|
+
*/
|
|
201
|
+
protected getProofGroupsForManageCalls(manageCalls: ManageCall[]): string[][] {
|
|
202
|
+
const tree = this.getMerkleTree();
|
|
203
|
+
const proofByReadableId = new Map<string, string[]>();
|
|
204
|
+
for (const [i, v] of tree.entries()) {
|
|
205
|
+
if (proofByReadableId.has(v.readableId)) {
|
|
206
|
+
throw new Error(`Duplicate readableId found in merkle tree: ${v.readableId}`);
|
|
207
|
+
}
|
|
208
|
+
proofByReadableId.set(v.readableId, tree.getProof(i));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return manageCalls.map((manageCall, index) => {
|
|
212
|
+
if (!manageCall.proofReadableId) {
|
|
213
|
+
throw new Error(`Missing proofReadableId for manage call at index ${index}`);
|
|
214
|
+
}
|
|
215
|
+
const proof = proofByReadableId.get(manageCall.proofReadableId);
|
|
216
|
+
if (!proof) {
|
|
217
|
+
throw new Error(`Proof not found for readableId: ${manageCall.proofReadableId}`);
|
|
218
|
+
}
|
|
219
|
+
return proof;
|
|
220
|
+
});
|
|
119
221
|
}
|
|
120
222
|
|
|
121
223
|
/**
|
|
@@ -157,10 +259,10 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
157
259
|
*/
|
|
158
260
|
getManageCall(proofGroups: string[][], manageCalls: ManageCall[]): Call {
|
|
159
261
|
assert(
|
|
160
|
-
proofGroups.length == manageCalls.length,
|
|
262
|
+
proofGroups.length == manageCalls.length,
|
|
161
263
|
`Proof IDs and Manage Calls length mismatch: ${proofGroups.length} != ${manageCalls.length}`
|
|
162
264
|
);
|
|
163
|
-
|
|
265
|
+
|
|
164
266
|
return this.managerContract.populate('manage_vault_with_merkle_verification', {
|
|
165
267
|
proofs: proofGroups.map(group => group),
|
|
166
268
|
decoder_and_sanitizers: manageCalls.map(call => call.sanitizer.address),
|
|
@@ -176,7 +278,7 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
176
278
|
*/
|
|
177
279
|
getSetManagerCall(strategist: ContractAddr, root = this.getMerkleRoot()): Call {
|
|
178
280
|
return this.managerContract.populate('set_manage_root', [
|
|
179
|
-
strategist.address,
|
|
281
|
+
strategist.address,
|
|
180
282
|
num.getHexString(root)
|
|
181
283
|
]);
|
|
182
284
|
}
|
|
@@ -194,7 +296,19 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
194
296
|
*/
|
|
195
297
|
async getVaultPositions(): Promise<VaultPosition[]> {
|
|
196
298
|
const positions = await Promise.all(
|
|
197
|
-
|
|
299
|
+
[
|
|
300
|
+
...this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter.getPositions()),
|
|
301
|
+
(async () => {
|
|
302
|
+
const unused = await this.getUnusedBalance();
|
|
303
|
+
return {
|
|
304
|
+
amount: unused.amount,
|
|
305
|
+
usdValue: unused.usdValue,
|
|
306
|
+
remarks: "Unused Balance",
|
|
307
|
+
tokenInfo: this.asset(),
|
|
308
|
+
protocol: Protocols.NONE
|
|
309
|
+
};
|
|
310
|
+
})()
|
|
311
|
+
]
|
|
198
312
|
)
|
|
199
313
|
return positions.flat().map((position) => ({
|
|
200
314
|
amount: position.amount,
|
|
@@ -222,10 +336,151 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
222
336
|
const netAPY = weightedAPYs / totalHoldingsUSDValue;
|
|
223
337
|
return {
|
|
224
338
|
net: netAPY,
|
|
225
|
-
splits: allPositions.map(p => ({apy: p.apy.apy, id: p.remarks ?? ''}))
|
|
339
|
+
splits: allPositions.map(p => ({ apy: p.apy.apy, id: p.remarks ?? '' }))
|
|
226
340
|
};
|
|
227
341
|
}
|
|
228
342
|
|
|
343
|
+
async getTVL(): Promise<SingleTokenInfo> {
|
|
344
|
+
const tvl = await this.contract.call('total_assets', []) as bigint;
|
|
345
|
+
const amount = Web3Number.fromWei(tvl.toString(), this.asset().decimals);
|
|
346
|
+
const price = await this.pricer.getPrice(this.asset().symbol);
|
|
347
|
+
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
348
|
+
return {
|
|
349
|
+
tokenInfo: this.asset(),
|
|
350
|
+
amount,
|
|
351
|
+
usdValue
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async getUserRealizedAPY(
|
|
356
|
+
blockIdentifier: BlockIdentifier = "latest",
|
|
357
|
+
sinceBlocks = 600000
|
|
358
|
+
): Promise<number> {
|
|
359
|
+
logger.verbose(
|
|
360
|
+
`${this.getTag()}: getUserRealizedAPY => starting with blockIdentifier=${blockIdentifier}, sinceBlocks=${sinceBlocks}`
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const blockNow =
|
|
364
|
+
typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
365
|
+
? Number(blockIdentifier)
|
|
366
|
+
: (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
367
|
+
const blockNowTime =
|
|
368
|
+
typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
369
|
+
? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp
|
|
370
|
+
: new Date().getTime() / 1000;
|
|
371
|
+
|
|
372
|
+
const blockBefore = Math.max(
|
|
373
|
+
blockNow - sinceBlocks,
|
|
374
|
+
this.metadata.launchBlock
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const assetsNowRaw: bigint = await this.contract.call("total_assets", [], {
|
|
378
|
+
blockIdentifier,
|
|
379
|
+
}) as bigint;
|
|
380
|
+
const amountNow = Web3Number.fromWei(
|
|
381
|
+
assetsNowRaw.toString(),
|
|
382
|
+
this.metadata.depositTokens[0].decimals
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const supplyNowRaw: bigint = await this.contract.call("total_supply", [], {
|
|
386
|
+
blockIdentifier,
|
|
387
|
+
}) as bigint;
|
|
388
|
+
const supplyNow = Web3Number.fromWei(supplyNowRaw.toString(), 18);
|
|
389
|
+
|
|
390
|
+
const assetsBeforeRaw: bigint = await this.contract.call(
|
|
391
|
+
"total_assets",
|
|
392
|
+
[],
|
|
393
|
+
{ blockIdentifier: blockBefore }
|
|
394
|
+
) as bigint;
|
|
395
|
+
const amountBefore = Web3Number.fromWei(
|
|
396
|
+
assetsBeforeRaw.toString(),
|
|
397
|
+
this.metadata.depositTokens[0].decimals
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const supplyBeforeRaw: bigint = await this.contract.call(
|
|
401
|
+
"total_supply",
|
|
402
|
+
[],
|
|
403
|
+
{ blockIdentifier: blockBefore }
|
|
404
|
+
) as bigint;
|
|
405
|
+
const supplyBefore = Web3Number.fromWei(supplyBeforeRaw.toString(), 18);
|
|
406
|
+
|
|
407
|
+
const blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
408
|
+
blockBefore
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
const assetsPerShareNow = amountNow
|
|
412
|
+
.multipliedBy(1e18)
|
|
413
|
+
.dividedBy(supplyNow.toString());
|
|
414
|
+
|
|
415
|
+
const assetsPerShareBf = amountBefore
|
|
416
|
+
.multipliedBy(1e18)
|
|
417
|
+
.dividedBy(supplyBefore.toString());
|
|
418
|
+
|
|
419
|
+
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
420
|
+
|
|
421
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsNow: ${amountNow.toString()}`);
|
|
422
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsBefore: ${amountBefore.toString()}`);
|
|
423
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsPerShareNow: ${assetsPerShareNow.toString()}`);
|
|
424
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsPerShareBf: ${assetsPerShareBf.toString()}`);
|
|
425
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Supply before: ${supplyBefore.toString()}`);
|
|
426
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Supply now: ${supplyNow.toString()}`);
|
|
427
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Time diff in seconds: ${timeDiffSeconds}`);
|
|
428
|
+
|
|
429
|
+
const apyForGivenBlocks =
|
|
430
|
+
Number(
|
|
431
|
+
assetsPerShareNow
|
|
432
|
+
.minus(assetsPerShareBf)
|
|
433
|
+
.multipliedBy(10000)
|
|
434
|
+
.dividedBy(assetsPerShareBf)
|
|
435
|
+
) / 10000;
|
|
436
|
+
|
|
437
|
+
return (apyForGivenBlocks * (365 * 24 * 3600)) / timeDiffSeconds;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]> {
|
|
441
|
+
const { user, investmentFlows = [] } = input;
|
|
442
|
+
const [userTVL] = await Promise.all([
|
|
443
|
+
this.getUserTVL(user),
|
|
444
|
+
]);
|
|
445
|
+
const cards: UserPositionCard[] = [
|
|
446
|
+
{
|
|
447
|
+
title: "Your Holdings",
|
|
448
|
+
tooltip: "Your Holdings",
|
|
449
|
+
value: this.formatTokenAmountForCard(userTVL.amount, userTVL.tokenInfo),
|
|
450
|
+
subValue: `≈ ${this.formatUSDForCard(userTVL.usdValue)}`,
|
|
451
|
+
subValueColor: "positive",
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
let lifetimeAmount = userTVL.amount.multipliedBy(0);
|
|
456
|
+
let lifetimeTokenInfo = userTVL.tokenInfo;
|
|
457
|
+
let lifetimeUsdValue = 0;
|
|
458
|
+
if (investmentFlows.length > 0) {
|
|
459
|
+
try {
|
|
460
|
+
const earningsResult = this.getLifetimeEarnings(userTVL, investmentFlows);
|
|
461
|
+
lifetimeAmount = earningsResult.lifetimeEarnings;
|
|
462
|
+
lifetimeTokenInfo = earningsResult.tokenInfo.tokenInfo;
|
|
463
|
+
const userAmount = userTVL.amount.toNumber();
|
|
464
|
+
if (Number.isFinite(userAmount) && userAmount > 0) {
|
|
465
|
+
const pricePerToken = userTVL.usdValue / userAmount;
|
|
466
|
+
lifetimeUsdValue = lifetimeAmount.toNumber() * pricePerToken;
|
|
467
|
+
}
|
|
468
|
+
} catch (error) {
|
|
469
|
+
logger.warn(`${this.getTag()}::getUserPositionCards lifetime earnings fallback`, error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
cards.push({
|
|
474
|
+
title: "Lifetime Earnings",
|
|
475
|
+
tooltip: "Lifetime Earnings",
|
|
476
|
+
value: this.formatTokenAmountForCard(lifetimeAmount, lifetimeTokenInfo),
|
|
477
|
+
subValue: `≈ ${this.formatUSDForCard(lifetimeUsdValue)}`,
|
|
478
|
+
subValueColor: this.getSubValueColorFromSignedNumber(lifetimeUsdValue),
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
return cards;
|
|
482
|
+
}
|
|
483
|
+
|
|
229
484
|
async getPrevAUM() {
|
|
230
485
|
const currentAUM: bigint = await this.contract.call('aum', []) as bigint;
|
|
231
486
|
const prevAum = Web3Number.fromWei(currentAUM.toString(), this.asset().decimals);
|
|
@@ -243,5 +498,11 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
243
498
|
return maxWithdrawables;
|
|
244
499
|
}
|
|
245
500
|
|
|
246
|
-
}
|
|
247
501
|
|
|
502
|
+
async getMaxTVL(): Promise<Web3Number> {
|
|
503
|
+
const lstToken = this.asset();
|
|
504
|
+
const depositLimit: any = await this.contract.call("get_deposit_limit", []);
|
|
505
|
+
const limitBN = uint256.uint256ToBN(depositLimit);
|
|
506
|
+
return Web3Number.fromWei(limitBN.toString(), lstToken.decimals);
|
|
507
|
+
}
|
|
508
|
+
}
|