@streamflow/staking 7.0.0-alpha.9 → 7.0.0
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/README.md +48 -2
- package/dist/cjs/__tests__/solana/rewards.spec.js +61 -0
- package/dist/cjs/index.js +3 -5
- package/dist/cjs/solana/client.js +76 -57
- package/dist/cjs/solana/constants.js +12 -3
- package/dist/cjs/solana/descriptor/idl/reward_pool.json +3 -5
- package/dist/cjs/solana/descriptor/idl/stake_pool.json +60 -262
- package/dist/cjs/solana/{utils.js → lib/fee-amounts.js} +5 -22
- package/dist/cjs/solana/lib/rewards.js +41 -21
- package/dist/cjs/solana/lib/stake-weight.js +19 -0
- package/dist/esm/__tests__/solana/rewards.spec.d.ts +1 -0
- package/dist/esm/__tests__/solana/rewards.spec.js +56 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/solana/client.d.ts +14 -3
- package/dist/esm/solana/client.js +50 -32
- package/dist/esm/solana/constants.d.ts +5 -3
- package/dist/esm/solana/constants.js +13 -4
- package/dist/esm/solana/descriptor/idl/reward_pool.json +3 -5
- package/dist/esm/solana/descriptor/idl/stake_pool.json +60 -262
- package/dist/esm/solana/{utils.d.ts → lib/fee-amounts.d.ts} +0 -2
- package/dist/esm/solana/{utils.js → lib/fee-amounts.js} +2 -14
- package/dist/esm/solana/lib/rewards.d.ts +6 -4
- package/dist/esm/solana/lib/rewards.js +38 -22
- package/dist/esm/solana/lib/stake-weight.d.ts +2 -0
- package/dist/esm/solana/lib/stake-weight.js +12 -0
- package/dist/esm/solana/types.d.ts +6 -3
- package/package.json +8 -7
|
@@ -3,24 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.calculateRewardAmountFromRate = exports.calculateRewardAmountFromValue = exports.calculateRewardRateFromAmount = exports.calcRewards = exports.RewardEntryAccumulator = void 0;
|
|
7
7
|
const web3_js_1 = require("@solana/web3.js");
|
|
8
8
|
// eslint-disable-next-line no-restricted-imports
|
|
9
9
|
const bn_js_1 = __importDefault(require("bn.js"));
|
|
10
|
+
const common_1 = require("@streamflow/common");
|
|
10
11
|
const constants_js_1 = require("../constants.js");
|
|
11
|
-
exports.REWARD_AMOUNT_PRECISION_FACTOR = new bn_js_1.default("1000000000");
|
|
12
12
|
class RewardEntryAccumulator {
|
|
13
|
-
constructor(
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
|
|
13
|
+
constructor(lastAccountedTs, claimedAmount, accountedAmount, rewardPool, stakeEntry, createdTs, lastRewardAmount, lastRewardPeriod, buffer) {
|
|
14
|
+
this.lastAccountedTs = lastAccountedTs;
|
|
15
|
+
this.claimedAmount = claimedAmount;
|
|
16
|
+
this.accountedAmount = accountedAmount;
|
|
17
|
+
this.rewardPool = rewardPool;
|
|
18
|
+
this.stakeEntry = stakeEntry;
|
|
19
|
+
this.createdTs = createdTs;
|
|
20
|
+
this.buffer = buffer;
|
|
21
|
+
this.lastRewardAmount = lastRewardAmount;
|
|
22
|
+
this.lastRewardPeriod = lastRewardPeriod;
|
|
23
|
+
}
|
|
24
|
+
static fromEntry(entry) {
|
|
25
|
+
return new this(entry.lastAccountedTs, entry.claimedAmount, entry.accountedAmount, entry.rewardPool, entry.stakeEntry, entry.createdTs, entry.lastRewardAmount, entry.lastRewardPeriod, entry.buffer);
|
|
24
26
|
}
|
|
25
27
|
// Calculate accountable amount by calculating how many seconds have passed since last claim/stake time
|
|
26
28
|
getAccountableAmount(stakedTs, accountableTs, effectiveStakedAmount, rewardAmount, rewardPeriod) {
|
|
@@ -31,15 +33,13 @@ class RewardEntryAccumulator {
|
|
|
31
33
|
}
|
|
32
34
|
const periodsPassed = secondsPassed.div(rewardPeriod);
|
|
33
35
|
const claimablePerEffectiveStake = periodsPassed.mul(rewardAmount);
|
|
34
|
-
|
|
35
|
-
return accountableAmount;
|
|
36
|
+
return claimablePerEffectiveStake.mul(effectiveStakedAmount).div(constants_js_1.SCALE_PRECISION_FACTOR_BN);
|
|
36
37
|
}
|
|
37
38
|
// Calculates claimable amount from accountable amount.
|
|
38
39
|
getClaimableAmount() {
|
|
39
|
-
const claimedAmount = this.claimedAmount.mul(
|
|
40
|
+
const claimedAmount = this.claimedAmount.mul(constants_js_1.REWARD_AMOUNT_PRECISION_FACTOR_BN);
|
|
40
41
|
const nonClaimedAmount = this.accountedAmount.sub(claimedAmount);
|
|
41
|
-
|
|
42
|
-
return claimableAmount;
|
|
42
|
+
return nonClaimedAmount.div(constants_js_1.REWARD_AMOUNT_PRECISION_FACTOR_BN);
|
|
43
43
|
}
|
|
44
44
|
// Get the time of the last unlock
|
|
45
45
|
getLastAccountedTs(stakedTs, claimableTs, rewardPeriod) {
|
|
@@ -47,8 +47,7 @@ class RewardEntryAccumulator {
|
|
|
47
47
|
const totalSecondsPassed = claimableTs.sub(lastAccountedTs);
|
|
48
48
|
const periodsPassed = totalSecondsPassed.div(rewardPeriod);
|
|
49
49
|
const periodsToSeconds = periodsPassed.mul(rewardPeriod);
|
|
50
|
-
|
|
51
|
-
return currClaimTs;
|
|
50
|
+
return lastAccountedTs.add(periodsToSeconds);
|
|
52
51
|
}
|
|
53
52
|
// Adds accounted amount
|
|
54
53
|
addAccountedAmount(accountedAmount) {
|
|
@@ -77,7 +76,7 @@ const calcRewards = (rewardEntryAccount, stakeEntryAccount, rewardPoolAccount) =
|
|
|
77
76
|
const rewardEntry = rewardEntryAccount?.account ?? createDefaultRewardEntry(stakeEntryAccount, rewardPoolAccount);
|
|
78
77
|
const stakeEntry = stakeEntryAccount.account;
|
|
79
78
|
const rewardPool = rewardPoolAccount.account;
|
|
80
|
-
const rewardEntryAccumulator =
|
|
79
|
+
const rewardEntryAccumulator = RewardEntryAccumulator.fromEntry(rewardEntry);
|
|
81
80
|
if (rewardEntryAccumulator.createdTs.lt(stakeEntry.createdTs)) {
|
|
82
81
|
throw new Error("InvalidRewardEntry");
|
|
83
82
|
}
|
|
@@ -132,3 +131,24 @@ const calcRewards = (rewardEntryAccount, stakeEntryAccount, rewardPoolAccount) =
|
|
|
132
131
|
return rewardEntryAccumulator.getClaimableAmount();
|
|
133
132
|
};
|
|
134
133
|
exports.calcRewards = calcRewards;
|
|
134
|
+
const calculateRewardRateFromAmount = (rewardAmount, stakeTokenDecimals, rewardTokenDecimals) => {
|
|
135
|
+
const decimals = rewardTokenDecimals + (constants_js_1.REWARD_AMOUNT_DECIMALS - stakeTokenDecimals);
|
|
136
|
+
return (0, common_1.getNumberFromBN)(rewardAmount, decimals);
|
|
137
|
+
};
|
|
138
|
+
exports.calculateRewardRateFromAmount = calculateRewardRateFromAmount;
|
|
139
|
+
const calculateRewardAmountFromValue = (rewardTokenValue, stakeTokenDecimals) => {
|
|
140
|
+
const decimalsDiff = constants_js_1.REWARD_AMOUNT_DECIMALS - stakeTokenDecimals;
|
|
141
|
+
if (decimalsDiff === 0) {
|
|
142
|
+
return rewardTokenValue;
|
|
143
|
+
}
|
|
144
|
+
const diffFactor = new bn_js_1.default(10).pow(new bn_js_1.default(Math.abs(decimalsDiff)));
|
|
145
|
+
if (decimalsDiff > 0) {
|
|
146
|
+
return rewardTokenValue.mul(diffFactor);
|
|
147
|
+
}
|
|
148
|
+
return rewardTokenValue.div(diffFactor);
|
|
149
|
+
};
|
|
150
|
+
exports.calculateRewardAmountFromValue = calculateRewardAmountFromValue;
|
|
151
|
+
const calculateRewardAmountFromRate = (rewardRate, stakeTokenDecimals, rewardTokenDecimals) => {
|
|
152
|
+
return (0, exports.calculateRewardAmountFromValue)((0, common_1.getBN)(rewardRate, rewardTokenDecimals), stakeTokenDecimals);
|
|
153
|
+
};
|
|
154
|
+
exports.calculateRewardAmountFromRate = calculateRewardAmountFromRate;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.calculateStakeWeight = void 0;
|
|
7
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
8
|
+
const constants_js_1 = require("../constants.js");
|
|
9
|
+
const calculateStakeWeight = (minDuration, maxDuration, maxWeight, duration) => {
|
|
10
|
+
const durationSpan = maxDuration.sub(minDuration);
|
|
11
|
+
if (durationSpan.eq(new bn_js_1.default(0))) {
|
|
12
|
+
return constants_js_1.SCALE_PRECISION_FACTOR_BN;
|
|
13
|
+
}
|
|
14
|
+
const durationExceedingMin = duration.sub(minDuration);
|
|
15
|
+
const normalizedWeight = durationExceedingMin.mul(constants_js_1.SCALE_PRECISION_FACTOR_BN).div(durationSpan);
|
|
16
|
+
const weightDiff = maxWeight.sub(constants_js_1.SCALE_PRECISION_FACTOR_BN);
|
|
17
|
+
return bn_js_1.default.max(constants_js_1.SCALE_PRECISION_FACTOR_BN.add(normalizedWeight.mul(weightDiff).div(constants_js_1.SCALE_PRECISION_FACTOR_BN)), constants_js_1.SCALE_PRECISION_FACTOR_BN);
|
|
18
|
+
};
|
|
19
|
+
exports.calculateStakeWeight = calculateStakeWeight;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getBN } from "@streamflow/common";
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
// eslint-disable-next-line no-restricted-imports
|
|
4
|
+
import BN from "bn.js";
|
|
5
|
+
import { describe, expect, test } from "vitest";
|
|
6
|
+
import { RewardEntryAccumulator, calculateRewardAmountFromRate, calculateRewardRateFromAmount, } from "../../solana/lib/rewards.js";
|
|
7
|
+
import { SCALE_PRECISION_FACTOR_BN } from "../../solana/constants.js";
|
|
8
|
+
const populateRewardEntry = (effectiveStakedAmount, rewardAmount, rewardPeriod, periods) => {
|
|
9
|
+
const rewardEntry = new RewardEntryAccumulator(new BN(0), new BN(0), new BN(0), PublicKey.default, PublicKey.default, new BN(0), new BN(0), new BN(0), []);
|
|
10
|
+
if (effectiveStakedAmount && rewardAmount && rewardPeriod) {
|
|
11
|
+
rewardEntry.accountedAmount = rewardEntry.getAccountableAmount(new BN(0), rewardPeriod.muln(periods || 1), effectiveStakedAmount, rewardAmount, rewardPeriod);
|
|
12
|
+
}
|
|
13
|
+
return rewardEntry;
|
|
14
|
+
};
|
|
15
|
+
describe("RewardEntryAccumulator", () => {
|
|
16
|
+
describe("getClaimableAmount", () => {
|
|
17
|
+
const testCases = [
|
|
18
|
+
[9, 9, 1, 0.0025, new BN(2_500_000)],
|
|
19
|
+
[6, 9, 1, 0.0025, new BN(2_500_000_000)],
|
|
20
|
+
[9, 6, 1, 0.0025, new BN(2_500)],
|
|
21
|
+
[1, 8, 1, 0.0025, new BN(25_000_000_000_000)],
|
|
22
|
+
];
|
|
23
|
+
testCases.forEach(([stakeTokenDecimals, rewardTokenDecimals, periods, rewardRate, expectedRewardAmount]) => {
|
|
24
|
+
test(`test decimals - ${stakeTokenDecimals}/${rewardTokenDecimals}/${periods}/${rewardRate}`, () => {
|
|
25
|
+
const stakedAmount = getBN(1, stakeTokenDecimals);
|
|
26
|
+
const effectiveStakedAmount = stakedAmount.mul(SCALE_PRECISION_FACTOR_BN);
|
|
27
|
+
const rewardPeriod = new BN(1);
|
|
28
|
+
const rewardAmount = calculateRewardAmountFromRate(rewardRate, stakeTokenDecimals, rewardTokenDecimals);
|
|
29
|
+
const rewardEntry = populateRewardEntry(effectiveStakedAmount, rewardAmount, rewardPeriod, periods);
|
|
30
|
+
const claimableAmount = rewardEntry.getClaimableAmount();
|
|
31
|
+
expect(rewardAmount.toString()).toEqual(expectedRewardAmount.toString());
|
|
32
|
+
expect(claimableAmount.toString()).toEqual(getBN(rewardRate, rewardTokenDecimals).muln(periods).toString());
|
|
33
|
+
expect(calculateRewardRateFromAmount(rewardAmount, stakeTokenDecimals, rewardTokenDecimals)).toEqual(rewardRate);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
test(`test decimals - negative difference`, () => {
|
|
37
|
+
let rewardAmount = calculateRewardAmountFromRate(0.0025, 18, 1);
|
|
38
|
+
expect(rewardAmount.toString()).toEqual(new BN(0).toString());
|
|
39
|
+
rewardAmount = calculateRewardAmountFromRate(0.0025, 12, 4);
|
|
40
|
+
expect(rewardAmount.toString()).toEqual(new BN(0).toString());
|
|
41
|
+
});
|
|
42
|
+
test(`test decimals - precision loss`, () => {
|
|
43
|
+
const stakeTokenDecimals = 12;
|
|
44
|
+
const rewardTokenDecimals = 6;
|
|
45
|
+
const stakedAmount = getBN(1, stakeTokenDecimals);
|
|
46
|
+
const effectiveStakedAmount = stakedAmount.mul(SCALE_PRECISION_FACTOR_BN);
|
|
47
|
+
const rewardPeriod = new BN(1);
|
|
48
|
+
const rewardAmount = calculateRewardAmountFromRate(0.0025, stakeTokenDecimals, rewardTokenDecimals);
|
|
49
|
+
const rewardEntry = populateRewardEntry(effectiveStakedAmount, rewardAmount, rewardPeriod);
|
|
50
|
+
const claimableAmount = rewardEntry.getClaimableAmount();
|
|
51
|
+
expect(rewardAmount.toString()).toEqual(new BN(2).toString());
|
|
52
|
+
expect(claimableAmount.toString()).toEqual(getBN(0.002, rewardTokenDecimals).toString());
|
|
53
|
+
expect(calculateRewardRateFromAmount(rewardAmount, stakeTokenDecimals, rewardTokenDecimals)).toEqual(0.002);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export * from "./solana/utils.js";
|
|
1
|
+
export { SolanaStakingClient } from "./solana/client.js";
|
|
3
2
|
export * from "./solana/types.js";
|
|
4
3
|
export * from "./solana/lib/derive-accounts.js";
|
|
5
4
|
export * from "./solana/lib/rewards.js";
|
|
5
|
+
export * from "./solana/lib/fee-amounts.js";
|
|
6
|
+
export * from "./solana/lib/stake-weight.js";
|
|
6
7
|
export * as constants from "./solana/constants.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export * from "./solana/utils.js";
|
|
1
|
+
export { SolanaStakingClient } from "./solana/client.js";
|
|
3
2
|
export * from "./solana/types.js";
|
|
4
3
|
export * from "./solana/lib/derive-accounts.js";
|
|
5
4
|
export * from "./solana/lib/rewards.js";
|
|
5
|
+
export * from "./solana/lib/fee-amounts.js";
|
|
6
|
+
export * from "./solana/lib/stake-weight.js";
|
|
6
7
|
export * as constants from "./solana/constants.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Program, ProgramAccount } from "@coral-xyz/anchor";
|
|
1
|
+
import { AccountsCoder, Idl, IdlTypes, Program, ProgramAccount } from "@coral-xyz/anchor";
|
|
2
2
|
import { Commitment, Connection, ConnectionConfig, PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
3
3
|
import { ICluster, ITransactionResult } from "@streamflow/common";
|
|
4
4
|
import PQueue from "p-queue";
|
|
@@ -6,7 +6,7 @@ import { REWARD_ENTRY_BYTE_OFFSETS, STAKE_ENTRY_BYTE_OFFSETS, STAKE_POOL_BYTE_OF
|
|
|
6
6
|
import { FeeManager as FeeManagerProgramType } from "./descriptor/fee_manager.js";
|
|
7
7
|
import { RewardPool as RewardPoolProgramType } from "./descriptor/reward_pool.js";
|
|
8
8
|
import { StakePool as StakePoolProgramType } from "./descriptor/stake_pool.js";
|
|
9
|
-
import { ClaimRewardPoolArgs, CreateRewardEntryArgs, CreateRewardPoolArgs, CreateStakePoolArgs, FeeValue, FundPoolArgs, IInteractSolanaExt, RewardEntry, RewardPool, StakeArgs, StakeEntry, StakePool, UnstakeArgs } from "./types.js";
|
|
9
|
+
import { ClaimRewardPoolArgs, CreateRewardEntryArgs, CreateRewardPoolArgs, CreateStakePoolArgs, DefaultFeeValueConfig, FeeValue, FundPoolArgs, IInteractSolanaExt, RewardEntry, RewardPool, StakeArgs, StakeEntry, StakePool, UnstakeArgs, UpdateRewardPoolArgs } from "./types.js";
|
|
10
10
|
interface Programs {
|
|
11
11
|
stakePoolProgram: Program<StakePoolProgramType>;
|
|
12
12
|
rewardPoolProgram: Program<RewardPoolProgramType>;
|
|
@@ -27,7 +27,7 @@ interface IInitOptions {
|
|
|
27
27
|
sendRate?: number;
|
|
28
28
|
sendThrottler?: PQueue;
|
|
29
29
|
}
|
|
30
|
-
export
|
|
30
|
+
export declare class SolanaStakingClient {
|
|
31
31
|
connection: Connection;
|
|
32
32
|
private commitment;
|
|
33
33
|
private sendThrottler;
|
|
@@ -41,6 +41,8 @@ export default class SolanaStakingClient {
|
|
|
41
41
|
searchStakeEntries(criteria?: Partial<Pick<StakeEntry, keyof typeof STAKE_ENTRY_BYTE_OFFSETS>>): Promise<ProgramAccount<StakeEntry>[]>;
|
|
42
42
|
searchRewardPools(criteria?: Partial<Pick<RewardPool, "stakePool" | "mint">>): Promise<ProgramAccount<RewardPool>[]>;
|
|
43
43
|
searchRewardEntries(criteria: Partial<Pick<RewardEntry, keyof typeof REWARD_ENTRY_BYTE_OFFSETS>>): Promise<ProgramAccount<RewardEntry>[]>;
|
|
44
|
+
getFee(target: string | PublicKey): Promise<FeeValue | DefaultFeeValueConfig>;
|
|
45
|
+
getDefaultFeeValue(): Promise<DefaultFeeValueConfig>;
|
|
44
46
|
getFeeValueIfExists(target: string | PublicKey): Promise<FeeValue | null>;
|
|
45
47
|
createStakePool(data: CreateStakePoolArgs, extParams: IInteractSolanaExt): Promise<CreationResult>;
|
|
46
48
|
prepareCreateStakePoolInstructions({ maxWeight, maxDuration, minDuration, mint, permissionless, nonce, tokenProgramId, }: CreateStakePoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
@@ -72,6 +74,15 @@ export default class SolanaStakingClient {
|
|
|
72
74
|
prepareCreateRewardEntryInstructions({ stakePoolMint, stakePool, rewardPoolNonce, depositNonce }: CreateRewardEntryArgs, extParams: IInteractSolanaExt): Promise<{
|
|
73
75
|
ixs: TransactionInstruction[];
|
|
74
76
|
}>;
|
|
77
|
+
updateRewardPool(data: UpdateRewardPoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
78
|
+
ixs: TransactionInstruction[];
|
|
79
|
+
txId: string;
|
|
80
|
+
}>;
|
|
81
|
+
prepareUpdateRewardPoolInstructions({ rewardPool, rewardAmount, rewardPeriod }: UpdateRewardPoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
82
|
+
ixs: TransactionInstruction[];
|
|
83
|
+
}>;
|
|
84
|
+
decode<ProgramName extends keyof Programs = keyof Programs, DecodingProgram = Programs[ProgramName], DerivedIdl extends Idl = DecodingProgram extends Program<infer IDLType> ? IDLType : never, AccountName extends keyof IdlTypes<DerivedIdl> = keyof IdlTypes<DerivedIdl>, DecodedAccount = IdlTypes<DerivedIdl>[AccountName]>(programKey: ProgramName, accountName: AccountName, accInfo: Parameters<AccountsCoder["decode"]>[1]): DecodedAccount;
|
|
85
|
+
getDiscriminator<ProgramName extends keyof Programs = keyof Programs, DecodingProgram = Programs[ProgramName], DerivedIdl extends Idl = DecodingProgram extends Program<infer IDLType> ? IDLType : never, AccountName extends keyof IdlTypes<DerivedIdl> = keyof IdlTypes<DerivedIdl>>(programKey: ProgramName, accountName: AccountName): number[];
|
|
75
86
|
private execute;
|
|
76
87
|
}
|
|
77
88
|
export {};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Program, parseIdlErrors, translateError, } from "@coral-xyz/anchor";
|
|
2
2
|
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
|
|
3
|
-
import { Connection, PublicKey
|
|
4
|
-
import { ContractError, ICluster } from "@streamflow/common";
|
|
5
|
-
import { buildSendThrottler, checkOrCreateAtaBatch, prepareTransaction, signAndExecuteTransaction, } from "@streamflow/common/solana";
|
|
3
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
4
|
+
import { ContractError, ICluster, invariant } from "@streamflow/common";
|
|
5
|
+
import { buildSendThrottler, checkOrCreateAtaBatch, getFilters, pk, prepareTransaction, signAndExecuteTransaction, } from "@streamflow/common/solana";
|
|
6
6
|
import { REWARD_ENTRY_BYTE_OFFSETS, REWARD_POOL_BYTE_OFFSETS, REWARD_POOL_PROGRAM_ID, STAKE_ENTRY_BYTE_OFFSETS, STAKE_POOL_BYTE_OFFSETS, STAKE_POOL_PROGRAM_ID, STREAMFLOW_TREASURY_PUBLIC_KEY, } from "./constants.js";
|
|
7
7
|
import FeeManagerIDL from "./descriptor/idl/fee_manager.json";
|
|
8
8
|
import RewardPoolIDL from "./descriptor/idl/reward_pool.json";
|
|
9
9
|
import StakePoolIDL from "./descriptor/idl/stake_pool.json";
|
|
10
|
-
import { deriveFeeValuePDA, deriveRewardPoolPDA, deriveRewardVaultPDA, deriveStakeEntryPDA, deriveStakeMintPDA, deriveStakePoolPDA, } from "./lib/derive-accounts.js";
|
|
11
|
-
export
|
|
10
|
+
import { deriveConfigPDA, deriveFeeValuePDA, deriveRewardPoolPDA, deriveRewardVaultPDA, deriveStakeEntryPDA, deriveStakeMintPDA, deriveStakePoolPDA, } from "./lib/derive-accounts.js";
|
|
11
|
+
export class SolanaStakingClient {
|
|
12
12
|
connection;
|
|
13
13
|
commitment;
|
|
14
14
|
sendThrottler;
|
|
@@ -73,6 +73,18 @@ export default class SolanaStakingClient {
|
|
|
73
73
|
const { rewardPoolProgram } = this.programs;
|
|
74
74
|
return rewardPoolProgram.account.rewardEntry.all(getFilters(criteria, REWARD_ENTRY_BYTE_OFFSETS));
|
|
75
75
|
}
|
|
76
|
+
async getFee(target) {
|
|
77
|
+
const perTargetFee = await this.getFeeValueIfExists(target);
|
|
78
|
+
if (perTargetFee) {
|
|
79
|
+
return perTargetFee;
|
|
80
|
+
}
|
|
81
|
+
return this.getDefaultFeeValue();
|
|
82
|
+
}
|
|
83
|
+
getDefaultFeeValue() {
|
|
84
|
+
const { feeManagerProgram } = this.programs;
|
|
85
|
+
const feeValueKey = deriveConfigPDA(feeManagerProgram.programId);
|
|
86
|
+
return feeManagerProgram.account.config.fetch(feeValueKey);
|
|
87
|
+
}
|
|
76
88
|
getFeeValueIfExists(target) {
|
|
77
89
|
const { feeManagerProgram } = this.programs;
|
|
78
90
|
const feeValueKey = deriveFeeValuePDA(feeManagerProgram.programId, new PublicKey(target));
|
|
@@ -263,6 +275,39 @@ export default class SolanaStakingClient {
|
|
|
263
275
|
.instruction();
|
|
264
276
|
return { ixs: [instruction] };
|
|
265
277
|
}
|
|
278
|
+
async updateRewardPool(data, extParams) {
|
|
279
|
+
const { ixs } = await this.prepareUpdateRewardPoolInstructions(data, extParams);
|
|
280
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
281
|
+
return {
|
|
282
|
+
ixs,
|
|
283
|
+
txId: signature,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async prepareUpdateRewardPoolInstructions({ rewardPool, rewardAmount, rewardPeriod }, extParams) {
|
|
287
|
+
const { rewardPoolProgram } = this.programs;
|
|
288
|
+
const invoker = extParams.invoker.publicKey;
|
|
289
|
+
invariant(invoker, "Undefined invoker publicKey");
|
|
290
|
+
const instruction = await rewardPoolProgram.methods
|
|
291
|
+
.updatePool(rewardAmount, rewardPeriod)
|
|
292
|
+
.accountsPartial({
|
|
293
|
+
authority: invoker,
|
|
294
|
+
rewardPool,
|
|
295
|
+
})
|
|
296
|
+
.instruction();
|
|
297
|
+
return { ixs: [instruction] };
|
|
298
|
+
}
|
|
299
|
+
decode(programKey, accountName, accInfo) {
|
|
300
|
+
const decodingProgram = this.programs[programKey];
|
|
301
|
+
invariant(decodingProgram, `Decoding program with key ${programKey} is not available`);
|
|
302
|
+
return decodingProgram.coder.accounts.decode(accountName.toString(), accInfo);
|
|
303
|
+
}
|
|
304
|
+
getDiscriminator(programKey, accountName) {
|
|
305
|
+
const decodingProgram = this.programs[programKey];
|
|
306
|
+
invariant(decodingProgram, `Decoding program with key ${programKey} is not available`);
|
|
307
|
+
const accountEntity = decodingProgram.idl.accounts.find((acc) => acc.name === accountName);
|
|
308
|
+
invariant(accountEntity, `Decoding program with key ${programKey} doesn't specify account with name ${accountName.toString()}`);
|
|
309
|
+
return accountEntity.discriminator;
|
|
310
|
+
}
|
|
266
311
|
async execute(ixs, extParams) {
|
|
267
312
|
const { tx, hash, context } = await prepareTransaction(this.connection, ixs, extParams.invoker.publicKey);
|
|
268
313
|
try {
|
|
@@ -284,30 +329,3 @@ export default class SolanaStakingClient {
|
|
|
284
329
|
}
|
|
285
330
|
}
|
|
286
331
|
}
|
|
287
|
-
function pk(address) {
|
|
288
|
-
return typeof address === "string" ? new PublicKey(address) : address;
|
|
289
|
-
}
|
|
290
|
-
const prefix = "Assertion failed";
|
|
291
|
-
function invariant(condition, message) {
|
|
292
|
-
if (condition) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const provided = typeof message === "function" ? message() : message;
|
|
296
|
-
const value = provided ? `${prefix}: ${provided}` : prefix;
|
|
297
|
-
throw new Error(value);
|
|
298
|
-
}
|
|
299
|
-
function getFilters(criteria, byteOffsets) {
|
|
300
|
-
return Object.entries(criteria).reduce((acc, [key, value]) => {
|
|
301
|
-
const criteriaKey = key;
|
|
302
|
-
const effectiveByteOffset = byteOffsets[criteriaKey];
|
|
303
|
-
if (criteria[criteriaKey] && effectiveByteOffset) {
|
|
304
|
-
acc.push({
|
|
305
|
-
memcmp: {
|
|
306
|
-
offset: effectiveByteOffset,
|
|
307
|
-
bytes: value.toString(),
|
|
308
|
-
},
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
return acc;
|
|
312
|
-
}, []);
|
|
313
|
-
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
/// <reference types="bn.js" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
1
|
import { BN } from "@coral-xyz/anchor";
|
|
4
2
|
import { ICluster } from "@streamflow/common";
|
|
5
3
|
import { PublicKey } from "@solana/web3.js";
|
|
6
4
|
export declare const FEE_PRECISION_FACTOR = 10000;
|
|
7
5
|
export declare const FEE_PRECISION_FACTOR_BN: BN;
|
|
8
|
-
export declare const DEFAULT_FEE =
|
|
6
|
+
export declare const DEFAULT_FEE = 19;
|
|
9
7
|
export declare const DEFAULT_FEE_BN: BN;
|
|
10
8
|
export declare const SCALE_PRECISION_FACTOR = 1000000000;
|
|
11
9
|
export declare const SCALE_PRECISION_FACTOR_BN: BN;
|
|
10
|
+
export declare const REWARD_AMOUNT_DECIMALS = 9;
|
|
11
|
+
export declare const REWARD_AMOUNT_PRECISION_FACTOR = 1000000000;
|
|
12
|
+
export declare const REWARD_AMOUNT_PRECISION_FACTOR_BN: BN;
|
|
12
13
|
export declare const U64_MAX = 18446744073709551615n;
|
|
13
14
|
export declare const STAKE_ENTRY_DISCRIMINATOR: number[];
|
|
14
15
|
export declare const STAKE_ENTRY_PREFIX: Buffer;
|
|
@@ -48,3 +49,4 @@ export declare const REWARD_ENTRY_BYTE_OFFSETS: {
|
|
|
48
49
|
};
|
|
49
50
|
export declare const STAKE_POOL_PROGRAM_ID: Record<ICluster, string>;
|
|
50
51
|
export declare const REWARD_POOL_PROGRAM_ID: Record<ICluster, string>;
|
|
52
|
+
export declare const FEE_PROGRAM_ID: Record<ICluster, string>;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { BN } from "@coral-xyz/anchor";
|
|
2
2
|
import { ICluster } from "@streamflow/common";
|
|
3
3
|
import { PublicKey } from "@solana/web3.js";
|
|
4
|
-
export const FEE_PRECISION_FACTOR =
|
|
4
|
+
export const FEE_PRECISION_FACTOR = 10_000;
|
|
5
5
|
export const FEE_PRECISION_FACTOR_BN = new BN(FEE_PRECISION_FACTOR);
|
|
6
|
-
export const DEFAULT_FEE =
|
|
6
|
+
export const DEFAULT_FEE = 19;
|
|
7
7
|
export const DEFAULT_FEE_BN = new BN(DEFAULT_FEE);
|
|
8
|
-
export const SCALE_PRECISION_FACTOR =
|
|
8
|
+
export const SCALE_PRECISION_FACTOR = 1_000_000_000;
|
|
9
9
|
export const SCALE_PRECISION_FACTOR_BN = new BN(SCALE_PRECISION_FACTOR);
|
|
10
|
+
export const REWARD_AMOUNT_DECIMALS = 9;
|
|
11
|
+
export const REWARD_AMOUNT_PRECISION_FACTOR = 1_000_000_000;
|
|
12
|
+
export const REWARD_AMOUNT_PRECISION_FACTOR_BN = new BN(REWARD_AMOUNT_PRECISION_FACTOR);
|
|
10
13
|
export const U64_MAX = 18446744073709551615n;
|
|
11
14
|
export const STAKE_ENTRY_DISCRIMINATOR = [187, 127, 9, 35, 155, 68, 86, 40];
|
|
12
15
|
export const STAKE_ENTRY_PREFIX = Buffer.from("stake-entry", "utf-8");
|
|
@@ -26,7 +29,7 @@ export const STAKE_ENTRY_BYTE_OFFSETS = {
|
|
|
26
29
|
payer: STAKE_ENTRY_OWNER_OFFSET,
|
|
27
30
|
stakePool: STAKE_ENTRY_STAKE_POOL_OFFSET,
|
|
28
31
|
};
|
|
29
|
-
export const STAKE_POOL_MINT_OFFSET = ANCHOR_DISCRIMINATOR_OFFSET +
|
|
32
|
+
export const STAKE_POOL_MINT_OFFSET = ANCHOR_DISCRIMINATOR_OFFSET + 2;
|
|
30
33
|
export const STAKE_POOL_CREATOR_OFFSET = STAKE_POOL_MINT_OFFSET + 32;
|
|
31
34
|
export const STAKE_POOL_BYTE_OFFSETS = {
|
|
32
35
|
mint: STAKE_POOL_MINT_OFFSET,
|
|
@@ -56,3 +59,9 @@ export const REWARD_POOL_PROGRAM_ID = {
|
|
|
56
59
|
[ICluster.Testnet]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
57
60
|
[ICluster.Local]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
58
61
|
};
|
|
62
|
+
export const FEE_PROGRAM_ID = {
|
|
63
|
+
[ICluster.Mainnet]: "FEELzfBhsWXTNJX53zZcDVfRNoFYZQ6cZA3jLiGVL16V",
|
|
64
|
+
[ICluster.Devnet]: "FEELzfBhsWXTNJX53zZcDVfRNoFYZQ6cZA3jLiGVL16V",
|
|
65
|
+
[ICluster.Testnet]: "FEELzfBhsWXTNJX53zZcDVfRNoFYZQ6cZA3jLiGVL16V",
|
|
66
|
+
[ICluster.Local]: "FEELzfBhsWXTNJX53zZcDVfRNoFYZQ6cZA3jLiGVL16V",
|
|
67
|
+
};
|
|
@@ -338,8 +338,8 @@
|
|
|
338
338
|
"program": {
|
|
339
339
|
"kind": "const",
|
|
340
340
|
"value": [
|
|
341
|
-
140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218,
|
|
342
|
-
|
|
341
|
+
140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16,
|
|
342
|
+
132, 4, 142, 123, 216, 219, 233, 248, 89
|
|
343
343
|
]
|
|
344
344
|
}
|
|
345
345
|
}
|
|
@@ -778,9 +778,7 @@
|
|
|
778
778
|
},
|
|
779
779
|
{
|
|
780
780
|
"name": "effective_amount",
|
|
781
|
-
"docs": [
|
|
782
|
-
"Effective Amount staked, does not equal to deposited amount, accounts for Stake Weight"
|
|
783
|
-
],
|
|
781
|
+
"docs": ["Effective Amount staked, does not equal to deposited amount, accounts for Stake Weight"],
|
|
784
782
|
"type": "u128"
|
|
785
783
|
},
|
|
786
784
|
{
|