@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.
- package/LICENSE +674 -0
- package/README.md +216 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/solana/client.js +315 -0
- package/dist/cjs/solana/constants.js +61 -0
- package/dist/cjs/solana/descriptor/fee_manager.js +2 -0
- package/dist/cjs/solana/descriptor/idl/fee_manager.json +284 -0
- package/dist/cjs/solana/descriptor/idl/reward_pool.json +905 -0
- package/dist/cjs/solana/descriptor/idl/stake_pool.json +734 -0
- package/dist/cjs/solana/descriptor/reward_pool.js +2 -0
- package/dist/cjs/solana/descriptor/stake_pool.js +2 -0
- package/dist/cjs/solana/lib/derive-accounts.js +46 -0
- package/dist/cjs/solana/lib/rewards.js +134 -0
- package/dist/cjs/solana/types.js +2 -0
- package/dist/cjs/solana/utils.js +61 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/solana/client.d.ts +77 -0
- package/dist/esm/solana/client.js +313 -0
- package/dist/esm/solana/constants.d.ts +50 -0
- package/dist/esm/solana/constants.js +58 -0
- package/dist/esm/solana/descriptor/fee_manager.d.ts +290 -0
- package/dist/esm/solana/descriptor/fee_manager.js +1 -0
- package/dist/esm/solana/descriptor/idl/fee_manager.json +284 -0
- package/dist/esm/solana/descriptor/idl/reward_pool.json +905 -0
- package/dist/esm/solana/descriptor/idl/stake_pool.json +734 -0
- package/dist/esm/solana/descriptor/reward_pool.d.ts +939 -0
- package/dist/esm/solana/descriptor/reward_pool.js +1 -0
- package/dist/esm/solana/descriptor/stake_pool.d.ts +538 -0
- package/dist/esm/solana/descriptor/stake_pool.js +1 -0
- package/dist/esm/solana/lib/derive-accounts.d.ts +10 -0
- package/dist/esm/solana/lib/derive-accounts.js +31 -0
- package/dist/esm/solana/lib/rewards.d.ts +24 -0
- package/dist/esm/solana/lib/rewards.js +136 -0
- package/dist/esm/solana/types.d.ts +66 -0
- package/dist/esm/solana/types.js +1 -0
- package/dist/esm/solana/utils.d.ts +11 -0
- package/dist/esm/solana/utils.js +50 -0
- package/package.json +48 -0
|
@@ -0,0 +1,46 @@
|
|
|
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.deriveFeeValuePDA = exports.deriveConfigPDA = exports.deriveRewardEntryPDA = exports.deriveRewardVaultPDA = exports.deriveRewardPoolPDA = exports.deriveStakeEntryPDA = exports.deriveStakeMintPDA = exports.deriveStakeVaultPDA = exports.deriveStakePoolPDA = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
// eslint-disable-next-line no-restricted-imports
|
|
9
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
10
|
+
const constants_js_1 = require("../constants.js");
|
|
11
|
+
const deriveStakePoolPDA = (programId, mint, authority, nonce) => {
|
|
12
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.STAKE_POOL_PREFIX, mint.toBuffer(), authority.toBuffer(), new bn_js_1.default(nonce).toArrayLike(Buffer, "le", 1)], programId)[0];
|
|
13
|
+
};
|
|
14
|
+
exports.deriveStakePoolPDA = deriveStakePoolPDA;
|
|
15
|
+
const deriveStakeVaultPDA = (programId, stakePool) => {
|
|
16
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.STAKE_VAULT_PREFIX, stakePool.toBuffer()], programId)[0];
|
|
17
|
+
};
|
|
18
|
+
exports.deriveStakeVaultPDA = deriveStakeVaultPDA;
|
|
19
|
+
const deriveStakeMintPDA = (programId, stakePool) => {
|
|
20
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.STAKE_MINT_PREFIX, stakePool.toBuffer()], programId)[0];
|
|
21
|
+
};
|
|
22
|
+
exports.deriveStakeMintPDA = deriveStakeMintPDA;
|
|
23
|
+
const deriveStakeEntryPDA = (programId, stakePool, authority, nonce) => {
|
|
24
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.STAKE_ENTRY_PREFIX, stakePool.toBuffer(), authority.toBuffer(), new bn_js_1.default(nonce).toArrayLike(Buffer, "le", 4)], programId)[0];
|
|
25
|
+
};
|
|
26
|
+
exports.deriveStakeEntryPDA = deriveStakeEntryPDA;
|
|
27
|
+
const deriveRewardPoolPDA = (programId, stakePool, mint, nonce) => {
|
|
28
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.REWARD_POOL_PREFIX, stakePool.toBuffer(), mint.toBuffer(), new bn_js_1.default(nonce).toArrayLike(Buffer, "le", 1)], programId)[0];
|
|
29
|
+
};
|
|
30
|
+
exports.deriveRewardPoolPDA = deriveRewardPoolPDA;
|
|
31
|
+
const deriveRewardVaultPDA = (programId, rewardPool) => {
|
|
32
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.REWARD_VAULT_PREFIX, rewardPool.toBuffer()], programId)[0];
|
|
33
|
+
};
|
|
34
|
+
exports.deriveRewardVaultPDA = deriveRewardVaultPDA;
|
|
35
|
+
const deriveRewardEntryPDA = (programId, rewardPool, stakeEntry) => {
|
|
36
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.REWARD_ENTRY_PREFIX, rewardPool.toBuffer(), stakeEntry.toBuffer()], programId)[0];
|
|
37
|
+
};
|
|
38
|
+
exports.deriveRewardEntryPDA = deriveRewardEntryPDA;
|
|
39
|
+
const deriveConfigPDA = (programId) => {
|
|
40
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.CONFIG_PREFIX], programId)[0];
|
|
41
|
+
};
|
|
42
|
+
exports.deriveConfigPDA = deriveConfigPDA;
|
|
43
|
+
const deriveFeeValuePDA = (programId, target) => {
|
|
44
|
+
return web3_js_1.PublicKey.findProgramAddressSync([constants_js_1.FEE_VALUE_PREFIX, target.toBuffer()], programId)[0];
|
|
45
|
+
};
|
|
46
|
+
exports.deriveFeeValuePDA = deriveFeeValuePDA;
|
|
@@ -0,0 +1,134 @@
|
|
|
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.calcRewards = exports.RewardEntryAccumulator = exports.REWARD_AMOUNT_PRECISION_FACTOR = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
// eslint-disable-next-line no-restricted-imports
|
|
9
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
10
|
+
const constants_js_1 = require("../constants.js");
|
|
11
|
+
exports.REWARD_AMOUNT_PRECISION_FACTOR = new bn_js_1.default("1000000000");
|
|
12
|
+
class RewardEntryAccumulator {
|
|
13
|
+
constructor(delegate) {
|
|
14
|
+
this.delegate = delegate;
|
|
15
|
+
this.lastAccountedTs = delegate.lastAccountedTs;
|
|
16
|
+
this.claimedAmount = delegate.claimedAmount;
|
|
17
|
+
this.accountedAmount = delegate.accountedAmount;
|
|
18
|
+
this.rewardPool = delegate.rewardPool;
|
|
19
|
+
this.stakeEntry = delegate.stakeEntry;
|
|
20
|
+
this.createdTs = delegate.createdTs;
|
|
21
|
+
this.buffer = delegate.buffer;
|
|
22
|
+
this.lastRewardAmount = delegate.lastRewardAmount;
|
|
23
|
+
this.lastRewardPeriod = delegate.lastRewardPeriod;
|
|
24
|
+
}
|
|
25
|
+
// Calculate accountable amount by calculating how many seconds have passed since last claim/stake time
|
|
26
|
+
getAccountableAmount(stakedTs, accountableTs, effectiveStakedAmount, rewardAmount, rewardPeriod) {
|
|
27
|
+
const lastAccountedTs = this.lastAccountedTs.gt(new bn_js_1.default(0)) ? this.lastAccountedTs : stakedTs;
|
|
28
|
+
const secondsPassed = accountableTs.sub(lastAccountedTs);
|
|
29
|
+
if (secondsPassed.lt(rewardPeriod)) {
|
|
30
|
+
return new bn_js_1.default(0);
|
|
31
|
+
}
|
|
32
|
+
const periodsPassed = secondsPassed.div(rewardPeriod);
|
|
33
|
+
const claimablePerEffectiveStake = periodsPassed.mul(rewardAmount);
|
|
34
|
+
const accountableAmount = claimablePerEffectiveStake.mul(effectiveStakedAmount).div(constants_js_1.SCALE_PRECISION_FACTOR_BN);
|
|
35
|
+
return accountableAmount;
|
|
36
|
+
}
|
|
37
|
+
// Calculates claimable amount from accountable amount.
|
|
38
|
+
getClaimableAmount() {
|
|
39
|
+
const claimedAmount = this.claimedAmount.mul(exports.REWARD_AMOUNT_PRECISION_FACTOR);
|
|
40
|
+
const nonClaimedAmount = this.accountedAmount.sub(claimedAmount);
|
|
41
|
+
const claimableAmount = nonClaimedAmount.div(exports.REWARD_AMOUNT_PRECISION_FACTOR);
|
|
42
|
+
return claimableAmount;
|
|
43
|
+
}
|
|
44
|
+
// Get the time of the last unlock
|
|
45
|
+
getLastAccountedTs(stakedTs, claimableTs, rewardPeriod) {
|
|
46
|
+
const lastAccountedTs = this.lastAccountedTs.gtn(0) ? this.lastAccountedTs : stakedTs;
|
|
47
|
+
const totalSecondsPassed = claimableTs.sub(lastAccountedTs);
|
|
48
|
+
const periodsPassed = totalSecondsPassed.div(rewardPeriod);
|
|
49
|
+
const periodsToSeconds = periodsPassed.mul(rewardPeriod);
|
|
50
|
+
const currClaimTs = lastAccountedTs.add(periodsToSeconds);
|
|
51
|
+
return currClaimTs;
|
|
52
|
+
}
|
|
53
|
+
// Adds accounted amount
|
|
54
|
+
addAccountedAmount(accountedAmount) {
|
|
55
|
+
this.accountedAmount = this.accountedAmount.add(accountedAmount);
|
|
56
|
+
}
|
|
57
|
+
// Adds claimed amount
|
|
58
|
+
addClaimedAmount(claimedAmount) {
|
|
59
|
+
this.claimedAmount = this.claimedAmount.add(claimedAmount);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.RewardEntryAccumulator = RewardEntryAccumulator;
|
|
63
|
+
const createDefaultRewardEntry = (stakeEntry, rewardPool) => {
|
|
64
|
+
return {
|
|
65
|
+
stakeEntry: new web3_js_1.PublicKey(stakeEntry.publicKey),
|
|
66
|
+
rewardPool: new web3_js_1.PublicKey(rewardPool.publicKey),
|
|
67
|
+
createdTs: stakeEntry.account.createdTs,
|
|
68
|
+
lastAccountedTs: new bn_js_1.default(0),
|
|
69
|
+
lastRewardAmount: new bn_js_1.default(0),
|
|
70
|
+
lastRewardPeriod: new bn_js_1.default(0),
|
|
71
|
+
accountedAmount: new bn_js_1.default(0),
|
|
72
|
+
claimedAmount: new bn_js_1.default(0),
|
|
73
|
+
buffer: [],
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const calcRewards = (rewardEntryAccount, stakeEntryAccount, rewardPoolAccount) => {
|
|
77
|
+
const rewardEntry = rewardEntryAccount?.account ?? createDefaultRewardEntry(stakeEntryAccount, rewardPoolAccount);
|
|
78
|
+
const stakeEntry = stakeEntryAccount.account;
|
|
79
|
+
const rewardPool = rewardPoolAccount.account;
|
|
80
|
+
const rewardEntryAccumulator = new RewardEntryAccumulator(rewardEntry);
|
|
81
|
+
if (rewardEntryAccumulator.createdTs.lt(stakeEntry.createdTs)) {
|
|
82
|
+
throw new Error("InvalidRewardEntry");
|
|
83
|
+
}
|
|
84
|
+
const currTs = Math.floor(Date.now() / 1000);
|
|
85
|
+
const stakedTs = rewardPool.createdTs ? bn_js_1.default.max(stakeEntry.createdTs, rewardPool.createdTs) : stakeEntry.createdTs;
|
|
86
|
+
const claimableTs = stakeEntry.closedTs.gtn(0) ? stakeEntry.closedTs : new bn_js_1.default(currTs);
|
|
87
|
+
const amountUpdated = !rewardPool.rewardAmount.eq(rewardPool.lastRewardAmount) &&
|
|
88
|
+
rewardPool.lastAmountUpdateTs.gt(stakeEntry.createdTs) &&
|
|
89
|
+
rewardPool.lastAmountUpdateTs.gt(stakeEntry.closedTs);
|
|
90
|
+
const periodUpdated = !rewardPool.rewardPeriod.eq(rewardPool.lastRewardPeriod) &&
|
|
91
|
+
rewardPool.lastPeriodUpdateTs.gt(stakeEntry.createdTs) &&
|
|
92
|
+
rewardPool.lastPeriodUpdateTs.gt(stakeEntry.closedTs);
|
|
93
|
+
if (amountUpdated || periodUpdated) {
|
|
94
|
+
let firstUpdateTs, secondUpdateTs, rewardAmount, rewardPeriod;
|
|
95
|
+
if (amountUpdated && periodUpdated) {
|
|
96
|
+
if (rewardPool.lastAmountUpdateTs.lt(rewardPool.lastPeriodUpdateTs)) {
|
|
97
|
+
firstUpdateTs = rewardPool.lastAmountUpdateTs;
|
|
98
|
+
secondUpdateTs = rewardPool.lastPeriodUpdateTs;
|
|
99
|
+
rewardAmount = rewardPool.rewardAmount;
|
|
100
|
+
rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
firstUpdateTs = rewardPool.lastPeriodUpdateTs;
|
|
104
|
+
secondUpdateTs = rewardPool.lastAmountUpdateTs;
|
|
105
|
+
rewardAmount = rewardEntryAccumulator.lastRewardAmount;
|
|
106
|
+
rewardPeriod = rewardPool.rewardPeriod;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (amountUpdated) {
|
|
110
|
+
firstUpdateTs = new bn_js_1.default(0);
|
|
111
|
+
secondUpdateTs = rewardPool.lastAmountUpdateTs;
|
|
112
|
+
rewardAmount = rewardEntryAccumulator.lastRewardAmount;
|
|
113
|
+
rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
firstUpdateTs = new bn_js_1.default(0);
|
|
117
|
+
secondUpdateTs = rewardPool.lastPeriodUpdateTs;
|
|
118
|
+
rewardAmount = rewardEntryAccumulator.lastRewardAmount;
|
|
119
|
+
rewardPeriod = rewardEntryAccumulator.lastRewardPeriod;
|
|
120
|
+
}
|
|
121
|
+
if (firstUpdateTs.gtn(0)) {
|
|
122
|
+
const firstAccountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, firstUpdateTs, stakeEntry.effectiveAmount, rewardEntryAccumulator.lastRewardAmount, rewardEntryAccumulator.lastRewardPeriod);
|
|
123
|
+
rewardEntryAccumulator.addAccountedAmount(firstAccountableAmount);
|
|
124
|
+
rewardEntryAccumulator.lastAccountedTs = rewardEntryAccumulator.getLastAccountedTs(stakedTs, firstUpdateTs, rewardPool.lastRewardPeriod);
|
|
125
|
+
}
|
|
126
|
+
const secondAccountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, secondUpdateTs, stakeEntry.effectiveAmount, rewardAmount, rewardPeriod);
|
|
127
|
+
rewardEntryAccumulator.addAccountedAmount(secondAccountableAmount);
|
|
128
|
+
rewardEntryAccumulator.lastAccountedTs = rewardEntryAccumulator.getLastAccountedTs(stakedTs, secondUpdateTs, rewardPeriod);
|
|
129
|
+
}
|
|
130
|
+
const accountableAmount = rewardEntryAccumulator.getAccountableAmount(stakedTs, claimableTs, stakeEntry.effectiveAmount, rewardPool.rewardAmount, rewardPool.rewardPeriod);
|
|
131
|
+
rewardEntryAccumulator.addAccountedAmount(accountableAmount);
|
|
132
|
+
return rewardEntryAccumulator.getClaimableAmount();
|
|
133
|
+
};
|
|
134
|
+
exports.calcRewards = calcRewards;
|
|
@@ -0,0 +1,61 @@
|
|
|
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.calculateAmountWithTransferFees = exports.divCeilN = exports.calculateDecimalsShift = exports.calculateFeeAmount = exports.calculateStakeWeight = void 0;
|
|
7
|
+
// eslint-disable-next-line no-restricted-imports
|
|
8
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
9
|
+
const constants_js_1 = require("./constants.js");
|
|
10
|
+
const calculateStakeWeight = (minDuration, maxDuration, maxWeight, duration) => {
|
|
11
|
+
const durationSpan = maxDuration.sub(minDuration);
|
|
12
|
+
if (durationSpan.eq(new bn_js_1.default(0))) {
|
|
13
|
+
return constants_js_1.SCALE_PRECISION_FACTOR_BN;
|
|
14
|
+
}
|
|
15
|
+
const durationExceedingMin = duration.sub(minDuration);
|
|
16
|
+
const normalizedWeight = durationExceedingMin.mul(constants_js_1.SCALE_PRECISION_FACTOR_BN).div(durationSpan);
|
|
17
|
+
const weightDiff = maxWeight.sub(constants_js_1.SCALE_PRECISION_FACTOR_BN);
|
|
18
|
+
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);
|
|
19
|
+
};
|
|
20
|
+
exports.calculateStakeWeight = calculateStakeWeight;
|
|
21
|
+
const calculateFeeAmount = (amount, fee = constants_js_1.DEFAULT_FEE_BN) => {
|
|
22
|
+
if (fee.eq(constants_js_1.FEE_PRECISION_FACTOR_BN)) {
|
|
23
|
+
return amount;
|
|
24
|
+
}
|
|
25
|
+
return amount.mul(fee).div(constants_js_1.FEE_PRECISION_FACTOR_BN);
|
|
26
|
+
};
|
|
27
|
+
exports.calculateFeeAmount = calculateFeeAmount;
|
|
28
|
+
const calculateDecimalsShift = (maxWeight, maxShift = 999) => {
|
|
29
|
+
if (maxShift == 0) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
let decimalsShift = 0;
|
|
33
|
+
while ((maxWeight * constants_js_1.U64_MAX) / BigInt(constants_js_1.SCALE_PRECISION_FACTOR) / BigInt(10 ** decimalsShift) > constants_js_1.U64_MAX) {
|
|
34
|
+
decimalsShift += 1;
|
|
35
|
+
if (decimalsShift == maxShift) {
|
|
36
|
+
return maxShift;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return decimalsShift;
|
|
40
|
+
};
|
|
41
|
+
exports.calculateDecimalsShift = calculateDecimalsShift;
|
|
42
|
+
const divCeilN = (n, d) => n / d + (n % d ? BigInt(1) : BigInt(0));
|
|
43
|
+
exports.divCeilN = divCeilN;
|
|
44
|
+
async function calculateAmountWithTransferFees(connection, transferFeeConfig, transferAmount) {
|
|
45
|
+
const epoch = await connection.getEpochInfo();
|
|
46
|
+
const transferFee = epoch.epoch >= transferFeeConfig.newerTransferFee.epoch
|
|
47
|
+
? transferFeeConfig.newerTransferFee
|
|
48
|
+
: transferFeeConfig.olderTransferFee;
|
|
49
|
+
const transferFeeBasisPoints = BigInt(transferFee.transferFeeBasisPoints);
|
|
50
|
+
let feeCharged = BigInt(0);
|
|
51
|
+
if (transferFeeBasisPoints !== BigInt(0)) {
|
|
52
|
+
const numerator = transferAmount * 10000n;
|
|
53
|
+
const denominator = 10000n - transferFeeBasisPoints;
|
|
54
|
+
const rawPreFeeAmount = (0, exports.divCeilN)(numerator, denominator);
|
|
55
|
+
const fee = rawPreFeeAmount - transferAmount;
|
|
56
|
+
transferAmount = rawPreFeeAmount;
|
|
57
|
+
feeCharged = fee > transferFee.maximumFee ? transferFee.maximumFee : fee;
|
|
58
|
+
}
|
|
59
|
+
return { transferAmount, feeCharged };
|
|
60
|
+
}
|
|
61
|
+
exports.calculateAmountWithTransferFees = calculateAmountWithTransferFees;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as SolanaStakingClient } from "./solana/client.js";
|
|
2
|
+
export * from "./solana/utils.js";
|
|
3
|
+
export * from "./solana/types.js";
|
|
4
|
+
export * from "./solana/lib/derive-accounts.js";
|
|
5
|
+
export * from "./solana/lib/rewards.js";
|
|
6
|
+
export * as constants from "./solana/constants.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as SolanaStakingClient } from "./solana/client.js";
|
|
2
|
+
export * from "./solana/utils.js";
|
|
3
|
+
export * from "./solana/types.js";
|
|
4
|
+
export * from "./solana/lib/derive-accounts.js";
|
|
5
|
+
export * from "./solana/lib/rewards.js";
|
|
6
|
+
export * as constants from "./solana/constants.js";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Program, ProgramAccount } from "@coral-xyz/anchor";
|
|
2
|
+
import { Commitment, Connection, ConnectionConfig, PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
3
|
+
import { ICluster, ITransactionResult } from "@streamflow/common";
|
|
4
|
+
import PQueue from "p-queue";
|
|
5
|
+
import { REWARD_ENTRY_BYTE_OFFSETS, STAKE_ENTRY_BYTE_OFFSETS, STAKE_POOL_BYTE_OFFSETS } from "./constants.js";
|
|
6
|
+
import { FeeManager as FeeManagerProgramType } from "./descriptor/fee_manager.js";
|
|
7
|
+
import { RewardPool as RewardPoolProgramType } from "./descriptor/reward_pool.js";
|
|
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";
|
|
10
|
+
interface Programs {
|
|
11
|
+
stakePoolProgram: Program<StakePoolProgramType>;
|
|
12
|
+
rewardPoolProgram: Program<RewardPoolProgramType>;
|
|
13
|
+
feeManagerProgram: Program<FeeManagerProgramType>;
|
|
14
|
+
}
|
|
15
|
+
type CreationResult = ITransactionResult & {
|
|
16
|
+
metadataId: PublicKey;
|
|
17
|
+
};
|
|
18
|
+
interface IInitOptions {
|
|
19
|
+
clusterUrl: string;
|
|
20
|
+
cluster?: ICluster;
|
|
21
|
+
commitment?: Commitment | ConnectionConfig;
|
|
22
|
+
programIds?: {
|
|
23
|
+
stakePool?: string;
|
|
24
|
+
rewardPool?: string;
|
|
25
|
+
feeManager?: string;
|
|
26
|
+
};
|
|
27
|
+
sendRate?: number;
|
|
28
|
+
sendThrottler?: PQueue;
|
|
29
|
+
}
|
|
30
|
+
export default class SolanaStakingClient {
|
|
31
|
+
connection: Connection;
|
|
32
|
+
private commitment;
|
|
33
|
+
private sendThrottler;
|
|
34
|
+
private programs;
|
|
35
|
+
constructor({ clusterUrl, cluster, commitment, programIds, sendRate, sendThrottler, }: IInitOptions);
|
|
36
|
+
getCurrentProgramId(programKey: keyof Programs): PublicKey;
|
|
37
|
+
getCommitment(): Commitment | undefined;
|
|
38
|
+
getStakePool(id: string | PublicKey): Promise<StakePool>;
|
|
39
|
+
searchStakePools(criteria?: Partial<Pick<StakePool, keyof typeof STAKE_POOL_BYTE_OFFSETS>>): Promise<ProgramAccount<StakePool>[]>;
|
|
40
|
+
getStakeEntry(id: string | PublicKey): Promise<StakeEntry | null>;
|
|
41
|
+
searchStakeEntries(criteria?: Partial<Pick<StakeEntry, keyof typeof STAKE_ENTRY_BYTE_OFFSETS>>): Promise<ProgramAccount<StakeEntry>[]>;
|
|
42
|
+
searchRewardPools(criteria?: Partial<Pick<RewardPool, "stakePool" | "mint">>): Promise<ProgramAccount<RewardPool>[]>;
|
|
43
|
+
searchRewardEntries(criteria: Partial<Pick<RewardEntry, keyof typeof REWARD_ENTRY_BYTE_OFFSETS>>): Promise<ProgramAccount<RewardEntry>[]>;
|
|
44
|
+
getFeeValueIfExists(target: string | PublicKey): Promise<FeeValue | null>;
|
|
45
|
+
createStakePool(data: CreateStakePoolArgs, extParams: IInteractSolanaExt): Promise<CreationResult>;
|
|
46
|
+
prepareCreateStakePoolInstructions({ maxWeight, maxDuration, minDuration, mint, permissionless, nonce, tokenProgramId, }: CreateStakePoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
47
|
+
ixs: TransactionInstruction[];
|
|
48
|
+
publicKey: PublicKey;
|
|
49
|
+
}>;
|
|
50
|
+
stake(data: StakeArgs, extParams: IInteractSolanaExt): Promise<ITransactionResult>;
|
|
51
|
+
prepareStakeInstructions({ nonce, amount, duration, stakePool, stakePoolMint, tokenProgramId }: StakeArgs, extParams: IInteractSolanaExt): Promise<{
|
|
52
|
+
ixs: TransactionInstruction[];
|
|
53
|
+
}>;
|
|
54
|
+
unstake(data: UnstakeArgs, extParams: IInteractSolanaExt): Promise<ITransactionResult>;
|
|
55
|
+
prepareUnstakeInstructions({ stakePool, stakePoolMint, nonce, tokenProgramId }: UnstakeArgs, extParams: IInteractSolanaExt): Promise<{
|
|
56
|
+
ixs: TransactionInstruction[];
|
|
57
|
+
}>;
|
|
58
|
+
createRewardPool(data: CreateRewardPoolArgs, extParams: IInteractSolanaExt): Promise<CreationResult>;
|
|
59
|
+
prepareCreateRewardPoolInstructions({ nonce, rewardAmount, rewardPeriod, rewardMint, permissionless, stakePool, tokenProgramId, }: CreateRewardPoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
60
|
+
publicKey: PublicKey;
|
|
61
|
+
ixs: TransactionInstruction[];
|
|
62
|
+
}>;
|
|
63
|
+
claimRewards(data: ClaimRewardPoolArgs, extParams: IInteractSolanaExt): Promise<ITransactionResult>;
|
|
64
|
+
prepareClaimRewardsInstructions({ rewardPoolNonce, depositNonce, stakePool, tokenProgramId, rewardMint }: ClaimRewardPoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
65
|
+
ixs: TransactionInstruction[];
|
|
66
|
+
}>;
|
|
67
|
+
fundPool(data: FundPoolArgs, extParams: IInteractSolanaExt): Promise<ITransactionResult>;
|
|
68
|
+
prepareFundPoolInstructions({ amount, tokenProgramId, rewardMint, stakePool, feeValue, nonce }: FundPoolArgs, extParams: IInteractSolanaExt): Promise<{
|
|
69
|
+
ixs: TransactionInstruction[];
|
|
70
|
+
}>;
|
|
71
|
+
createRewardEntry(data: CreateRewardEntryArgs, extParams: IInteractSolanaExt): Promise<ITransactionResult>;
|
|
72
|
+
prepareCreateRewardEntryInstructions({ stakePoolMint, stakePool, rewardPoolNonce, depositNonce }: CreateRewardEntryArgs, extParams: IInteractSolanaExt): Promise<{
|
|
73
|
+
ixs: TransactionInstruction[];
|
|
74
|
+
}>;
|
|
75
|
+
private execute;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { Program, parseIdlErrors, translateError, } from "@coral-xyz/anchor";
|
|
2
|
+
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
|
|
3
|
+
import { Connection, PublicKey, } from "@solana/web3.js";
|
|
4
|
+
import { ContractError, ICluster } from "@streamflow/common";
|
|
5
|
+
import { buildSendThrottler, checkOrCreateAtaBatch, prepareTransaction, signAndExecuteTransaction, } from "@streamflow/common/solana";
|
|
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
|
+
import FeeManagerIDL from "./descriptor/idl/fee_manager.json";
|
|
8
|
+
import RewardPoolIDL from "./descriptor/idl/reward_pool.json";
|
|
9
|
+
import StakePoolIDL from "./descriptor/idl/stake_pool.json";
|
|
10
|
+
import { deriveFeeValuePDA, deriveRewardPoolPDA, deriveRewardVaultPDA, deriveStakeEntryPDA, deriveStakeMintPDA, deriveStakePoolPDA, } from "./lib/derive-accounts.js";
|
|
11
|
+
export default class SolanaStakingClient {
|
|
12
|
+
connection;
|
|
13
|
+
commitment;
|
|
14
|
+
sendThrottler;
|
|
15
|
+
programs;
|
|
16
|
+
constructor({ clusterUrl, cluster = ICluster.Mainnet, commitment = "confirmed", programIds, sendRate = 1, sendThrottler, }) {
|
|
17
|
+
this.commitment = commitment;
|
|
18
|
+
this.connection = new Connection(clusterUrl, this.commitment);
|
|
19
|
+
this.sendThrottler = sendThrottler ?? buildSendThrottler(sendRate);
|
|
20
|
+
const stakePoolIdl = {
|
|
21
|
+
...StakePoolIDL,
|
|
22
|
+
address: programIds?.stakePool ?? STAKE_POOL_PROGRAM_ID[cluster] ?? StakePoolIDL.address,
|
|
23
|
+
};
|
|
24
|
+
const rewardPoolIdl = {
|
|
25
|
+
...RewardPoolIDL,
|
|
26
|
+
address: programIds?.rewardPool ?? REWARD_POOL_PROGRAM_ID[cluster] ?? RewardPoolIDL.address,
|
|
27
|
+
};
|
|
28
|
+
const feeManagerIdl = {
|
|
29
|
+
...FeeManagerIDL,
|
|
30
|
+
address: programIds?.feeManager ?? FeeManagerIDL.address,
|
|
31
|
+
};
|
|
32
|
+
this.programs = {
|
|
33
|
+
stakePoolProgram: new Program(stakePoolIdl, {
|
|
34
|
+
connection: this.connection,
|
|
35
|
+
}),
|
|
36
|
+
rewardPoolProgram: new Program(rewardPoolIdl, {
|
|
37
|
+
connection: this.connection,
|
|
38
|
+
}),
|
|
39
|
+
feeManagerProgram: new Program(feeManagerIdl, {
|
|
40
|
+
connection: this.connection,
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
getCurrentProgramId(programKey) {
|
|
45
|
+
const program = this.programs[programKey];
|
|
46
|
+
invariant(program, `Program ${programKey} is not found`);
|
|
47
|
+
return program.programId;
|
|
48
|
+
}
|
|
49
|
+
getCommitment() {
|
|
50
|
+
return typeof this.commitment == "string" ? this.commitment : this.commitment.commitment;
|
|
51
|
+
}
|
|
52
|
+
async getStakePool(id) {
|
|
53
|
+
const { stakePoolProgram } = this.programs;
|
|
54
|
+
return stakePoolProgram.account.stakePool.fetch(id);
|
|
55
|
+
}
|
|
56
|
+
async searchStakePools(criteria = {}) {
|
|
57
|
+
const { stakePoolProgram } = this.programs;
|
|
58
|
+
return stakePoolProgram.account.stakePool.all(getFilters(criteria, STAKE_POOL_BYTE_OFFSETS));
|
|
59
|
+
}
|
|
60
|
+
async getStakeEntry(id) {
|
|
61
|
+
const { stakePoolProgram } = this.programs;
|
|
62
|
+
return stakePoolProgram.account.stakeEntry.fetch(id);
|
|
63
|
+
}
|
|
64
|
+
async searchStakeEntries(criteria = {}) {
|
|
65
|
+
const { stakePoolProgram } = this.programs;
|
|
66
|
+
return stakePoolProgram.account.stakeEntry.all(getFilters(criteria, STAKE_ENTRY_BYTE_OFFSETS));
|
|
67
|
+
}
|
|
68
|
+
async searchRewardPools(criteria = {}) {
|
|
69
|
+
const { rewardPoolProgram } = this.programs;
|
|
70
|
+
return rewardPoolProgram.account.rewardPool.all(getFilters(criteria, REWARD_POOL_BYTE_OFFSETS));
|
|
71
|
+
}
|
|
72
|
+
async searchRewardEntries(criteria) {
|
|
73
|
+
const { rewardPoolProgram } = this.programs;
|
|
74
|
+
return rewardPoolProgram.account.rewardEntry.all(getFilters(criteria, REWARD_ENTRY_BYTE_OFFSETS));
|
|
75
|
+
}
|
|
76
|
+
getFeeValueIfExists(target) {
|
|
77
|
+
const { feeManagerProgram } = this.programs;
|
|
78
|
+
const feeValueKey = deriveFeeValuePDA(feeManagerProgram.programId, new PublicKey(target));
|
|
79
|
+
return feeManagerProgram.account.feeValue.fetchNullable(feeValueKey);
|
|
80
|
+
}
|
|
81
|
+
async createStakePool(data, extParams) {
|
|
82
|
+
const { ixs, publicKey } = await this.prepareCreateStakePoolInstructions(data, extParams);
|
|
83
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
84
|
+
return {
|
|
85
|
+
ixs,
|
|
86
|
+
txId: signature,
|
|
87
|
+
metadataId: publicKey,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async prepareCreateStakePoolInstructions({ maxWeight, maxDuration, minDuration, mint, permissionless = false, nonce, tokenProgramId = TOKEN_PROGRAM_ID, }, extParams) {
|
|
91
|
+
const { stakePoolProgram } = this.programs;
|
|
92
|
+
const creator = extParams.invoker.publicKey;
|
|
93
|
+
invariant(creator, "Undefined invoker publicKey");
|
|
94
|
+
const createInstruction = await stakePoolProgram.methods
|
|
95
|
+
.createPool(nonce, maxWeight, minDuration, maxDuration, permissionless)
|
|
96
|
+
.accounts({
|
|
97
|
+
creator,
|
|
98
|
+
mint,
|
|
99
|
+
tokenProgram: tokenProgramId,
|
|
100
|
+
})
|
|
101
|
+
.instruction();
|
|
102
|
+
const stakePoolPDA = deriveStakePoolPDA(stakePoolProgram.programId, pk(mint), creator, nonce);
|
|
103
|
+
return { ixs: [createInstruction], publicKey: stakePoolPDA };
|
|
104
|
+
}
|
|
105
|
+
async stake(data, extParams) {
|
|
106
|
+
const { ixs } = await this.prepareStakeInstructions(data, extParams);
|
|
107
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
108
|
+
return {
|
|
109
|
+
ixs,
|
|
110
|
+
txId: signature,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async prepareStakeInstructions({ nonce, amount, duration, stakePool, stakePoolMint, tokenProgramId = TOKEN_PROGRAM_ID }, extParams) {
|
|
114
|
+
const { stakePoolProgram } = this.programs;
|
|
115
|
+
const staker = extParams.invoker.publicKey;
|
|
116
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
117
|
+
const mint = deriveStakeMintPDA(stakePoolProgram.programId, pk(stakePool));
|
|
118
|
+
const stakeMintAccountKey = getAssociatedTokenAddressSync(mint, staker, false, pk(tokenProgramId));
|
|
119
|
+
const poolMintAccountKey = getAssociatedTokenAddressSync(pk(stakePoolMint), staker, false, pk(tokenProgramId));
|
|
120
|
+
const instruction = await stakePoolProgram.methods
|
|
121
|
+
.stake(nonce, amount, duration)
|
|
122
|
+
.accounts({
|
|
123
|
+
stakePool: stakePool,
|
|
124
|
+
tokenProgram: tokenProgramId,
|
|
125
|
+
from: poolMintAccountKey,
|
|
126
|
+
to: stakeMintAccountKey,
|
|
127
|
+
authority: staker,
|
|
128
|
+
payer: staker,
|
|
129
|
+
})
|
|
130
|
+
.instruction();
|
|
131
|
+
return { ixs: [instruction] };
|
|
132
|
+
}
|
|
133
|
+
async unstake(data, extParams) {
|
|
134
|
+
const { ixs } = await this.prepareUnstakeInstructions(data, extParams);
|
|
135
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
136
|
+
return {
|
|
137
|
+
ixs,
|
|
138
|
+
txId: signature,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async prepareUnstakeInstructions({ stakePool, stakePoolMint, nonce, tokenProgramId = TOKEN_PROGRAM_ID }, extParams) {
|
|
142
|
+
const { stakePoolProgram } = this.programs;
|
|
143
|
+
const staker = extParams.invoker.publicKey;
|
|
144
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
145
|
+
const stakeMintKey = deriveStakeMintPDA(stakePoolProgram.programId, pk(stakePool));
|
|
146
|
+
const stakeEntryKey = deriveStakeEntryPDA(stakePoolProgram.programId, pk(stakePool), staker, nonce);
|
|
147
|
+
const poolMintAccountKey = getAssociatedTokenAddressSync(pk(stakePoolMint), staker, false, pk(tokenProgramId));
|
|
148
|
+
const stakeMintAccountKey = getAssociatedTokenAddressSync(stakeMintKey, staker, false, pk(tokenProgramId));
|
|
149
|
+
const instruction = await stakePoolProgram.methods
|
|
150
|
+
.unstake()
|
|
151
|
+
.accounts({
|
|
152
|
+
stakeEntry: stakeEntryKey,
|
|
153
|
+
to: poolMintAccountKey,
|
|
154
|
+
from: stakeMintAccountKey,
|
|
155
|
+
authority: staker,
|
|
156
|
+
tokenProgram: tokenProgramId,
|
|
157
|
+
})
|
|
158
|
+
.instruction();
|
|
159
|
+
return { ixs: [instruction] };
|
|
160
|
+
}
|
|
161
|
+
async createRewardPool(data, extParams) {
|
|
162
|
+
const { ixs, publicKey } = await this.prepareCreateRewardPoolInstructions(data, extParams);
|
|
163
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
164
|
+
return {
|
|
165
|
+
ixs,
|
|
166
|
+
txId: signature,
|
|
167
|
+
metadataId: publicKey,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async prepareCreateRewardPoolInstructions({ nonce, rewardAmount, rewardPeriod, rewardMint, permissionless = false, stakePool, tokenProgramId = TOKEN_PROGRAM_ID, }, extParams) {
|
|
171
|
+
const { rewardPoolProgram } = this.programs;
|
|
172
|
+
const creator = extParams.invoker.publicKey;
|
|
173
|
+
invariant(creator, "Undefined invoker publicKey");
|
|
174
|
+
const instruction = await rewardPoolProgram.methods
|
|
175
|
+
.createPool(nonce, rewardAmount, rewardPeriod, permissionless)
|
|
176
|
+
.accounts({
|
|
177
|
+
creator,
|
|
178
|
+
stakePool,
|
|
179
|
+
mint: rewardMint,
|
|
180
|
+
tokenProgram: tokenProgramId,
|
|
181
|
+
})
|
|
182
|
+
.instruction();
|
|
183
|
+
const rewardPoolKey = deriveRewardPoolPDA(rewardPoolProgram.programId, pk(stakePool), pk(rewardMint), nonce);
|
|
184
|
+
return { publicKey: rewardPoolKey, ixs: [instruction] };
|
|
185
|
+
}
|
|
186
|
+
async claimRewards(data, extParams) {
|
|
187
|
+
const { ixs } = await this.prepareClaimRewardsInstructions(data, extParams);
|
|
188
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
189
|
+
return {
|
|
190
|
+
ixs,
|
|
191
|
+
txId: signature,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async prepareClaimRewardsInstructions({ rewardPoolNonce, depositNonce, stakePool, tokenProgramId = TOKEN_PROGRAM_ID, rewardMint }, extParams) {
|
|
195
|
+
const { stakePoolProgram, rewardPoolProgram } = this.programs;
|
|
196
|
+
const staker = extParams.invoker.publicKey;
|
|
197
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
198
|
+
const instruction = await rewardPoolProgram.methods
|
|
199
|
+
.claimRewards()
|
|
200
|
+
.accounts({
|
|
201
|
+
stakeEntry: deriveStakeEntryPDA(stakePoolProgram.programId, pk(stakePool), staker, depositNonce),
|
|
202
|
+
rewardPool: deriveRewardPoolPDA(rewardPoolProgram.programId, pk(stakePool), pk(rewardMint), rewardPoolNonce),
|
|
203
|
+
claimant: staker,
|
|
204
|
+
tokenProgram: tokenProgramId,
|
|
205
|
+
to: getAssociatedTokenAddressSync(pk(rewardMint), staker, false, pk(tokenProgramId)),
|
|
206
|
+
})
|
|
207
|
+
.instruction();
|
|
208
|
+
return { ixs: [instruction] };
|
|
209
|
+
}
|
|
210
|
+
async fundPool(data, extParams) {
|
|
211
|
+
const { ixs } = await this.prepareFundPoolInstructions(data, extParams);
|
|
212
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
213
|
+
return {
|
|
214
|
+
ixs,
|
|
215
|
+
txId: signature,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
async prepareFundPoolInstructions({ amount, tokenProgramId = TOKEN_PROGRAM_ID, rewardMint, stakePool, feeValue, nonce }, extParams) {
|
|
219
|
+
const { rewardPoolProgram } = this.programs;
|
|
220
|
+
const staker = extParams.invoker.publicKey;
|
|
221
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
222
|
+
const existingFee = await this.getFeeValueIfExists(staker);
|
|
223
|
+
const rewardMintPk = pk(rewardMint);
|
|
224
|
+
const tokenProgramPk = pk(tokenProgramId);
|
|
225
|
+
const treasuryATA = !existingFee || existingFee.streamflowFee.gtn(0)
|
|
226
|
+
? await checkOrCreateAtaBatch(this.connection, [STREAMFLOW_TREASURY_PUBLIC_KEY], rewardMintPk, extParams.invoker, tokenProgramPk)
|
|
227
|
+
: null;
|
|
228
|
+
const rewardPoolPda = deriveRewardPoolPDA(rewardPoolProgram.programId, pk(stakePool), rewardMintPk, nonce);
|
|
229
|
+
const instruction = await rewardPoolProgram.methods
|
|
230
|
+
.fundPool(amount)
|
|
231
|
+
.accountsPartial({
|
|
232
|
+
funder: staker,
|
|
233
|
+
rewardPool: rewardPoolPda,
|
|
234
|
+
from: getAssociatedTokenAddressSync(rewardMintPk, staker, false, tokenProgramPk),
|
|
235
|
+
tokenProgram: tokenProgramId,
|
|
236
|
+
vault: deriveRewardVaultPDA(rewardPoolProgram.programId, rewardPoolPda),
|
|
237
|
+
mint: rewardMint,
|
|
238
|
+
feeValue,
|
|
239
|
+
})
|
|
240
|
+
.instruction();
|
|
241
|
+
return { ixs: treasuryATA ? treasuryATA.concat([instruction]) : [instruction] };
|
|
242
|
+
}
|
|
243
|
+
async createRewardEntry(data, extParams) {
|
|
244
|
+
const { ixs } = await this.prepareCreateRewardEntryInstructions(data, extParams);
|
|
245
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
246
|
+
return {
|
|
247
|
+
ixs,
|
|
248
|
+
txId: signature,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async prepareCreateRewardEntryInstructions({ stakePoolMint, stakePool, rewardPoolNonce, depositNonce }, extParams) {
|
|
252
|
+
const { stakePoolProgram, rewardPoolProgram } = this.programs;
|
|
253
|
+
const staker = extParams.invoker.publicKey;
|
|
254
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
255
|
+
const instruction = await rewardPoolProgram.methods
|
|
256
|
+
.createEntry()
|
|
257
|
+
.accounts({
|
|
258
|
+
payer: staker,
|
|
259
|
+
authority: staker,
|
|
260
|
+
stakeEntry: deriveStakeEntryPDA(stakePoolProgram.programId, pk(stakePool), staker, depositNonce),
|
|
261
|
+
rewardPool: deriveRewardPoolPDA(rewardPoolProgram.programId, pk(stakePool), pk(stakePoolMint), rewardPoolNonce),
|
|
262
|
+
})
|
|
263
|
+
.instruction();
|
|
264
|
+
return { ixs: [instruction] };
|
|
265
|
+
}
|
|
266
|
+
async execute(ixs, extParams) {
|
|
267
|
+
const { tx, hash, context } = await prepareTransaction(this.connection, ixs, extParams.invoker.publicKey);
|
|
268
|
+
try {
|
|
269
|
+
const signature = await signAndExecuteTransaction(this.connection, extParams.invoker, tx, {
|
|
270
|
+
hash,
|
|
271
|
+
context,
|
|
272
|
+
commitment: this.getCommitment(),
|
|
273
|
+
}, { sendThrottler: this.sendThrottler });
|
|
274
|
+
return { signature };
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
if (err instanceof Error) {
|
|
278
|
+
const parsed = translateError(err, parseIdlErrors(this.programs.stakePoolProgram.idl));
|
|
279
|
+
if (parsed) {
|
|
280
|
+
throw new ContractError(err, parsed.name, parsed.message);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
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
|
+
}
|