@streamflow/staking 7.0.0-alpha.10

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 (39) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +216 -0
  3. package/dist/cjs/index.js +39 -0
  4. package/dist/cjs/solana/client.js +315 -0
  5. package/dist/cjs/solana/constants.js +61 -0
  6. package/dist/cjs/solana/descriptor/fee_manager.js +2 -0
  7. package/dist/cjs/solana/descriptor/idl/fee_manager.json +284 -0
  8. package/dist/cjs/solana/descriptor/idl/reward_pool.json +905 -0
  9. package/dist/cjs/solana/descriptor/idl/stake_pool.json +734 -0
  10. package/dist/cjs/solana/descriptor/reward_pool.js +2 -0
  11. package/dist/cjs/solana/descriptor/stake_pool.js +2 -0
  12. package/dist/cjs/solana/lib/derive-accounts.js +46 -0
  13. package/dist/cjs/solana/lib/rewards.js +134 -0
  14. package/dist/cjs/solana/types.js +2 -0
  15. package/dist/cjs/solana/utils.js +61 -0
  16. package/dist/esm/index.d.ts +6 -0
  17. package/dist/esm/index.js +6 -0
  18. package/dist/esm/solana/client.d.ts +77 -0
  19. package/dist/esm/solana/client.js +313 -0
  20. package/dist/esm/solana/constants.d.ts +50 -0
  21. package/dist/esm/solana/constants.js +58 -0
  22. package/dist/esm/solana/descriptor/fee_manager.d.ts +290 -0
  23. package/dist/esm/solana/descriptor/fee_manager.js +1 -0
  24. package/dist/esm/solana/descriptor/idl/fee_manager.json +284 -0
  25. package/dist/esm/solana/descriptor/idl/reward_pool.json +905 -0
  26. package/dist/esm/solana/descriptor/idl/stake_pool.json +734 -0
  27. package/dist/esm/solana/descriptor/reward_pool.d.ts +939 -0
  28. package/dist/esm/solana/descriptor/reward_pool.js +1 -0
  29. package/dist/esm/solana/descriptor/stake_pool.d.ts +538 -0
  30. package/dist/esm/solana/descriptor/stake_pool.js +1 -0
  31. package/dist/esm/solana/lib/derive-accounts.d.ts +10 -0
  32. package/dist/esm/solana/lib/derive-accounts.js +31 -0
  33. package/dist/esm/solana/lib/rewards.d.ts +24 -0
  34. package/dist/esm/solana/lib/rewards.js +136 -0
  35. package/dist/esm/solana/types.d.ts +66 -0
  36. package/dist/esm/solana/types.js +1 -0
  37. package/dist/esm/solana/utils.d.ts +11 -0
  38. package/dist/esm/solana/utils.js +50 -0
  39. package/package.json +48 -0
