@wireio/stake 0.5.1 → 0.5.2
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/lib/stake.browser.js +54 -11
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +958 -0
- package/lib/stake.js +58 -11
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +54 -11
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/networks/solana/solana.ts +107 -14
- package/src/staker.ts +1 -1
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ComputeBudgetProgram,
|
|
4
4
|
Connection,
|
|
5
5
|
ConnectionConfig,
|
|
6
|
+
PerfSample,
|
|
6
7
|
PublicKey as SolPubKey,
|
|
7
8
|
SystemProgram,
|
|
8
9
|
Transaction,
|
|
@@ -40,8 +41,10 @@ import { TokenClient } from './clients/token.client';
|
|
|
40
41
|
import {
|
|
41
42
|
deriveLiqsolMintPda,
|
|
42
43
|
deriveReservePoolPda,
|
|
44
|
+
deriveStakeMetricsPda,
|
|
43
45
|
deriveVaultPda,
|
|
44
46
|
INDEX_SCALE,
|
|
47
|
+
PAY_RATE_SCALE_FACTOR,
|
|
45
48
|
} from './constants';
|
|
46
49
|
|
|
47
50
|
import { buildSolanaTrancheSnapshot, ceilDiv } from './utils';
|
|
@@ -73,6 +76,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
73
76
|
public leaderboardClient: LeaderboardClient;
|
|
74
77
|
public outpostClient: OutpostClient;
|
|
75
78
|
public tokenClient: TokenClient;
|
|
79
|
+
public program: SolanaProgramService
|
|
76
80
|
|
|
77
81
|
get solPubKey(): SolPubKey {
|
|
78
82
|
if (!this.pubKey) throw new Error('pubKey is undefined');
|
|
@@ -186,6 +190,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
186
190
|
this.leaderboardClient = new LeaderboardClient(this.anchor);
|
|
187
191
|
this.outpostClient = new OutpostClient(this.anchor);
|
|
188
192
|
this.tokenClient = new TokenClient(this.anchor);
|
|
193
|
+
this.program = new SolanaProgramService(this.anchor);
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
// ---------------------------------------------------------------------
|
|
@@ -477,27 +482,114 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
477
482
|
// READ-ONLY Public Methods
|
|
478
483
|
// ---------------------------------------------------------------------
|
|
479
484
|
|
|
480
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Returns the system APY (percent) for Solana,
|
|
487
|
+
* using compound interest per epoch and a
|
|
488
|
+
* cluster-derived epochs-per-year.
|
|
489
|
+
*/
|
|
481
490
|
async getSystemAPY(): Promise<number> {
|
|
482
|
-
//
|
|
483
|
-
const
|
|
491
|
+
// 1) Per-epoch rate (decimal) from on-chain stakeMetrics
|
|
492
|
+
const ratePerEpoch = await this.getEpochRateDecimalFromProgram();
|
|
493
|
+
console.log('epochRateDecimal', ratePerEpoch);
|
|
484
494
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
495
|
+
// 2) Live epochs-per-year estimate from cluster
|
|
496
|
+
const epochsPerYear = await this.getEpochsPerYearFromCluster();
|
|
497
|
+
console.log('epochsPerYear', epochsPerYear);
|
|
488
498
|
|
|
489
|
-
//
|
|
490
|
-
const
|
|
491
|
-
|
|
499
|
+
// 3) Compound: (1 + r)^N - 1
|
|
500
|
+
const apyDecimal = Math.pow(1 + ratePerEpoch, epochsPerYear) - 1;
|
|
501
|
+
console.log('apyDecimal', apyDecimal);
|
|
492
502
|
|
|
493
|
-
//
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
const apyPercent = apyDecimal * 100; // e.g. 7
|
|
503
|
+
// 4) Convert to percent
|
|
504
|
+
const apyPercent = apyDecimal * 100;
|
|
505
|
+
console.log('apyPercent', apyPercent);
|
|
497
506
|
|
|
498
507
|
return apyPercent;
|
|
499
508
|
}
|
|
500
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Reads the liqsol_core stakeMetrics account and returns the
|
|
512
|
+
* Solana per-epoch system rate as a **decimal** (not BPS),
|
|
513
|
+
* de-scaled using PAY_RATE_SCALE_FACTOR (1e12).
|
|
514
|
+
*/
|
|
515
|
+
private async getEpochRateDecimalFromProgram(): Promise<number> {
|
|
516
|
+
const liqSolCoreProgram = this.program.getProgram('liqsolCore');
|
|
517
|
+
const stakeMetricsPda = deriveStakeMetricsPda();
|
|
518
|
+
const stakeMetrics =
|
|
519
|
+
await liqSolCoreProgram.account.stakeMetrics.fetch(stakeMetricsPda);
|
|
520
|
+
|
|
521
|
+
// solSystemPayRate is stored on-chain with PAY_RATE_SCALE_FACTOR (1e12)
|
|
522
|
+
const raw = BigInt(stakeMetrics.solSystemPayRate.toString());
|
|
523
|
+
|
|
524
|
+
// Convert to JS number in **decimal per epoch** units
|
|
525
|
+
const rateDecimal = Number(raw) / Number(PAY_RATE_SCALE_FACTOR);
|
|
526
|
+
|
|
527
|
+
console.log('solSystemPayRate(raw)', raw.toString());
|
|
528
|
+
console.log('epochRateDecimal(computed)', rateDecimal);
|
|
529
|
+
|
|
530
|
+
return rateDecimal;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Simple cache so we don’t hammer RPC
|
|
534
|
+
private epochsPerYearCache?: { value: number; fetchedAt: number };
|
|
535
|
+
private static readonly EPOCHS_PER_YEAR_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Derive "epochs per year" from the live Solana cluster.
|
|
539
|
+
*
|
|
540
|
+
* Uses:
|
|
541
|
+
* - getRecentPerformanceSamples() to estimate slots/second
|
|
542
|
+
* - getEpochInfo() to read slotsInEpoch
|
|
543
|
+
*/
|
|
544
|
+
private async getEpochsPerYearFromCluster(): Promise<number> {
|
|
545
|
+
const now = Date.now();
|
|
546
|
+
|
|
547
|
+
if (
|
|
548
|
+
this.epochsPerYearCache &&
|
|
549
|
+
now - this.epochsPerYearCache.fetchedAt <
|
|
550
|
+
SolanaStakingClient.EPOCHS_PER_YEAR_TTL_MS
|
|
551
|
+
) {
|
|
552
|
+
return this.epochsPerYearCache.value;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const connection = this.anchor.connection;
|
|
556
|
+
|
|
557
|
+
// 1) Estimate slots/second from recent performance samples
|
|
558
|
+
const samples: PerfSample[] = await connection.getRecentPerformanceSamples(
|
|
559
|
+
60,
|
|
560
|
+
);
|
|
561
|
+
if (!samples.length) {
|
|
562
|
+
throw new Error('No performance samples available from cluster');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const totalSlots = samples.reduce((acc, s) => acc + s.numSlots, 0);
|
|
566
|
+
const totalSecs = samples.reduce((acc, s) => acc + s.samplePeriodSecs, 0);
|
|
567
|
+
|
|
568
|
+
if (totalSecs === 0) {
|
|
569
|
+
throw new Error(
|
|
570
|
+
'Cluster returned zero samplePeriodSecs in performance samples',
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const slotsPerSecond = totalSlots / totalSecs;
|
|
575
|
+
|
|
576
|
+
// 2) Slots per epoch from cluster
|
|
577
|
+
const epochInfo = await connection.getEpochInfo(); // finalized commitment by default
|
|
578
|
+
const slotsPerEpoch = epochInfo.slotsInEpoch;
|
|
579
|
+
|
|
580
|
+
const secondsPerEpoch = slotsPerEpoch / slotsPerSecond;
|
|
581
|
+
const secondsPerYear = 365 * 24 * 60 * 60;
|
|
582
|
+
|
|
583
|
+
const epochsPerYear = secondsPerYear / secondsPerEpoch;
|
|
584
|
+
|
|
585
|
+
this.epochsPerYearCache = {
|
|
586
|
+
value: epochsPerYear,
|
|
587
|
+
fetchedAt: now,
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
return epochsPerYear;
|
|
591
|
+
}
|
|
592
|
+
|
|
501
593
|
// ---------------------------------------------
|
|
502
594
|
// Deposit fee calculation (SOL -> liqSOL)
|
|
503
595
|
// ---------------------------------------------
|
|
@@ -520,7 +612,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
520
612
|
return BigInt(0);
|
|
521
613
|
}
|
|
522
614
|
|
|
523
|
-
const [avgPayRate, globalConfig]
|
|
615
|
+
const [avgPayRate, globalConfig]: [BN, GlobalConfig] = await Promise.all([
|
|
524
616
|
this.distributionClient.getAverageScaledPayRate(windowSize),
|
|
525
617
|
this.distributionClient.getGlobalConfig(),
|
|
526
618
|
]);
|
|
@@ -756,4 +848,5 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
756
848
|
);
|
|
757
849
|
}
|
|
758
850
|
}
|
|
851
|
+
|
|
759
852
|
}
|
package/src/staker.ts
CHANGED