@strkfarm/sdk 2.0.0-dev.4 → 2.0.0-dev.41
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 +116250 -90801
- package/dist/index.browser.mjs +13050 -10957
- package/dist/index.d.ts +2232 -1933
- package/dist/index.js +13380 -11084
- package/dist/index.mjs +13280 -11007
- package/package.json +6 -7
- 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 +93 -36
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +218 -5
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +21 -12
- package/src/modules/ekubo-pricer.ts +79 -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 +94 -40
- package/src/modules/pricerBase.ts +2 -1
- 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 +1047 -351
- 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 +353 -9
- package/src/strategies/svk-strategy.ts +283 -31
- package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1262 -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 +1098 -712
- 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 +631 -414
- package/src/strategies/universal-strategy.tsx +1331 -1173
- package/src/strategies/vesu-rebalance.tsx +252 -152
- package/src/strategies/yoloVault.ts +1087 -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 -661
- 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,79 @@ 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): Promise<Call[]> {
|
|
84
|
+
assert(
|
|
85
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
86
|
+
"Withdraw token mismatch"
|
|
87
|
+
);
|
|
88
|
+
const shares = await this.contract.call('convert_to_shares', [uint256.bnToUint256(amountInfo.amount.toWei())]);
|
|
89
|
+
const call = this.contract.populate("request_redeem", [
|
|
90
|
+
uint256.bnToUint256(shares.toString()),
|
|
91
|
+
receiver.address,
|
|
92
|
+
owner.address
|
|
93
|
+
]);
|
|
94
|
+
return [call];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getUserTVL(user: ContractAddr, blockIdentifier: BlockIdentifier = "latest"): Promise<SingleTokenInfo> {
|
|
98
|
+
const shares: any = await this.contract.call("balanceOf", [user.address], { blockIdentifier });
|
|
99
|
+
const assets: any = await this.contract.call(
|
|
100
|
+
"convert_to_assets",
|
|
101
|
+
[uint256.bnToUint256(shares)],
|
|
102
|
+
{ blockIdentifier }
|
|
103
|
+
);
|
|
104
|
+
const amount = Web3Number.fromWei(
|
|
105
|
+
assets.toString(),
|
|
106
|
+
this.metadata.depositTokens[0].decimals
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const blockNumber = typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
110
|
+
? Number(blockIdentifier)
|
|
111
|
+
: undefined;
|
|
112
|
+
|
|
113
|
+
const price = await this.pricer.getPrice(
|
|
114
|
+
this.metadata.depositTokens[0].symbol,
|
|
115
|
+
blockNumber
|
|
116
|
+
);
|
|
117
|
+
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
118
|
+
return {
|
|
119
|
+
tokenInfo: this.asset(),
|
|
120
|
+
amount,
|
|
121
|
+
usdValue
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
60
125
|
/**
|
|
61
126
|
* Returns the unused balance in the vault allocator.
|
|
62
127
|
* Note: This function is common for any SVK strategy.
|
|
63
128
|
*/
|
|
64
129
|
async getUnusedBalance(): Promise<SingleTokenInfo> {
|
|
65
130
|
const balance = await (new ERC20(this.config)).balanceOf(
|
|
66
|
-
this.asset().address,
|
|
67
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
131
|
+
this.asset().address,
|
|
132
|
+
this.metadata.additionalInfo.vaultAllocator,
|
|
68
133
|
this.asset().decimals
|
|
69
134
|
);
|
|
70
135
|
const price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
@@ -76,6 +141,24 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
76
141
|
};
|
|
77
142
|
}
|
|
78
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Builds a manage call from an adapter's proof tree.
|
|
146
|
+
* DRY helper for the repeated getProofs → getManageCall pattern.
|
|
147
|
+
*/
|
|
148
|
+
protected async buildManageCallFromAdapter(
|
|
149
|
+
adapter: { getProofs: (isDeposit: boolean, tree: any) => any },
|
|
150
|
+
isDeposit: boolean,
|
|
151
|
+
amount: Web3Number,
|
|
152
|
+
additionalParams?: Record<string, any>,
|
|
153
|
+
): Promise<Call> {
|
|
154
|
+
const proofsInfo = adapter.getProofs(isDeposit, this.getMerkleTree());
|
|
155
|
+
const manageCalls = await proofsInfo.callConstructor({ amount, ...additionalParams });
|
|
156
|
+
return this.getManageCall(
|
|
157
|
+
this.getProofGroupsForManageCalls(manageCalls),
|
|
158
|
+
manageCalls,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
79
162
|
/**
|
|
80
163
|
* Bridges liquidity from the vault allocator back to the vault.
|
|
81
164
|
* Note: This function is common for any SVK strategy.
|
|
@@ -85,37 +168,54 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
85
168
|
const approveAdapter = this.metadata.additionalInfo.leafAdapters.find(
|
|
86
169
|
adapter => adapter().leaves.find(leaf => leaf.readableId === UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY)
|
|
87
170
|
);
|
|
88
|
-
|
|
171
|
+
|
|
89
172
|
if (!approveAdapter) {
|
|
90
173
|
throw new Error(`${this.getTag()}::getBringLiquidityCall: approve adapter not found`);
|
|
91
174
|
}
|
|
92
|
-
|
|
175
|
+
|
|
93
176
|
// Find bring liquidity leaf adapter
|
|
94
177
|
const bringLiquidityAdapter = this.metadata.additionalInfo.leafAdapters.find(
|
|
95
178
|
adapter => adapter().leaves.find(leaf => leaf.readableId === UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
|
|
96
179
|
);
|
|
97
|
-
|
|
180
|
+
|
|
98
181
|
if (!bringLiquidityAdapter) {
|
|
99
182
|
throw new Error(`${this.getTag()}::getBringLiquidityCall: bring liquidity adapter not found`);
|
|
100
183
|
}
|
|
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
|
-
|
|
184
|
+
|
|
112
185
|
// Construct calls
|
|
113
186
|
logger.verbose("approve adapter amount", params.amount);
|
|
114
187
|
const approveCalls = await approveAdapter().callConstructor({ amount: params.amount });
|
|
115
188
|
const bringLiquidityCalls = await bringLiquidityAdapter().callConstructor({ amount: params.amount });
|
|
116
|
-
|
|
189
|
+
const manageCalls = [...approveCalls, ...bringLiquidityCalls];
|
|
190
|
+
|
|
117
191
|
// Combine into final call
|
|
118
|
-
return this.getManageCall(
|
|
192
|
+
return this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Resolves ordered merkle proofs from emitted manage calls.
|
|
197
|
+
* This ensures only runtime-required proofs are passed to manager calls.
|
|
198
|
+
*/
|
|
199
|
+
protected getProofGroupsForManageCalls(manageCalls: ManageCall[]): string[][] {
|
|
200
|
+
const tree = this.getMerkleTree();
|
|
201
|
+
const proofByReadableId = new Map<string, string[]>();
|
|
202
|
+
for (const [i, v] of tree.entries()) {
|
|
203
|
+
if (proofByReadableId.has(v.readableId)) {
|
|
204
|
+
throw new Error(`Duplicate readableId found in merkle tree: ${v.readableId}`);
|
|
205
|
+
}
|
|
206
|
+
proofByReadableId.set(v.readableId, tree.getProof(i));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return manageCalls.map((manageCall, index) => {
|
|
210
|
+
if (!manageCall.proofReadableId) {
|
|
211
|
+
throw new Error(`Missing proofReadableId for manage call at index ${index}`);
|
|
212
|
+
}
|
|
213
|
+
const proof = proofByReadableId.get(manageCall.proofReadableId);
|
|
214
|
+
if (!proof) {
|
|
215
|
+
throw new Error(`Proof not found for readableId: ${manageCall.proofReadableId}`);
|
|
216
|
+
}
|
|
217
|
+
return proof;
|
|
218
|
+
});
|
|
119
219
|
}
|
|
120
220
|
|
|
121
221
|
/**
|
|
@@ -157,10 +257,10 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
157
257
|
*/
|
|
158
258
|
getManageCall(proofGroups: string[][], manageCalls: ManageCall[]): Call {
|
|
159
259
|
assert(
|
|
160
|
-
proofGroups.length == manageCalls.length,
|
|
260
|
+
proofGroups.length == manageCalls.length,
|
|
161
261
|
`Proof IDs and Manage Calls length mismatch: ${proofGroups.length} != ${manageCalls.length}`
|
|
162
262
|
);
|
|
163
|
-
|
|
263
|
+
|
|
164
264
|
return this.managerContract.populate('manage_vault_with_merkle_verification', {
|
|
165
265
|
proofs: proofGroups.map(group => group),
|
|
166
266
|
decoder_and_sanitizers: manageCalls.map(call => call.sanitizer.address),
|
|
@@ -176,7 +276,7 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
176
276
|
*/
|
|
177
277
|
getSetManagerCall(strategist: ContractAddr, root = this.getMerkleRoot()): Call {
|
|
178
278
|
return this.managerContract.populate('set_manage_root', [
|
|
179
|
-
strategist.address,
|
|
279
|
+
strategist.address,
|
|
180
280
|
num.getHexString(root)
|
|
181
281
|
]);
|
|
182
282
|
}
|
|
@@ -194,7 +294,19 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
194
294
|
*/
|
|
195
295
|
async getVaultPositions(): Promise<VaultPosition[]> {
|
|
196
296
|
const positions = await Promise.all(
|
|
197
|
-
|
|
297
|
+
[
|
|
298
|
+
...this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter.getPositions()),
|
|
299
|
+
(async () => {
|
|
300
|
+
const unused = await this.getUnusedBalance();
|
|
301
|
+
return {
|
|
302
|
+
amount: unused.amount,
|
|
303
|
+
usdValue: unused.usdValue,
|
|
304
|
+
remarks: "Unused Balance",
|
|
305
|
+
tokenInfo: this.asset(),
|
|
306
|
+
protocol: Protocols.NONE
|
|
307
|
+
};
|
|
308
|
+
})()
|
|
309
|
+
]
|
|
198
310
|
)
|
|
199
311
|
return positions.flat().map((position) => ({
|
|
200
312
|
amount: position.amount,
|
|
@@ -222,10 +334,151 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
222
334
|
const netAPY = weightedAPYs / totalHoldingsUSDValue;
|
|
223
335
|
return {
|
|
224
336
|
net: netAPY,
|
|
225
|
-
splits: allPositions.map(p => ({apy: p.apy.apy, id: p.remarks ?? ''}))
|
|
337
|
+
splits: allPositions.map(p => ({ apy: p.apy.apy, id: p.remarks ?? '' }))
|
|
226
338
|
};
|
|
227
339
|
}
|
|
228
340
|
|
|
341
|
+
async getTVL(): Promise<SingleTokenInfo> {
|
|
342
|
+
const tvl = await this.contract.call('total_assets', []) as bigint;
|
|
343
|
+
const amount = Web3Number.fromWei(tvl.toString(), this.asset().decimals);
|
|
344
|
+
const price = await this.pricer.getPrice(this.asset().symbol);
|
|
345
|
+
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
346
|
+
return {
|
|
347
|
+
tokenInfo: this.asset(),
|
|
348
|
+
amount,
|
|
349
|
+
usdValue
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async getUserRealizedAPY(
|
|
354
|
+
blockIdentifier: BlockIdentifier = "latest",
|
|
355
|
+
sinceBlocks = 600000
|
|
356
|
+
): Promise<number> {
|
|
357
|
+
logger.verbose(
|
|
358
|
+
`${this.getTag()}: getUserRealizedAPY => starting with blockIdentifier=${blockIdentifier}, sinceBlocks=${sinceBlocks}`
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const blockNow =
|
|
362
|
+
typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
363
|
+
? Number(blockIdentifier)
|
|
364
|
+
: (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
365
|
+
const blockNowTime =
|
|
366
|
+
typeof blockIdentifier === "number" || typeof blockIdentifier === "bigint"
|
|
367
|
+
? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp
|
|
368
|
+
: new Date().getTime() / 1000;
|
|
369
|
+
|
|
370
|
+
const blockBefore = Math.max(
|
|
371
|
+
blockNow - sinceBlocks,
|
|
372
|
+
this.metadata.launchBlock
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
const assetsNowRaw: bigint = await this.contract.call("total_assets", [], {
|
|
376
|
+
blockIdentifier,
|
|
377
|
+
}) as bigint;
|
|
378
|
+
const amountNow = Web3Number.fromWei(
|
|
379
|
+
assetsNowRaw.toString(),
|
|
380
|
+
this.metadata.depositTokens[0].decimals
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const supplyNowRaw: bigint = await this.contract.call("total_supply", [], {
|
|
384
|
+
blockIdentifier,
|
|
385
|
+
}) as bigint;
|
|
386
|
+
const supplyNow = Web3Number.fromWei(supplyNowRaw.toString(), 18);
|
|
387
|
+
|
|
388
|
+
const assetsBeforeRaw: bigint = await this.contract.call(
|
|
389
|
+
"total_assets",
|
|
390
|
+
[],
|
|
391
|
+
{ blockIdentifier: blockBefore }
|
|
392
|
+
) as bigint;
|
|
393
|
+
const amountBefore = Web3Number.fromWei(
|
|
394
|
+
assetsBeforeRaw.toString(),
|
|
395
|
+
this.metadata.depositTokens[0].decimals
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
const supplyBeforeRaw: bigint = await this.contract.call(
|
|
399
|
+
"total_supply",
|
|
400
|
+
[],
|
|
401
|
+
{ blockIdentifier: blockBefore }
|
|
402
|
+
) as bigint;
|
|
403
|
+
const supplyBefore = Web3Number.fromWei(supplyBeforeRaw.toString(), 18);
|
|
404
|
+
|
|
405
|
+
const blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
406
|
+
blockBefore
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const assetsPerShareNow = amountNow
|
|
410
|
+
.multipliedBy(1e18)
|
|
411
|
+
.dividedBy(supplyNow.toString());
|
|
412
|
+
|
|
413
|
+
const assetsPerShareBf = amountBefore
|
|
414
|
+
.multipliedBy(1e18)
|
|
415
|
+
.dividedBy(supplyBefore.toString());
|
|
416
|
+
|
|
417
|
+
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
418
|
+
|
|
419
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsNow: ${amountNow.toString()}`);
|
|
420
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsBefore: ${amountBefore.toString()}`);
|
|
421
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsPerShareNow: ${assetsPerShareNow.toString()}`);
|
|
422
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] assetsPerShareBf: ${assetsPerShareBf.toString()}`);
|
|
423
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Supply before: ${supplyBefore.toString()}`);
|
|
424
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Supply now: ${supplyNow.toString()}`);
|
|
425
|
+
logger.verbose(`${this.getTag()} [getUserRealizedAPY] Time diff in seconds: ${timeDiffSeconds}`);
|
|
426
|
+
|
|
427
|
+
const apyForGivenBlocks =
|
|
428
|
+
Number(
|
|
429
|
+
assetsPerShareNow
|
|
430
|
+
.minus(assetsPerShareBf)
|
|
431
|
+
.multipliedBy(10000)
|
|
432
|
+
.dividedBy(assetsPerShareBf)
|
|
433
|
+
) / 10000;
|
|
434
|
+
|
|
435
|
+
return (apyForGivenBlocks * (365 * 24 * 3600)) / timeDiffSeconds;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]> {
|
|
439
|
+
const { user, investmentFlows = [] } = input;
|
|
440
|
+
const [userTVL] = await Promise.all([
|
|
441
|
+
this.getUserTVL(user),
|
|
442
|
+
]);
|
|
443
|
+
const cards: UserPositionCard[] = [
|
|
444
|
+
{
|
|
445
|
+
title: "Your Holdings",
|
|
446
|
+
tooltip: "Your Holdings",
|
|
447
|
+
value: this.formatTokenAmountForCard(userTVL.amount, userTVL.tokenInfo),
|
|
448
|
+
subValue: `≈ ${this.formatUSDForCard(userTVL.usdValue)}`,
|
|
449
|
+
subValueColor: "positive",
|
|
450
|
+
},
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
let lifetimeAmount = userTVL.amount.multipliedBy(0);
|
|
454
|
+
let lifetimeTokenInfo = userTVL.tokenInfo;
|
|
455
|
+
let lifetimeUsdValue = 0;
|
|
456
|
+
if (investmentFlows.length > 0) {
|
|
457
|
+
try {
|
|
458
|
+
const earningsResult = this.getLifetimeEarnings(userTVL, investmentFlows);
|
|
459
|
+
lifetimeAmount = earningsResult.lifetimeEarnings;
|
|
460
|
+
lifetimeTokenInfo = earningsResult.tokenInfo.tokenInfo;
|
|
461
|
+
const userAmount = userTVL.amount.toNumber();
|
|
462
|
+
if (Number.isFinite(userAmount) && userAmount > 0) {
|
|
463
|
+
const pricePerToken = userTVL.usdValue / userAmount;
|
|
464
|
+
lifetimeUsdValue = lifetimeAmount.toNumber() * pricePerToken;
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logger.warn(`${this.getTag()}::getUserPositionCards lifetime earnings fallback`, error);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
cards.push({
|
|
472
|
+
title: "Lifetime Earnings",
|
|
473
|
+
tooltip: "Lifetime Earnings",
|
|
474
|
+
value: this.formatTokenAmountForCard(lifetimeAmount, lifetimeTokenInfo),
|
|
475
|
+
subValue: `≈ ${this.formatUSDForCard(lifetimeUsdValue)}`,
|
|
476
|
+
subValueColor: this.getSubValueColorFromSignedNumber(lifetimeUsdValue),
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
return cards;
|
|
480
|
+
}
|
|
481
|
+
|
|
229
482
|
async getPrevAUM() {
|
|
230
483
|
const currentAUM: bigint = await this.contract.call('aum', []) as bigint;
|
|
231
484
|
const prevAum = Web3Number.fromWei(currentAUM.toString(), this.asset().decimals);
|
|
@@ -244,4 +497,3 @@ export abstract class SVKStrategy<S extends UniversalStrategySettings>
|
|
|
244
497
|
}
|
|
245
498
|
|
|
246
499
|
}
|
|
247
|
-
|