@@ -0,0 +1,136 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ // eslint-disable-next-line no-restricted-imports
3
+ import BN from "bn.js";
4
+ import { SCALE_PRECISION_FACTOR_BN } from "../constants.js";
5
+ export const REWARD_AMOUNT_PRECISION_FACTOR = new BN("1000000000");
6
+ export class RewardEntryAccumulator {
7
+ delegate;
8
+ lastAccountedTs;
9
+ claimedAmount;
10
+ accountedAmount;
11
+ rewardPool;
12
+ stakeEntry;
13
+ createdTs;
14
+ lastRewardAmount;
15
+ lastRewardPeriod;
16
+ buffer;
17
+ constructor(delegate) {
18
+ this.delegate = delegate;
19
+ this.lastAccountedTs = delegate.lastAccountedTs;
20
+ this.claimedAmount = delegate.claimedAmount;
21
+ this.accountedAmount = delegate.accountedAmount;
22
+ this.rewardPool = delegate.rewardPool;
23
+ this.stakeEntry = delegate.stakeEntry;
24
+ this.createdTs = delegate.createdTs;
25
+ this.buffer = delegate.buffer;
26
+ this.lastRewardAmount = delegate.lastRewardAmount;
27
+ this.lastRewardPeriod = delegate.lastRewardPeriod;
28
+ }
29
+ // Calculate accountable amount by calculating how many seconds have passed since last claim/stake time
30
+ getAccountableAmount(stakedTs, accountableTs, effectiveStakedAmount, rewardAmount, rewardPeriod) {
31
+ const lastAccountedTs = this.lastAccountedTs.gt(new BN(0)) ? this.lastAccountedTs : stakedTs;
32
+ const secondsPassed = accountableTs.sub(lastAccountedTs);
33
+ if (secondsPassed.lt(rewardPeriod)) {
34
+ return new BN(0);
35
+ }
36
+ const periodsPassed = secondsPassed.div(rewardPeriod);
37
+ const claimablePerEffectiveStake = periodsPassed.mul(rewardAmount);
38
+ const accountableAmount = claimablePerEffectiveStake.mul(effectiveStakedAmount).div(SCALE_PRECISION_FACTOR_BN);
39
+ return accountableAmount;
40
+ }
41
+ // Calculates claimable amount from accountable amount.
42
+ getClaimableAmount() {
43
+ const claimedAmount = this.claimedAmount.mul(REWARD_AMOUNT_PRECISION_FACTOR);
44
+ const nonClaimedAmount = this.accountedAmount.sub(claimedAmount);
45
+ const claimableAmount = nonClaimedAmount.div(REWARD_AMOUNT_PRECISION_FACTOR);
46
+ return claimableAmount;
47
+ }
48
+ // Get the time of the last unlock
49
+ getLastAccountedTs(stakedTs, claimableTs, rewardPeriod) {
50
+ const lastAccountedTs = this.lastAccountedTs.gtn(0) ? this.lastAccountedTs : stakedTs;
51
+ const totalSecondsPassed = claimableTs.sub(lastAccountedTs);
52
+ const periodsPassed = totalSecondsPassed.div(rewardPeriod);
53
+ const periodsToSeconds = periodsPassed.mul(rewardPeriod);
54
+ const currClaimTs = lastAccountedTs.add(periodsToSeconds);
55
+ return currClaimTs;
56
+ }
57
+ // Adds accounted amount
58
+ addAccountedAmount(accountedAmount) {
59
+ this.accountedAmount = this.accountedAmount.add(accountedAmount);
60
+ }
61
+ // Adds claimed amount
62
+ addClaimedAmount(claimedAmount) {
63
+ this.claimedAmount = this.claimedAmount.add(claimedAmount);
64
+ }
65
+ }
66
+ const createDefaultRewardEntry = (stakeEntry, rewardPool) => {
67
+ return {
68
+ stakeEntry: new PublicKey(stakeEntry.publicKey),
69
+ rewardPool: new PublicKey(rewardPool.publicKey),
70
+ createdTs: stakeEntry.account.createdTs,
71
+ lastAccountedTs: new BN(0),
72
+ lastRewardAmount: new BN(0),
73
+ lastRewardPeriod: new BN(0),
74
+ accountedAmount: new BN(0),
75
+ claimedAmount: new BN(0),
76
+ buffer: [],
77
+ };
78
+ };
79
+ export const calcRewards = (rewardEntryAccount, stakeEntryAccount, rewardPoolAccount) => {
80
+ const rewardEntry = rewardEntryAccount?.account ?? createDefaultRewardEntry(stakeEntryAccount, rewardPoolAccount);
81
+ const stakeEntry = stakeEntryAccount.account;
82
+ const rewardPool = rewardPoolAccount.account;
83
+ const rewardEntryAccumulator = new RewardEntryAccumulator(rewardEntry);
84
+ if (rewardEntryAccumulator.createdTs.lt(stakeEntry.createdTs)) {
85
+ throw new Error("InvalidRewardEntry");
86
+ }
87
+ const currTs = Math.floor(Date.now() / 1000);
88
+ const stakedTs = rewardPool.createdTs ? BN.max(stakeEntry.createdTs, rewardPool.createdTs) : stakeEntry.createdTs;
89
+ const claimableTs = stakeEntry.closedTs.gtn(0) ? stakeEntry.closedTs : new BN(currTs);
90
+ const amountUpdated = !rewardPool.rewardAmount.eq(rewardPool.lastRewardAmount) &&
91
+ rewardPool.lastAmountUpdateTs.gt(stakeEntry.createdTs) &&
92
+ rewardPool.lastAmountUpdateTs.gt(stakeEntry.closedTs);
93
+ const periodUpdated = !rewardPool.rewardPeriod.eq(rewardPool.lastRewardPeriod) &&
94
+ rewardPool.lastPeriodUpdateTs.gt(stakeEntry.createdTs) &&
95
+ rewardPool.lastPeriodUpdateTs.gt(stakeEntry.closedTs);
96
+ if (amountUpdated || periodUpdated) {
97
+ let firstUpdateTs, secondUpdateTs, rewardAmount, rewardPeriod;
98
+ if (amountUpdated && periodUpdated) {
99
+ if (rewardPool.lastAmountUpdateTs.lt(rewardPool.lastPeriodUpdateTs)) {
100
+ firstUpdateTs = rewardPool.lastAmountUpdateTs;
101
+ secondUpdateTs = rewardPool.lastPeriodUpdateTs;
102
+ rewardAmount = rewardPool.rewardAmount;
103
+ rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
104
+ }
105
+ else {
106
+ firstUpdateTs = rewardPool.lastPeriodUpdateTs;
107
+ secondUpdateTs = rewardPool.lastAmountUpdateTs;
108
+ rewardAmount = rewardEntryAccumulator.lastRewardAmount;
109
+ rewardPeriod = rewardPool.rewardPeriod;
110
+ }
111
+ }
112
+ else if (amountUpdated) {
113
+ firstUpdateTs = new BN(0);
114
+ secondUpdateTs = rewardPool.lastAmountUpdateTs;
115
+ rewardAmount = rewardEntryAccumulator.lastRewardAmount;
116
+ rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
117
+ }
118
+ else {
119
+ firstUpdateTs = new BN(0);
120
+ secondUpdateTs = rewardPool.lastPeriodUpdateTs;
121
+ rewardAmount = rewardEntryAccumulator.lastRewardAmount;
122
+ rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
123
+ }
124
+ if (firstUpdateTs.gtn(0)) {
125
+ const firstAccountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, firstUpdateTs, stakeEntry.effectiveAmount, rewardEntryAccumulator.lastRewardAmount, rewardEntryAccumulator.lastRewardPeriod);
126
+ rewardEntryAccumulator.addAccountedAmount(firstAccountableAmount);
127
+ rewardEntryAccumulator.lastAccountedTs = rewardEntryAccumulator.getLastAccountedTs(stakedTs, firstUpdateTs, rewardPool.lastRewardPeriod);
128
+ }
129
+ const secondAccountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, secondUpdateTs, stakeEntry.effectiveAmount, rewardAmount, rewardPeriod);
130
+ rewardEntryAccumulator.addAccountedAmount(secondAccountableAmount);
131
+ rewardEntryAccumulator.lastAccountedTs = rewardEntryAccumulator.getLastAccountedTs(stakedTs, secondUpdateTs, rewardPeriod);
132
+ }
133
+ const accountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, claimableTs, stakeEntry.effectiveAmount, rewardPool.rewardAmount, rewardPool.rewardPeriod);
134
+ rewardEntryAccumulator.addAccountedAmount(accountableAmount);
135
+ return rewardEntryAccumulator.getClaimableAmount();
136
+ };
@@ -0,0 +1,66 @@
1
+ import { Address, type IdlTypes } from "@coral-xyz/anchor";
2
+ import { SignerWalletAdapter } from "@solana/wallet-adapter-base";
3
+ import { Keypair } from "@solana/web3.js";
4
+ import { ITransactionSolanaExt } from "@streamflow/common/solana";
5
+ import BN from "bn.js";
6
+ import { RewardPool as RewardPoolIDL } from "./descriptor/reward_pool.js";
7
+ import { StakePool as StakePoolIDL } from "./descriptor/stake_pool.js";
8
+ import { FeeManager as FeeManagerIDL } from "./descriptor/fee_manager.js";
9
+ export type StakePool = IdlTypes<StakePoolIDL>["stakePool"];
10
+ export type StakeEntry = IdlTypes<StakePoolIDL>["stakeEntry"];
11
+ export type RewardEntry = IdlTypes<RewardPoolIDL>["rewardEntry"];
12
+ export type RewardPool = IdlTypes<RewardPoolIDL>["rewardPool"];
13
+ export type FeeValue = IdlTypes<FeeManagerIDL>["feeValue"];
14
+ export interface IInteractSolanaExt extends ITransactionSolanaExt {
15
+ invoker: SignerWalletAdapter | Keypair;
16
+ }
17
+ export interface BaseStakePoolArgs {
18
+ stakePool: Address;
19
+ stakePoolMint: Address;
20
+ }
21
+ interface TokenProgram {
22
+ tokenProgramId?: Address;
23
+ }
24
+ interface StakeBaseArgs extends BaseStakePoolArgs, TokenProgram {
25
+ nonce: number;
26
+ }
27
+ export type UnstakeArgs = StakeBaseArgs;
28
+ export interface StakeArgs extends StakeBaseArgs {
29
+ amount: BN;
30
+ duration: BN;
31
+ payer?: Keypair;
32
+ authority?: Address;
33
+ }
34
+ export interface FundPoolArgs extends BaseStakePoolArgs, TokenProgram {
35
+ amount: BN;
36
+ nonce: number;
37
+ rewardMint: Address;
38
+ feeValue: Address | null;
39
+ }
40
+ export interface CreateRewardEntryArgs extends BaseStakePoolArgs, TokenProgram {
41
+ depositNonce: number;
42
+ rewardPoolNonce: number;
43
+ }
44
+ export interface CreateRewardPoolArgs extends BaseStakePoolArgs, TokenProgram {
45
+ stakePoolNonce: number;
46
+ rewardMint: Address;
47
+ nonce: number;
48
+ rewardAmount: BN;
49
+ rewardPeriod: BN;
50
+ permissionless: boolean;
51
+ }
52
+ export interface ClaimRewardPoolArgs extends BaseStakePoolArgs, TokenProgram {
53
+ depositNonce: number;
54
+ rewardMint: Address;
55
+ rewardPoolNonce: number;
56
+ }
57
+ export interface CreateStakePoolArgs extends TokenProgram {
58
+ mint: Address;
59
+ nonce: number;
60
+ maxWeight: BN;
61
+ minDuration: BN;
62
+ maxDuration: BN;
63
+ permissionless?: boolean;
64
+ authority?: Keypair;
65
+ }
66
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { TransferFeeConfig } from "@solana/spl-token";
2
+ import { Connection } from "@solana/web3.js";
3
+ import BN from "bn.js";
4
+ export declare const calculateStakeWeight: (minDuration: BN, maxDuration: BN, maxWeight: BN, duration: BN) => BN;
5
+ export declare const calculateFeeAmount: (amount: BN, fee?: BN) => BN;
6
+ export declare const calculateDecimalsShift: (maxWeight: bigint, maxShift?: number) => number;
7
+ export declare const divCeilN: (n: bigint, d: bigint) => bigint;
8
+ export declare function calculateAmountWithTransferFees(connection: Connection, transferFeeConfig: TransferFeeConfig, transferAmount: bigint): Promise<{
9
+ transferAmount: bigint;
10
+ feeCharged: bigint;
11
+ }>;
@@ -0,0 +1,50 @@
1
+ // eslint-disable-next-line no-restricted-imports
2
+ import BN from "bn.js";
3
+ import { DEFAULT_FEE_BN, FEE_PRECISION_FACTOR_BN, SCALE_PRECISION_FACTOR, SCALE_PRECISION_FACTOR_BN, U64_MAX, } from "./constants.js";
4
+ export const calculateStakeWeight = (minDuration, maxDuration, maxWeight, duration) => {
5
+ const durationSpan = maxDuration.sub(minDuration);
6
+ if (durationSpan.eq(new BN(0))) {
7
+ return SCALE_PRECISION_FACTOR_BN;
8
+ }
9
+ const durationExceedingMin = duration.sub(minDuration);
10
+ const normalizedWeight = durationExceedingMin.mul(SCALE_PRECISION_FACTOR_BN).div(durationSpan);
11
+ const weightDiff = maxWeight.sub(SCALE_PRECISION_FACTOR_BN);
12
+ return BN.max(SCALE_PRECISION_FACTOR_BN.add(normalizedWeight.mul(weightDiff).div(SCALE_PRECISION_FACTOR_BN)), SCALE_PRECISION_FACTOR_BN);
13
+ };
14
+ export const calculateFeeAmount = (amount, fee = DEFAULT_FEE_BN) => {
15
+ if (fee.eq(FEE_PRECISION_FACTOR_BN)) {
16
+ return amount;
17
+ }
18
+ return amount.mul(fee).div(FEE_PRECISION_FACTOR_BN);
19
+ };
20
+ export const calculateDecimalsShift = (maxWeight, maxShift = 999) => {
21
+ if (maxShift == 0) {
22
+ return 0;
23
+ }
24
+ let decimalsShift = 0;
25
+ while ((maxWeight * U64_MAX) / BigInt(SCALE_PRECISION_FACTOR) / BigInt(10 ** decimalsShift) > U64_MAX) {
26
+ decimalsShift += 1;
27
+ if (decimalsShift == maxShift) {
28
+ return maxShift;
29
+ }
30
+ }
31
+ return decimalsShift;
32
+ };
33
+ export const divCeilN = (n, d) => n / d + (n % d ? BigInt(1) : BigInt(0));
34
+ export async function calculateAmountWithTransferFees(connection, transferFeeConfig, transferAmount) {
35
+ const epoch = await connection.getEpochInfo();
36
+ const transferFee = epoch.epoch >= transferFeeConfig.newerTransferFee.epoch
37
+ ? transferFeeConfig.newerTransferFee
38
+ : transferFeeConfig.olderTransferFee;
39
+ const transferFeeBasisPoints = BigInt(transferFee.transferFeeBasisPoints);
40
+ let feeCharged = BigInt(0);
41
+ if (transferFeeBasisPoints !== BigInt(0)) {
42
+ const numerator = transferAmount * 10000n;
43
+ const denominator = 10000n - transferFeeBasisPoints;
44
+ const rawPreFeeAmount = divCeilN(numerator, denominator);
45
+ const fee = rawPreFeeAmount - transferAmount;
46
+ transferAmount = rawPreFeeAmount;
47
+ feeCharged = fee > transferFee.maximumFee ? transferFee.maximumFee : fee;
48
+ }
49
+ return { transferAmount, feeCharged };
50
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@streamflow/staking",
3
+ "version": "7.0.0-alpha.10",
4
+ "description": "JavaScript SDK to interact with Streamflow Staking protocol.",
5
+ "homepage": "https://github.com/streamflow-finance/js-sdk/",
6
+ "main": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/cjs/index.js",
13
+ "types": "./dist/esm/index.d.ts"
14
+ },
15
+ "./solana": {
16
+ "import": "./dist/esm/solana/index.js",
17
+ "require": "./dist/cjs/solana/index.js",
18
+ "types": "./dist/esm/solana/index.d.ts"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build:cjs": "rm -rf dist/cjs; tsc -p tsconfig.cjs.json",
23
+ "build:esm": "rm -rf dist/esm; tsc -p tsconfig.esm.json",
24
+ "build": "rm -rf dist; pnpm run build:cjs && pnpm run build:esm",
25
+ "pack": "pnpm build && pnpm pack",
26
+ "lint": "eslint --fix .",
27
+ "prepublishOnly": "npm run lint && npm run build"
28
+ },
29
+ "gitHead": "263638b9fd29395dcb64c33dfc2197df47f526af",
30
+ "devDependencies": {
31
+ "@streamflow/eslint-config": "7.0.0-alpha.10",
32
+ "@types/bn.js": "5.1.1",
33
+ "typescript": "^4.9.5"
34
+ },
35
+ "dependencies": {
36
+ "@coral-xyz/anchor": "^0.30.0",
37
+ "@coral-xyz/borsh": "^0.29.0",
38
+ "@solana/buffer-layout": "4.0.1",
39
+ "@solana/spl-token": "0.3.6",
40
+ "@solana/wallet-adapter-base": "0.9.19",
41
+ "@solana/web3.js": "1.70.1",
42
+ "@streamflow/common": "7.0.0-alpha.10",
43
+ "bn.js": "5.2.1",
44
+ "borsh": "^2.0.0",
45
+ "bs58": "5.0.0",
46
+ "p-queue": "^8.0.1"
47
+ }
48
+ }