@opendatalabs/vana-sdk 2.2.2 → 2.2.3-canary.07f70f7

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.
Files changed (43) hide show
  1. package/dist/controllers/permissions.cjs +19 -14
  2. package/dist/controllers/permissions.cjs.map +1 -1
  3. package/dist/controllers/permissions.js +19 -14
  4. package/dist/controllers/permissions.js.map +1 -1
  5. package/dist/controllers/staking.cjs +666 -0
  6. package/dist/controllers/staking.cjs.map +1 -0
  7. package/dist/controllers/staking.d.ts +462 -0
  8. package/dist/controllers/staking.js +642 -0
  9. package/dist/controllers/staking.js.map +1 -0
  10. package/dist/core.cjs +4 -0
  11. package/dist/core.cjs.map +1 -1
  12. package/dist/core.d.ts +3 -0
  13. package/dist/core.js +4 -0
  14. package/dist/core.js.map +1 -1
  15. package/dist/generated/abi/VanaPoolEntityImplementation.cjs +37 -0
  16. package/dist/generated/abi/VanaPoolEntityImplementation.cjs.map +1 -1
  17. package/dist/generated/abi/VanaPoolEntityImplementation.d.ts +29 -0
  18. package/dist/generated/abi/VanaPoolEntityImplementation.js +37 -0
  19. package/dist/generated/abi/VanaPoolEntityImplementation.js.map +1 -1
  20. package/dist/generated/abi/VanaPoolStakingImplementation.cjs +187 -19
  21. package/dist/generated/abi/VanaPoolStakingImplementation.cjs.map +1 -1
  22. package/dist/generated/abi/VanaPoolStakingImplementation.d.ts +144 -14
  23. package/dist/generated/abi/VanaPoolStakingImplementation.js +187 -19
  24. package/dist/generated/abi/VanaPoolStakingImplementation.js.map +1 -1
  25. package/dist/generated/abi/index.d.ts +173 -14
  26. package/dist/generated/event-types.cjs.map +1 -1
  27. package/dist/generated/event-types.d.ts +7 -0
  28. package/dist/generated/eventRegistry.cjs +42 -0
  29. package/dist/generated/eventRegistry.cjs.map +1 -1
  30. package/dist/generated/eventRegistry.js +42 -0
  31. package/dist/generated/eventRegistry.js.map +1 -1
  32. package/dist/index.browser.d.ts +2 -0
  33. package/dist/index.browser.js +2 -0
  34. package/dist/index.browser.js.map +1 -1
  35. package/dist/index.node.cjs +3 -0
  36. package/dist/index.node.cjs.map +1 -1
  37. package/dist/index.node.d.ts +2 -0
  38. package/dist/index.node.js +2 -0
  39. package/dist/index.node.js.map +1 -1
  40. package/dist/tests/staking.test.d.ts +1 -0
  41. package/dist/types/permissions.cjs.map +1 -1
  42. package/dist/types/permissions.d.ts +2 -0
  43. package/package.json +1 -1
@@ -0,0 +1,666 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var staking_exports = {};
20
+ __export(staking_exports, {
21
+ StakingController: () => StakingController
22
+ });
23
+ module.exports = __toCommonJS(staking_exports);
24
+ var import_base = require("./base");
25
+ var import_viem = require("viem");
26
+ var import_addresses = require("../generated/addresses");
27
+ var import_abi = require("../generated/abi");
28
+ var import_errors = require("../errors");
29
+ class StakingController extends import_base.BaseController {
30
+ constructor(context) {
31
+ super(context);
32
+ }
33
+ /**
34
+ * Gets the chain ID from context.
35
+ */
36
+ getChainId() {
37
+ const chainId = this.context.walletClient?.chain?.id ?? this.context.publicClient.chain?.id;
38
+ if (!chainId) {
39
+ throw new Error("Chain ID not available");
40
+ }
41
+ return chainId;
42
+ }
43
+ /**
44
+ * Gets the VanaPoolEntity contract instance.
45
+ */
46
+ getEntityContract() {
47
+ const chainId = this.getChainId();
48
+ return (0, import_viem.getContract)({
49
+ address: (0, import_addresses.getContractAddress)(chainId, "VanaPoolEntity"),
50
+ abi: (0, import_abi.getAbi)("VanaPoolEntity"),
51
+ client: this.context.publicClient
52
+ });
53
+ }
54
+ /**
55
+ * Gets the VanaPoolStaking contract instance.
56
+ */
57
+ getStakingContract() {
58
+ const chainId = this.getChainId();
59
+ return (0, import_viem.getContract)({
60
+ address: (0, import_addresses.getContractAddress)(chainId, "VanaPoolStaking"),
61
+ abi: (0, import_abi.getAbi)("VanaPoolStaking"),
62
+ client: this.context.publicClient
63
+ });
64
+ }
65
+ /**
66
+ * Gets the total amount of VANA staked in the VanaPool protocol or a specific entity.
67
+ *
68
+ * @remarks
69
+ * When called without an entityId, this retrieves the sum of activeRewardPool
70
+ * across all active entities in the VanaPool protocol.
71
+ * When called with an entityId, this returns the activeRewardPool for that specific entity.
72
+ * The value is returned in wei (10^18 = 1 VANA).
73
+ *
74
+ * @param entityId - Optional entity ID to get staked amount for a specific entity
75
+ * @returns The total amount of VANA staked in wei
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * // Get total staked across all entities
80
+ * const totalStaked = await vana.staking.getTotalVanaStaked();
81
+ * console.log(`Total staked: ${Number(totalStaked) / 1e18} VANA`);
82
+ *
83
+ * // Get staked amount for a specific entity
84
+ * const entityStaked = await vana.staking.getTotalVanaStaked(1n);
85
+ * console.log(`Entity 1 staked: ${Number(entityStaked) / 1e18} VANA`);
86
+ * ```
87
+ */
88
+ async getTotalVanaStaked(entityId) {
89
+ const entityContract = this.getEntityContract();
90
+ if (entityId !== void 0) {
91
+ const entity = await entityContract.read.entities([entityId]);
92
+ return entity.activeRewardPool;
93
+ }
94
+ const activeEntityIds = await entityContract.read.activeEntitiesValues();
95
+ let totalStaked = 0n;
96
+ for (const id of activeEntityIds) {
97
+ const entity = await entityContract.read.entities([id]);
98
+ totalStaked += entity.activeRewardPool;
99
+ }
100
+ return totalStaked;
101
+ }
102
+ /**
103
+ * Gets information about a specific staking entity.
104
+ *
105
+ * @param entityId - The ID of the entity to query
106
+ * @returns The entity information including name, APY, shares, and reward pools
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const entity = await vana.staking.getEntity(1n);
111
+ * console.log(`Entity: ${entity.name}`);
112
+ * console.log(`Total shares: ${entity.totalShares}`);
113
+ * console.log(`Max APY: ${Number(entity.maxAPY) / 100}%`);
114
+ * ```
115
+ */
116
+ async getEntity(entityId) {
117
+ const entityContract = this.getEntityContract();
118
+ const result = await entityContract.read.entities([entityId]);
119
+ return {
120
+ entityId: result.entityId,
121
+ ownerAddress: result.ownerAddress,
122
+ status: result.status,
123
+ name: result.name,
124
+ maxAPY: result.maxAPY,
125
+ lockedRewardPool: result.lockedRewardPool,
126
+ activeRewardPool: result.activeRewardPool,
127
+ totalShares: result.totalShares,
128
+ lastUpdateTimestamp: result.lastUpdateTimestamp
129
+ };
130
+ }
131
+ /**
132
+ * Gets the total number of staking entities in the protocol.
133
+ *
134
+ * @returns The count of entities
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const count = await vana.staking.getEntitiesCount();
139
+ * console.log(`Total entities: ${count}`);
140
+ * ```
141
+ */
142
+ async getEntitiesCount() {
143
+ const entityContract = this.getEntityContract();
144
+ return entityContract.read.entitiesCount();
145
+ }
146
+ /**
147
+ * Gets the IDs of all active staking entities.
148
+ *
149
+ * @returns Array of active entity IDs
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * const activeIds = await vana.staking.getActiveEntities();
154
+ * console.log(`Active entities: ${activeIds.join(', ')}`);
155
+ * ```
156
+ */
157
+ async getActiveEntities() {
158
+ const entityContract = this.getEntityContract();
159
+ return entityContract.read.activeEntitiesValues();
160
+ }
161
+ /**
162
+ * Gets a staker's position in a specific entity.
163
+ *
164
+ * @param staker - The address of the staker
165
+ * @param entityId - The ID of the entity
166
+ * @returns The staker's position information
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const position = await vana.staking.getStakerPosition(
171
+ * '0x742d35...',
172
+ * 1n
173
+ * );
174
+ * console.log(`Shares: ${position.shares}`);
175
+ * console.log(`Rewards: ${position.realizedRewards}`);
176
+ * ```
177
+ */
178
+ async getStakerPosition(staker, entityId) {
179
+ const stakingContract = this.getStakingContract();
180
+ const result = await stakingContract.read.stakerEntities([
181
+ staker,
182
+ entityId
183
+ ]);
184
+ return {
185
+ shares: result.shares,
186
+ costBasis: result.costBasis,
187
+ rewardEligibilityTimestamp: result.rewardEligibilityTimestamp,
188
+ realizedRewards: result.realizedRewards,
189
+ vestedRewards: result.vestedRewards
190
+ };
191
+ }
192
+ /**
193
+ * Gets the earned rewards for a staker in an entity.
194
+ *
195
+ * @param staker - The address of the staker
196
+ * @param entityId - The ID of the entity
197
+ * @returns The earned rewards amount in wei
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const rewards = await vana.staking.getEarnedRewards(
202
+ * '0x742d35...',
203
+ * 1n
204
+ * );
205
+ * console.log(`Earned: ${Number(rewards) / 1e18} VANA`);
206
+ * ```
207
+ */
208
+ async getEarnedRewards(staker, entityId) {
209
+ const stakingContract = this.getStakingContract();
210
+ return stakingContract.read.getEarnedRewards([staker, entityId]);
211
+ }
212
+ /**
213
+ * Gets the minimum stake amount required to stake.
214
+ *
215
+ * @returns The minimum stake amount in wei
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * const minStake = await vana.staking.getMinStakeAmount();
220
+ * console.log(`Minimum stake: ${Number(minStake) / 1e18} VANA`);
221
+ * ```
222
+ */
223
+ async getMinStakeAmount() {
224
+ const stakingContract = this.getStakingContract();
225
+ return stakingContract.read.minStakeAmount();
226
+ }
227
+ /**
228
+ * Gets the count of active stakers in the protocol.
229
+ *
230
+ * @returns The number of active stakers
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * const count = await vana.staking.getActiveStakersCount();
235
+ * console.log(`Active stakers: ${count}`);
236
+ * ```
237
+ */
238
+ async getActiveStakersCount() {
239
+ const stakingContract = this.getStakingContract();
240
+ return stakingContract.read.activeStakersListCount();
241
+ }
242
+ /**
243
+ * Gets the bonding period for staking.
244
+ *
245
+ * @returns The bonding period in seconds
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * const bondingPeriod = await vana.staking.getBondingPeriod();
250
+ * console.log(`Bonding period: ${bondingPeriod / 86400n} days`);
251
+ * ```
252
+ */
253
+ async getBondingPeriod() {
254
+ const stakingContract = this.getStakingContract();
255
+ return stakingContract.read.bondingPeriod();
256
+ }
257
+ /**
258
+ * Gets the total distributed rewards for a specific entity from the subgraph.
259
+ *
260
+ * @remarks
261
+ * This queries the VanaPool subgraph to retrieve the cumulative `totalDistributedRewards`
262
+ * for a staking entity. This value represents the sum of all rewards that have been
263
+ * processed (moved from lockedRewardPool to activeRewardPool) minus any forfeited
264
+ * rewards that were returned.
265
+ *
266
+ * Requires a configured `subgraphUrl` in the Vana constructor options.
267
+ *
268
+ * @param entityId - The ID of the entity to query
269
+ * @param options - Optional configuration including custom subgraph URL
270
+ * @returns The total distributed rewards in wei
271
+ * @throws {BlockchainError} When subgraph URL is not configured or query fails
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * const totalRewards = await vana.staking.getTotalDistributedRewards(1n);
276
+ * const totalRewardsVana = Number(totalRewards) / 1e18;
277
+ * console.log(`Total distributed rewards: ${totalRewardsVana.toLocaleString()} VANA`);
278
+ * ```
279
+ */
280
+ async getTotalDistributedRewards(entityId, options = {}) {
281
+ const graphqlEndpoint = options.subgraphUrl ?? this.context.subgraphUrl;
282
+ if (!graphqlEndpoint) {
283
+ throw new import_errors.BlockchainError(
284
+ "subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
285
+ );
286
+ }
287
+ const query = `
288
+ query GetStakingEntityRewards($entityId: ID!) {
289
+ stakingEntity(id: $entityId) {
290
+ totalDistributedRewards
291
+ }
292
+ }
293
+ `;
294
+ const response = await fetch(graphqlEndpoint, {
295
+ method: "POST",
296
+ headers: {
297
+ "Content-Type": "application/json"
298
+ },
299
+ body: JSON.stringify({
300
+ query,
301
+ variables: {
302
+ entityId: entityId.toString()
303
+ }
304
+ })
305
+ });
306
+ if (!response.ok) {
307
+ throw new import_errors.BlockchainError(
308
+ `Subgraph query failed with status ${response.status}`
309
+ );
310
+ }
311
+ const result = await response.json();
312
+ if (result.errors && result.errors.length > 0) {
313
+ throw new import_errors.BlockchainError(
314
+ `Subgraph query error: ${result.errors[0].message}`
315
+ );
316
+ }
317
+ if (!result.data?.stakingEntity) {
318
+ throw new import_errors.BlockchainError(`Staking entity ${entityId} not found`);
319
+ }
320
+ return BigInt(result.data.stakingEntity.totalDistributedRewards);
321
+ }
322
+ /**
323
+ * Gets a comprehensive staking summary for a staker in an entity.
324
+ *
325
+ * @remarks
326
+ * This method aggregates all relevant staking information for a staker in a single call,
327
+ * including staked amount, current value, bonding status, and all reward types.
328
+ *
329
+ * Reward breakdown:
330
+ * - `earnedRewards` = pendingInterest + vestedRewards + realizedRewards
331
+ * - `pendingInterest` = currentValue - costBasis (unvested appreciation)
332
+ * - `vestedRewards` = rewards that have vested but not yet withdrawn
333
+ * - `realizedRewards` = rewards already withdrawn during unstakes
334
+ *
335
+ * @param staker - The address of the staker
336
+ * @param entityId - The ID of the entity
337
+ * @returns A comprehensive summary of the staker's position
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * const summary = await vana.staking.getStakerSummary('0x742d35...', 1n);
342
+ * console.log(`Total staked: ${Number(summary.totalStaked) / 1e18} VANA`);
343
+ * console.log(`Current value: ${Number(summary.currentValue) / 1e18} VANA`);
344
+ * console.log(`In bonding period: ${summary.isInBondingPeriod}`);
345
+ * console.log(`Remaining bonding time: ${Number(summary.remainingBondingTime) / 86400} days`);
346
+ * console.log(`Vested rewards: ${Number(summary.vestedRewards) / 1e18} VANA`);
347
+ * console.log(`Unvested rewards: ${Number(summary.unvestedRewards) / 1e18} VANA`);
348
+ * console.log(`Realized rewards: ${Number(summary.realizedRewards) / 1e18} VANA`);
349
+ * console.log(`Total earned: ${Number(summary.earnedRewards) / 1e18} VANA`);
350
+ * ```
351
+ */
352
+ async getStakerSummary(staker, entityId) {
353
+ const chainId = this.getChainId();
354
+ const stakingAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolStaking");
355
+ const entityAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolEntity");
356
+ const stakingAbi = (0, import_abi.getAbi)("VanaPoolStaking");
357
+ const entityAbi = (0, import_abi.getAbi)("VanaPoolEntity");
358
+ const block = await this.context.publicClient.getBlock();
359
+ const blockNumber = block.number;
360
+ const multicallResults = await this.context.publicClient.multicall({
361
+ contracts: [
362
+ {
363
+ address: stakingAddress,
364
+ abi: stakingAbi,
365
+ functionName: "stakerEntities",
366
+ args: [staker, entityId]
367
+ },
368
+ {
369
+ address: stakingAddress,
370
+ abi: stakingAbi,
371
+ functionName: "getEarnedRewards",
372
+ args: [staker, entityId]
373
+ },
374
+ {
375
+ address: entityAddress,
376
+ abi: entityAbi,
377
+ functionName: "entityShareToVana",
378
+ args: [entityId]
379
+ }
380
+ ],
381
+ blockNumber
382
+ });
383
+ const [positionResult, earnedRewardsResult, shareToVanaResult] = multicallResults;
384
+ if (positionResult.status === "failure" || earnedRewardsResult.status === "failure" || shareToVanaResult.status === "failure") {
385
+ throw new import_errors.BlockchainError(
386
+ "Failed to fetch staker summary: one or more contract calls failed"
387
+ );
388
+ }
389
+ const position = positionResult.result;
390
+ const earnedRewards = earnedRewardsResult.result;
391
+ const shareToVana = shareToVanaResult.result;
392
+ const currentTimestamp = block.timestamp;
393
+ const eligibilityTimestamp = position.rewardEligibilityTimestamp;
394
+ const remainingBondingTime = eligibilityTimestamp > currentTimestamp ? eligibilityTimestamp - currentTimestamp : 0n;
395
+ const isInBondingPeriod = remainingBondingTime > 0n;
396
+ const currentValue = position.shares * shareToVana / 10n ** 18n;
397
+ const unvestedRewards = currentValue > position.costBasis ? currentValue - position.costBasis : 0n;
398
+ return {
399
+ shares: position.shares,
400
+ costBasis: position.costBasis,
401
+ currentValue,
402
+ rewardEligibilityTimestamp: eligibilityTimestamp,
403
+ remainingBondingTime,
404
+ isInBondingPeriod,
405
+ vestedRewards: position.vestedRewards,
406
+ unvestedRewards,
407
+ realizedRewards: position.realizedRewards,
408
+ earnedRewards
409
+ };
410
+ }
411
+ /**
412
+ * Stakes VANA to an entity in the VanaPool protocol.
413
+ *
414
+ * @remarks
415
+ * This method stakes native VANA tokens to a specified entity. The staker will receive
416
+ * shares in proportion to their stake amount. A bonding period applies during which
417
+ * rewards cannot be fully claimed without penalty.
418
+ *
419
+ * Requires a wallet client to be configured in the Vana constructor.
420
+ *
421
+ * @param params - The staking parameters
422
+ * @param params.entityId - The ID of the entity to stake to
423
+ * @param params.amount - The amount of VANA to stake (in wei, or as a string like "1.5" for 1.5 VANA)
424
+ * @param params.recipient - Optional recipient address for the shares (defaults to the sender)
425
+ * @param params.minShares - Optional minimum shares to receive (slippage protection, defaults to 0)
426
+ * @returns The transaction hash
427
+ * @throws {BlockchainError} When wallet client is not configured
428
+ *
429
+ * @example
430
+ * ```typescript
431
+ * // Stake 100 VANA to entity 1
432
+ * const txHash = await vana.staking.stake({
433
+ * entityId: 1n,
434
+ * amount: "100", // 100 VANA
435
+ * });
436
+ * console.log(`Staked! Transaction: ${txHash}`);
437
+ *
438
+ * // Stake with slippage protection
439
+ * const txHash2 = await vana.staking.stake({
440
+ * entityId: 1n,
441
+ * amount: parseEther("50"), // 50 VANA in wei
442
+ * minShares: parseEther("49"), // Expect at least 49 shares
443
+ * });
444
+ * ```
445
+ */
446
+ async stake(params, options) {
447
+ this.assertWallet();
448
+ const chainId = this.getChainId();
449
+ const stakingAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolStaking");
450
+ const stakingAbi = (0, import_abi.getAbi)("VanaPoolStaking");
451
+ const amountWei = typeof params.amount === "string" ? (0, import_viem.parseEther)(params.amount) : params.amount;
452
+ const account = this.context.walletClient.account ?? this.context.userAddress;
453
+ const accountAddress = typeof account === "string" ? account : account.address;
454
+ const recipient = params.recipient ?? accountAddress;
455
+ const minShares = params.minShares ?? 0n;
456
+ const txHash = await this.context.walletClient.writeContract({
457
+ address: stakingAddress,
458
+ abi: stakingAbi,
459
+ functionName: "stake",
460
+ args: [params.entityId, recipient, minShares],
461
+ value: amountWei,
462
+ account,
463
+ chain: this.context.walletClient.chain,
464
+ ...this.spreadTransactionOptions(options)
465
+ });
466
+ return txHash;
467
+ }
468
+ /**
469
+ * Gets the maximum amount of VANA that can be unstaked in a single transaction.
470
+ *
471
+ * @remarks
472
+ * This calls the contract's getMaxUnstakeAmount which returns the minimum of:
473
+ * 1. The withdrawable VANA (costBasis if in bonding period, shareValue if eligible)
474
+ * 2. The entity's activeRewardPool (what the entity has available)
475
+ * 3. The treasury balance (what can be paid out)
476
+ *
477
+ * The limiting factor indicates what's constraining the unstake:
478
+ * - 0 = user shares/costBasis
479
+ * - 1 = activeRewardPool
480
+ * - 2 = treasury
481
+ *
482
+ * @param staker - The address of the staker
483
+ * @param entityId - The ID of the entity
484
+ * @returns Object containing maxVana, maxShares, limitingFactor, and isInBondingPeriod
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * const result = await vana.staking.getMaxUnstakeAmount('0x742d35...', 1n);
489
+ * console.log(`Max unstake: ${Number(result.maxVana) / 1e18} VANA`);
490
+ * console.log(`Max shares: ${result.maxShares}`);
491
+ * console.log(`In bonding period: ${result.isInBondingPeriod}`);
492
+ * ```
493
+ */
494
+ async getMaxUnstakeAmount(staker, entityId) {
495
+ const stakingContract = this.getStakingContract();
496
+ const result = await stakingContract.read.getMaxUnstakeAmount([
497
+ staker,
498
+ entityId
499
+ ]);
500
+ return {
501
+ maxVana: result[0],
502
+ maxShares: result[1],
503
+ limitingFactor: Number(result[2]),
504
+ isInBondingPeriod: result[3]
505
+ };
506
+ }
507
+ /**
508
+ * Computes the new bonding period end timestamp after adding stake.
509
+ *
510
+ * @remarks
511
+ * When a staker adds more stake to an existing position, the reward eligibility timestamp
512
+ * is recalculated as a weighted average of the existing and new positions. This function
513
+ * allows you to preview what the new eligibility timestamp would be without executing
514
+ * the stake transaction.
515
+ *
516
+ * The formula used (matching the contract):
517
+ * ```
518
+ * newEligibility = (existingShares * existingEligibility + newShares * newEligibility) / totalShares
519
+ * ```
520
+ *
521
+ * Where `newEligibility` for the incoming stake is `currentTimestamp + bondingPeriod`.
522
+ *
523
+ * @param params - The parameters for computing the new bonding period
524
+ * @param params.staker - The address of the staker
525
+ * @param params.entityId - The ID of the entity
526
+ * @param params.stakeAmount - The amount of VANA to stake (in wei)
527
+ * @returns Object containing the new eligibility timestamp and related info
528
+ *
529
+ * @example
530
+ * ```typescript
531
+ * // Preview bonding period after staking 100 VANA
532
+ * const preview = await vana.staking.computeNewBondingPeriod({
533
+ * staker: '0x742d35...',
534
+ * entityId: 1n,
535
+ * stakeAmount: parseEther("100"),
536
+ * });
537
+ * console.log(`New eligibility: ${new Date(Number(preview.newEligibilityTimestamp) * 1000)}`);
538
+ * console.log(`Remaining bonding time: ${Number(preview.newRemainingBondingTime) / 86400} days`);
539
+ * ```
540
+ */
541
+ async computeNewBondingPeriod(params) {
542
+ const chainId = this.getChainId();
543
+ const stakingAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolStaking");
544
+ const entityAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolEntity");
545
+ const stakingAbi = (0, import_abi.getAbi)("VanaPoolStaking");
546
+ const entityAbi = (0, import_abi.getAbi)("VanaPoolEntity");
547
+ const block = await this.context.publicClient.getBlock();
548
+ const blockNumber = block.number;
549
+ const currentTimestamp = block.timestamp;
550
+ const multicallResults = await this.context.publicClient.multicall({
551
+ contracts: [
552
+ {
553
+ address: stakingAddress,
554
+ abi: stakingAbi,
555
+ functionName: "stakerEntities",
556
+ args: [params.staker, params.entityId]
557
+ },
558
+ {
559
+ address: stakingAddress,
560
+ abi: stakingAbi,
561
+ functionName: "bondingPeriod",
562
+ args: []
563
+ },
564
+ {
565
+ address: entityAddress,
566
+ abi: entityAbi,
567
+ functionName: "vanaToEntityShare",
568
+ args: [params.entityId]
569
+ }
570
+ ],
571
+ blockNumber
572
+ });
573
+ const [positionResult, bondingPeriodResult, vanaToShareResult] = multicallResults;
574
+ if (positionResult.status === "failure" || bondingPeriodResult.status === "failure" || vanaToShareResult.status === "failure") {
575
+ throw new import_errors.BlockchainError(
576
+ "Failed to compute new bonding period: one or more contract calls failed"
577
+ );
578
+ }
579
+ const position = positionResult.result;
580
+ const bondingPeriodDuration = bondingPeriodResult.result;
581
+ const vanaToShare = vanaToShareResult.result;
582
+ const currentShares = position.shares;
583
+ const currentEligibilityTimestamp = position.rewardEligibilityTimestamp;
584
+ const currentRemainingBondingTime = currentEligibilityTimestamp > currentTimestamp ? currentEligibilityTimestamp - currentTimestamp : 0n;
585
+ const estimatedNewShares = params.stakeAmount * vanaToShare / 10n ** 18n;
586
+ const totalSharesAfter = currentShares + estimatedNewShares;
587
+ const newStakeEligibility = currentTimestamp + bondingPeriodDuration;
588
+ let newEligibilityTimestamp;
589
+ if (currentShares === 0n) {
590
+ newEligibilityTimestamp = newStakeEligibility;
591
+ } else {
592
+ newEligibilityTimestamp = (currentShares * currentEligibilityTimestamp + estimatedNewShares * newStakeEligibility) / totalSharesAfter;
593
+ }
594
+ const newRemainingBondingTime = newEligibilityTimestamp > currentTimestamp ? newEligibilityTimestamp - currentTimestamp : 0n;
595
+ return {
596
+ newEligibilityTimestamp,
597
+ newRemainingBondingTime,
598
+ currentEligibilityTimestamp,
599
+ currentRemainingBondingTime,
600
+ currentShares,
601
+ estimatedNewShares,
602
+ totalSharesAfter,
603
+ bondingPeriodDuration,
604
+ currentTimestamp
605
+ };
606
+ }
607
+ /**
608
+ * Unstakes VANA from an entity in the VanaPool protocol.
609
+ *
610
+ * @remarks
611
+ * This method unstakes native VANA tokens from a specified entity. The amount
612
+ * that can be unstaked depends on whether the staker is in the bonding period:
613
+ *
614
+ * - **During bonding period**: Only cost basis can be withdrawn; rewards are forfeited
615
+ * - **After bonding period**: Full current value (cost basis + rewards) can be withdrawn
616
+ *
617
+ * Use `getMaxUnstakeAmount` to determine the maximum amount that can be unstaked.
618
+ *
619
+ * Requires a wallet client to be configured in the Vana constructor.
620
+ *
621
+ * @param params - The unstaking parameters
622
+ * @param params.entityId - The ID of the entity to unstake from
623
+ * @param params.amount - The amount of VANA to unstake (in wei, or as a string like "1.5" for 1.5 VANA)
624
+ * @param params.maxShares - Maximum shares to burn for slippage protection (defaults to 0, no protection)
625
+ * @returns The transaction hash
626
+ * @throws {BlockchainError} When wallet client is not configured
627
+ *
628
+ * @example
629
+ * ```typescript
630
+ * // Get max unstake amount first
631
+ * const maxUnstake = await vana.staking.getMaxUnstakeAmount(address, 1n);
632
+ *
633
+ * // Unstake the maximum amount with slippage protection
634
+ * const txHash = await vana.staking.unstake({
635
+ * entityId: 1n,
636
+ * amount: maxUnstake.maxVana,
637
+ * maxShares: maxUnstake.maxShares,
638
+ * });
639
+ * console.log(`Unstaked! Transaction: ${txHash}`);
640
+ * ```
641
+ */
642
+ async unstake(params, options) {
643
+ this.assertWallet();
644
+ const chainId = this.getChainId();
645
+ const stakingAddress = (0, import_addresses.getContractAddress)(chainId, "VanaPoolStaking");
646
+ const stakingAbi = (0, import_abi.getAbi)("VanaPoolStaking");
647
+ const amountWei = typeof params.amount === "string" ? (0, import_viem.parseEther)(params.amount) : params.amount;
648
+ const maxShares = params.maxShares ?? 0n;
649
+ const account = this.context.walletClient.account ?? this.context.userAddress;
650
+ const txHash = await this.context.walletClient.writeContract({
651
+ address: stakingAddress,
652
+ abi: stakingAbi,
653
+ functionName: "unstakeVana",
654
+ args: [params.entityId, amountWei, maxShares],
655
+ account,
656
+ chain: this.context.walletClient.chain,
657
+ ...this.spreadTransactionOptions(options)
658
+ });
659
+ return txHash;
660
+ }
661
+ }
662
+ // Annotate the CommonJS export names for ESM import in node:
663
+ 0 && (module.exports = {
664
+ StakingController
665
+ });
666
+ //# sourceMappingURL=staking.cjs.map