@streamflow/staking 7.0.0-alpha.6
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 +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 +69 -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
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Streamflow Staking
|
|
2
|
+
|
|
3
|
+
## JS SDK to interact with Streamflow Staking.
|
|
4
|
+
|
|
5
|
+
This package allows you to
|
|
6
|
+
- `create staking pools and rewards pools`;
|
|
7
|
+
- `claim rewards`;
|
|
8
|
+
- `stake`;
|
|
9
|
+
- `unstake`;
|
|
10
|
+
- `fund rewards pools`;
|
|
11
|
+
|
|
12
|
+
with the Streamflow Staking protocol.
|
|
13
|
+
|
|
14
|
+
This protocol is the complex of several programs which ensures flexibility and accountability for stakers and respective incentives for them.
|
|
15
|
+
|
|
16
|
+
aforementioned programs are:
|
|
17
|
+
- Stake Pools Program
|
|
18
|
+
- Reward Pools Program
|
|
19
|
+
- Fee Management Program (for streamflow usage, non-required and ommited from further docs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## API Reference
|
|
23
|
+
API Documentation available here: [docs site →](https://streamflow-finance.github.io/js-sdk/)
|
|
24
|
+
|
|
25
|
+
The Stake Pool Program entities:
|
|
26
|
+
1. Stake Pool
|
|
27
|
+
2. Stake Entry (PDA which stores info about current stakes, durations and rewards structure)
|
|
28
|
+
|
|
29
|
+
The Reward Pool Program entities:
|
|
30
|
+
1. Reward Pool (must has a back-reference to a stake pool)
|
|
31
|
+
2. Reward Entry (PDA stores claims information and time-bound params)
|
|
32
|
+
|
|
33
|
+
1 Stake Pool can have N Reward Pools.
|
|
34
|
+
|
|
35
|
+
> [!NOTE]
|
|
36
|
+
> There is a limitation of 255 Reward Pools per a single token mint for a stake pool.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Install the sdk with npm
|
|
42
|
+
|
|
43
|
+
```npm
|
|
44
|
+
npm install @streamflow/staking
|
|
45
|
+
```
|
|
46
|
+
```yarn
|
|
47
|
+
yarn install @streamflow/staking
|
|
48
|
+
```
|
|
49
|
+
```pnpm
|
|
50
|
+
pnpm add @streamflow/staking
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage/Examples
|
|
54
|
+
|
|
55
|
+
#### Create a client
|
|
56
|
+
```typescript
|
|
57
|
+
const client = new SolanaStakingClient({
|
|
58
|
+
clusterUrl: "https://api.mainnet-beta.solana.com",
|
|
59
|
+
cluster: ICluster.Mainnet
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> [!WARNING]
|
|
64
|
+
> All operations expect ATAs to be created at the moment of execution and don't add these instructions.
|
|
65
|
+
> - Stake - staker's ATAs for stake mint and stake mint (see deriveStakeMintPDA fn)
|
|
66
|
+
> - Withdraw/Unstake - staker's ATAs for stake mint and stake mint (see deriveStakeMintPDA fn)
|
|
67
|
+
> - Claim rewards - staker's ATAs for reward mint
|
|
68
|
+
> - Fund Reward Pool - signer creates Streamflow Treasury's ATA for holding fee if defined
|
|
69
|
+
|
|
70
|
+
#### Read operations
|
|
71
|
+
```typescript
|
|
72
|
+
|
|
73
|
+
await client.searchStakePools({ mint, creator }) // returns results of lookup, `mint` and `creator` both optional. Omit the argument to get all pools
|
|
74
|
+
|
|
75
|
+
await client.searchStakeEntries({ payer, stakePool }) // returns all stake entries. Omit the argument to get all.
|
|
76
|
+
|
|
77
|
+
await client.searchRewardPools({ stakePool, mint })
|
|
78
|
+
|
|
79
|
+
await client.searchRewardEntries({ stakeEntry, rewardPool })
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Create a staking pool
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const client = new SolanaStakingClient({
|
|
87
|
+
clusterUrl: "https://api.mainnet-beta.solana.com",
|
|
88
|
+
cluster: ICluster.Mainnet
|
|
89
|
+
});
|
|
90
|
+
/*
|
|
91
|
+
Rewards Multiplier powered by 10^9.
|
|
92
|
+
Example: if multiplier is 2_000_000_000 than stakes for maxDuration will have 2x more rewards than stakes for minDuration
|
|
93
|
+
*/
|
|
94
|
+
const multiplier = new BN(1_000_000_000);
|
|
95
|
+
/*
|
|
96
|
+
30 days - Unix time in seconds
|
|
97
|
+
*/
|
|
98
|
+
const maxDuration = new BN(2592000);
|
|
99
|
+
/*
|
|
100
|
+
1 day - Unix time in seconds
|
|
101
|
+
*/
|
|
102
|
+
const maxDuration = new BN(86400);
|
|
103
|
+
/*
|
|
104
|
+
Limits signers that can create/assign reward pools to this stake pool. True - anyone can
|
|
105
|
+
*/
|
|
106
|
+
const permissionless = false;
|
|
107
|
+
/*
|
|
108
|
+
[0;256) derive stake pool PDA account address.
|
|
109
|
+
If stake pool with the same mint already exists, it is required to pick a vacant nonce
|
|
110
|
+
*/
|
|
111
|
+
const nonce = 0;
|
|
112
|
+
|
|
113
|
+
const { metadataId: stakePoolPda } = await client.createStakePool({
|
|
114
|
+
maxWeight: multiplier,
|
|
115
|
+
maxDuration,
|
|
116
|
+
minDuration,
|
|
117
|
+
mint: MINT_ADDRESS,
|
|
118
|
+
permissionless,
|
|
119
|
+
nonce:
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Create a rewardPool pool
|
|
125
|
+
```typescript
|
|
126
|
+
/*
|
|
127
|
+
[0;256) derive reward pool PDA account address.
|
|
128
|
+
If reward pool with the same mint already exists, it is required to pick a vacant nonce
|
|
129
|
+
*/
|
|
130
|
+
const nonce = 0;
|
|
131
|
+
/*
|
|
132
|
+
Amount of rewarding tokens stakers get in return for staking exactly 1 token to the staking pool
|
|
133
|
+
*/
|
|
134
|
+
const rewardAmount = new BN(100);
|
|
135
|
+
/*
|
|
136
|
+
1 day - Unix time in seconds. Period for rewarding stakers. Period starts counting from the moment of staking
|
|
137
|
+
*/
|
|
138
|
+
const rewardPeriod = new BN(86400);
|
|
139
|
+
const rewardMint = REWARD_MINT_ADDRESS; // rewarding token
|
|
140
|
+
/*
|
|
141
|
+
Whether to allow anyone to fund this reward pool. If true anyone can fund, otherwise only the creator can
|
|
142
|
+
*/
|
|
143
|
+
const permissionless = true;
|
|
144
|
+
|
|
145
|
+
client.createRewardPool({
|
|
146
|
+
nonce,
|
|
147
|
+
rewardAmount,
|
|
148
|
+
rewardPeriod,
|
|
149
|
+
rewardMint,
|
|
150
|
+
permissionless = false,
|
|
151
|
+
stakePool: stakePoolPda,
|
|
152
|
+
stakePoolMint: MINT_ADDRESS,
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
#### Deposit/Stake to a stake pool
|
|
158
|
+
```typescript
|
|
159
|
+
/*
|
|
160
|
+
[0;256) derive stake entry PDA account address.
|
|
161
|
+
If stake entry with the same nonce already exists, it is required to pick a vacant one
|
|
162
|
+
*/
|
|
163
|
+
const nonce = 0;
|
|
164
|
+
const amount = new BN(1000); // tokens to stake
|
|
165
|
+
const duration = new BN(86400 * 2) // 2 days, must be in the range of stakePool's min and max durations
|
|
166
|
+
await client.stake({ nonce, amount, duration, stakePool, stakePoolMint });
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Unstake/Withdraw to a stake pool
|
|
170
|
+
```typescript
|
|
171
|
+
/*
|
|
172
|
+
Usually to achieve this the app already loaded available stakeEntries.
|
|
173
|
+
Stake Entry holds used `nonce`, so `nonce` below could be taken from the stake entry
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
[0;256) derived stake entry PDA account address.
|
|
178
|
+
If stake entry with the same nonce already exists, it is required to pick a vacant one
|
|
179
|
+
*/
|
|
180
|
+
const nonce = 0; //
|
|
181
|
+
await client.unstake({ stakePool, stakePoolMint, nonce });
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Claim a reward
|
|
185
|
+
Since each stake entry can produce multiple rewards (single claim per each reward pool linked to the staking pool) this operation
|
|
186
|
+
can be triggered for every reward pool separately.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
await client.claimRewards({
|
|
190
|
+
rewardPoolNonce,
|
|
191
|
+
depositNonce,
|
|
192
|
+
stakePool,
|
|
193
|
+
rewardMint,
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
> [!Note]
|
|
198
|
+
> All operations have accompanying APIs for manual transaction building. Consult with API docs to find respective calls.
|
|
199
|
+
> For instance, prepareClaimRewardsInstructions.
|
|
200
|
+
> These APIs allow to aggregate multiple operations in a single transaction according to the app needs.
|
|
201
|
+
|
|
202
|
+
## Appendix
|
|
203
|
+
|
|
204
|
+
Streamflow Staking protocol program IDs
|
|
205
|
+
|
|
206
|
+
| Solana | |
|
|
207
|
+
| ------- | -------------------------------------------- |
|
|
208
|
+
| Staking Pools Mainnet | STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH |
|
|
209
|
+
| Reward Pools Mainnet | RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj |
|
|
210
|
+
| ---- | --- |
|
|
211
|
+
| Staking Pools Devnet | STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH |
|
|
212
|
+
| Reward Pools Devnet | RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj |
|
|
213
|
+
|
|
214
|
+
### IDLs
|
|
215
|
+
For further details you can consult with IDLs of protocols available at:
|
|
216
|
+
`@streamflow/staking/dist/esm/solana/descriptor`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
+
if (mod && mod.__esModule) return mod;
|
|
23
|
+
var result = {};
|
|
24
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
+
__setModuleDefault(result, mod);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.constants = exports.SolanaStakingClient = void 0;
|
|
33
|
+
var client_js_1 = require("./solana/client.js");
|
|
34
|
+
Object.defineProperty(exports, "SolanaStakingClient", { enumerable: true, get: function () { return __importDefault(client_js_1).default; } });
|
|
35
|
+
__exportStar(require("./solana/utils.js"), exports);
|
|
36
|
+
__exportStar(require("./solana/types.js"), exports);
|
|
37
|
+
__exportStar(require("./solana/lib/derive-accounts.js"), exports);
|
|
38
|
+
__exportStar(require("./solana/lib/rewards.js"), exports);
|
|
39
|
+
exports.constants = __importStar(require("./solana/constants.js"));
|
|
@@ -0,0 +1,315 @@
|
|
|
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
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
8
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
9
|
+
const common_1 = require("@streamflow/common");
|
|
10
|
+
const solana_1 = require("@streamflow/common/solana");
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
12
|
+
const fee_manager_json_1 = __importDefault(require("./descriptor/idl/fee_manager.json"));
|
|
13
|
+
const reward_pool_json_1 = __importDefault(require("./descriptor/idl/reward_pool.json"));
|
|
14
|
+
const stake_pool_json_1 = __importDefault(require("./descriptor/idl/stake_pool.json"));
|
|
15
|
+
const derive_accounts_js_1 = require("./lib/derive-accounts.js");
|
|
16
|
+
class SolanaStakingClient {
|
|
17
|
+
constructor({ clusterUrl, cluster = common_1.ICluster.Mainnet, commitment = "confirmed", programIds, sendRate = 1, sendThrottler, }) {
|
|
18
|
+
this.commitment = commitment;
|
|
19
|
+
this.connection = new web3_js_1.Connection(clusterUrl, this.commitment);
|
|
20
|
+
this.sendThrottler = sendThrottler ?? (0, solana_1.buildSendThrottler)(sendRate);
|
|
21
|
+
const stakePoolIdl = {
|
|
22
|
+
...stake_pool_json_1.default,
|
|
23
|
+
address: programIds?.stakePool ?? constants_js_1.STAKE_POOL_PROGRAM_ID[cluster] ?? stake_pool_json_1.default.address,
|
|
24
|
+
};
|
|
25
|
+
const rewardPoolIdl = {
|
|
26
|
+
...reward_pool_json_1.default,
|
|
27
|
+
address: programIds?.rewardPool ?? constants_js_1.REWARD_POOL_PROGRAM_ID[cluster] ?? reward_pool_json_1.default.address,
|
|
28
|
+
};
|
|
29
|
+
const feeManagerIdl = {
|
|
30
|
+
...fee_manager_json_1.default,
|
|
31
|
+
address: programIds?.feeManager ?? fee_manager_json_1.default.address,
|
|
32
|
+
};
|
|
33
|
+
this.programs = {
|
|
34
|
+
stakePoolProgram: new anchor_1.Program(stakePoolIdl, {
|
|
35
|
+
connection: this.connection,
|
|
36
|
+
}),
|
|
37
|
+
rewardPoolProgram: new anchor_1.Program(rewardPoolIdl, {
|
|
38
|
+
connection: this.connection,
|
|
39
|
+
}),
|
|
40
|
+
feeManagerProgram: new anchor_1.Program(feeManagerIdl, {
|
|
41
|
+
connection: this.connection,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
getCurrentProgramId(programKey) {
|
|
46
|
+
const program = this.programs[programKey];
|
|
47
|
+
invariant(program, `Program ${programKey} is not found`);
|
|
48
|
+
return program.programId;
|
|
49
|
+
}
|
|
50
|
+
getCommitment() {
|
|
51
|
+
return typeof this.commitment == "string" ? this.commitment : this.commitment.commitment;
|
|
52
|
+
}
|
|
53
|
+
async getStakePool(id) {
|
|
54
|
+
const { stakePoolProgram } = this.programs;
|
|
55
|
+
return stakePoolProgram.account.stakePool.fetch(id);
|
|
56
|
+
}
|
|
57
|
+
async searchStakePools(criteria = {}) {
|
|
58
|
+
const { stakePoolProgram } = this.programs;
|
|
59
|
+
return stakePoolProgram.account.stakePool.all(getFilters(criteria, constants_js_1.STAKE_POOL_BYTE_OFFSETS));
|
|
60
|
+
}
|
|
61
|
+
async getStakeEntry(id) {
|
|
62
|
+
const { stakePoolProgram } = this.programs;
|
|
63
|
+
return stakePoolProgram.account.stakeEntry.fetch(id);
|
|
64
|
+
}
|
|
65
|
+
async searchStakeEntries(criteria = {}) {
|
|
66
|
+
const { stakePoolProgram } = this.programs;
|
|
67
|
+
return stakePoolProgram.account.stakeEntry.all(getFilters(criteria, constants_js_1.STAKE_ENTRY_BYTE_OFFSETS));
|
|
68
|
+
}
|
|
69
|
+
async searchRewardPools(criteria = {}) {
|
|
70
|
+
const { rewardPoolProgram } = this.programs;
|
|
71
|
+
return rewardPoolProgram.account.rewardPool.all(getFilters(criteria, constants_js_1.REWARD_POOL_BYTE_OFFSETS));
|
|
72
|
+
}
|
|
73
|
+
async searchRewardEntries(criteria) {
|
|
74
|
+
const { rewardPoolProgram } = this.programs;
|
|
75
|
+
return rewardPoolProgram.account.rewardEntry.all(getFilters(criteria, constants_js_1.REWARD_ENTRY_BYTE_OFFSETS));
|
|
76
|
+
}
|
|
77
|
+
getFeeValueIfExists(target) {
|
|
78
|
+
const { feeManagerProgram } = this.programs;
|
|
79
|
+
const feeValueKey = (0, derive_accounts_js_1.deriveFeeValuePDA)(feeManagerProgram.programId, new web3_js_1.PublicKey(target));
|
|
80
|
+
return feeManagerProgram.account.feeValue.fetchNullable(feeValueKey);
|
|
81
|
+
}
|
|
82
|
+
async createStakePool(data, extParams) {
|
|
83
|
+
const { ixs, publicKey } = await this.prepareCreateStakePoolInstructions(data, extParams);
|
|
84
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
85
|
+
return {
|
|
86
|
+
ixs,
|
|
87
|
+
txId: signature,
|
|
88
|
+
metadataId: publicKey,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async prepareCreateStakePoolInstructions({ maxWeight, maxDuration, minDuration, mint, permissionless = false, nonce, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID, }, extParams) {
|
|
92
|
+
const { stakePoolProgram } = this.programs;
|
|
93
|
+
const creator = extParams.invoker.publicKey;
|
|
94
|
+
invariant(creator, "Undefined invoker publicKey");
|
|
95
|
+
const createInstruction = await stakePoolProgram.methods
|
|
96
|
+
.createPool(nonce, maxWeight, minDuration, maxDuration, permissionless)
|
|
97
|
+
.accounts({
|
|
98
|
+
creator,
|
|
99
|
+
mint,
|
|
100
|
+
tokenProgram: tokenProgramId,
|
|
101
|
+
})
|
|
102
|
+
.instruction();
|
|
103
|
+
const stakePoolPDA = (0, derive_accounts_js_1.deriveStakePoolPDA)(stakePoolProgram.programId, pk(mint), creator, nonce);
|
|
104
|
+
return { ixs: [createInstruction], publicKey: stakePoolPDA };
|
|
105
|
+
}
|
|
106
|
+
async stake(data, extParams) {
|
|
107
|
+
const { ixs } = await this.prepareStakeInstructions(data, extParams);
|
|
108
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
109
|
+
return {
|
|
110
|
+
ixs,
|
|
111
|
+
txId: signature,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async prepareStakeInstructions({ nonce, amount, duration, stakePool, stakePoolMint, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID }, extParams) {
|
|
115
|
+
const { stakePoolProgram } = this.programs;
|
|
116
|
+
const staker = extParams.invoker.publicKey;
|
|
117
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
118
|
+
const mint = (0, derive_accounts_js_1.deriveStakeMintPDA)(stakePoolProgram.programId, pk(stakePool));
|
|
119
|
+
const stakeMintAccountKey = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, staker, false, pk(tokenProgramId));
|
|
120
|
+
const poolMintAccountKey = (0, spl_token_1.getAssociatedTokenAddressSync)(pk(stakePoolMint), staker, false, pk(tokenProgramId));
|
|
121
|
+
const instruction = await stakePoolProgram.methods
|
|
122
|
+
.stake(nonce, amount, duration)
|
|
123
|
+
.accounts({
|
|
124
|
+
stakePool: stakePool,
|
|
125
|
+
tokenProgram: tokenProgramId,
|
|
126
|
+
from: poolMintAccountKey,
|
|
127
|
+
to: stakeMintAccountKey,
|
|
128
|
+
authority: staker,
|
|
129
|
+
payer: staker,
|
|
130
|
+
})
|
|
131
|
+
.instruction();
|
|
132
|
+
return { ixs: [instruction] };
|
|
133
|
+
}
|
|
134
|
+
async unstake(data, extParams) {
|
|
135
|
+
const { ixs } = await this.prepareUnstakeInstructions(data, extParams);
|
|
136
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
137
|
+
return {
|
|
138
|
+
ixs,
|
|
139
|
+
txId: signature,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async prepareUnstakeInstructions({ stakePool, stakePoolMint, nonce, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID }, extParams) {
|
|
143
|
+
const { stakePoolProgram } = this.programs;
|
|
144
|
+
const staker = extParams.invoker.publicKey;
|
|
145
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
146
|
+
const stakeMintKey = (0, derive_accounts_js_1.deriveStakeMintPDA)(stakePoolProgram.programId, pk(stakePool));
|
|
147
|
+
const stakeEntryKey = (0, derive_accounts_js_1.deriveStakeEntryPDA)(stakePoolProgram.programId, pk(stakePool), staker, nonce);
|
|
148
|
+
const poolMintAccountKey = (0, spl_token_1.getAssociatedTokenAddressSync)(pk(stakePoolMint), staker, false, pk(tokenProgramId));
|
|
149
|
+
const stakeMintAccountKey = (0, spl_token_1.getAssociatedTokenAddressSync)(stakeMintKey, staker, false, pk(tokenProgramId));
|
|
150
|
+
const instruction = await stakePoolProgram.methods
|
|
151
|
+
.unstake()
|
|
152
|
+
.accounts({
|
|
153
|
+
stakeEntry: stakeEntryKey,
|
|
154
|
+
to: poolMintAccountKey,
|
|
155
|
+
from: stakeMintAccountKey,
|
|
156
|
+
authority: staker,
|
|
157
|
+
tokenProgram: tokenProgramId,
|
|
158
|
+
})
|
|
159
|
+
.instruction();
|
|
160
|
+
return { ixs: [instruction] };
|
|
161
|
+
}
|
|
162
|
+
async createRewardPool(data, extParams) {
|
|
163
|
+
const { ixs, publicKey } = await this.prepareCreateRewardPoolInstructions(data, extParams);
|
|
164
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
165
|
+
return {
|
|
166
|
+
ixs,
|
|
167
|
+
txId: signature,
|
|
168
|
+
metadataId: publicKey,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async prepareCreateRewardPoolInstructions({ nonce, rewardAmount, rewardPeriod, rewardMint, permissionless = false, stakePool, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID, }, extParams) {
|
|
172
|
+
const { rewardPoolProgram } = this.programs;
|
|
173
|
+
const creator = extParams.invoker.publicKey;
|
|
174
|
+
invariant(creator, "Undefined invoker publicKey");
|
|
175
|
+
const instruction = await rewardPoolProgram.methods
|
|
176
|
+
.createPool(nonce, rewardAmount, rewardPeriod, permissionless)
|
|
177
|
+
.accounts({
|
|
178
|
+
creator,
|
|
179
|
+
stakePool,
|
|
180
|
+
mint: rewardMint,
|
|
181
|
+
tokenProgram: tokenProgramId,
|
|
182
|
+
})
|
|
183
|
+
.instruction();
|
|
184
|
+
const rewardPoolKey = (0, derive_accounts_js_1.deriveRewardPoolPDA)(rewardPoolProgram.programId, pk(stakePool), pk(rewardMint), nonce);
|
|
185
|
+
return { publicKey: rewardPoolKey, ixs: [instruction] };
|
|
186
|
+
}
|
|
187
|
+
async claimRewards(data, extParams) {
|
|
188
|
+
const { ixs } = await this.prepareClaimRewardsInstructions(data, extParams);
|
|
189
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
190
|
+
return {
|
|
191
|
+
ixs,
|
|
192
|
+
txId: signature,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
async prepareClaimRewardsInstructions({ rewardPoolNonce, depositNonce, stakePool, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID, rewardMint }, extParams) {
|
|
196
|
+
const { stakePoolProgram, rewardPoolProgram } = this.programs;
|
|
197
|
+
const staker = extParams.invoker.publicKey;
|
|
198
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
199
|
+
const instruction = await rewardPoolProgram.methods
|
|
200
|
+
.claimRewards()
|
|
201
|
+
.accounts({
|
|
202
|
+
stakeEntry: (0, derive_accounts_js_1.deriveStakeEntryPDA)(stakePoolProgram.programId, pk(stakePool), staker, depositNonce),
|
|
203
|
+
rewardPool: (0, derive_accounts_js_1.deriveRewardPoolPDA)(rewardPoolProgram.programId, pk(stakePool), pk(rewardMint), rewardPoolNonce),
|
|
204
|
+
claimant: staker,
|
|
205
|
+
tokenProgram: tokenProgramId,
|
|
206
|
+
to: (0, spl_token_1.getAssociatedTokenAddressSync)(pk(rewardMint), staker, false, pk(tokenProgramId)),
|
|
207
|
+
})
|
|
208
|
+
.instruction();
|
|
209
|
+
return { ixs: [instruction] };
|
|
210
|
+
}
|
|
211
|
+
async fundPool(data, extParams) {
|
|
212
|
+
const { ixs } = await this.prepareFundPoolInstructions(data, extParams);
|
|
213
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
214
|
+
return {
|
|
215
|
+
ixs,
|
|
216
|
+
txId: signature,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async prepareFundPoolInstructions({ amount, tokenProgramId = spl_token_1.TOKEN_PROGRAM_ID, rewardMint, stakePool, feeValue, nonce }, extParams) {
|
|
220
|
+
const { rewardPoolProgram } = this.programs;
|
|
221
|
+
const staker = extParams.invoker.publicKey;
|
|
222
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
223
|
+
const existingFee = await this.getFeeValueIfExists(staker);
|
|
224
|
+
const rewardMintPk = pk(rewardMint);
|
|
225
|
+
const tokenProgramPk = pk(tokenProgramId);
|
|
226
|
+
const treasuryATA = !existingFee || existingFee.streamflowFee.gtn(0)
|
|
227
|
+
? await (0, solana_1.checkOrCreateAtaBatch)(this.connection, [constants_js_1.STREAMFLOW_TREASURY_PUBLIC_KEY], rewardMintPk, extParams.invoker, tokenProgramPk)
|
|
228
|
+
: null;
|
|
229
|
+
const rewardPoolPda = (0, derive_accounts_js_1.deriveRewardPoolPDA)(rewardPoolProgram.programId, pk(stakePool), rewardMintPk, nonce);
|
|
230
|
+
const instruction = await rewardPoolProgram.methods
|
|
231
|
+
.fundPool(amount)
|
|
232
|
+
.accountsPartial({
|
|
233
|
+
funder: staker,
|
|
234
|
+
rewardPool: rewardPoolPda,
|
|
235
|
+
from: (0, spl_token_1.getAssociatedTokenAddressSync)(rewardMintPk, staker, false, tokenProgramPk),
|
|
236
|
+
tokenProgram: tokenProgramId,
|
|
237
|
+
vault: (0, derive_accounts_js_1.deriveRewardVaultPDA)(rewardPoolProgram.programId, rewardPoolPda),
|
|
238
|
+
mint: rewardMint,
|
|
239
|
+
feeValue,
|
|
240
|
+
})
|
|
241
|
+
.instruction();
|
|
242
|
+
return { ixs: treasuryATA ? treasuryATA.concat([instruction]) : [instruction] };
|
|
243
|
+
}
|
|
244
|
+
async createRewardEntry(data, extParams) {
|
|
245
|
+
const { ixs } = await this.prepareCreateRewardEntryInstructions(data, extParams);
|
|
246
|
+
const { signature } = await this.execute(ixs, extParams);
|
|
247
|
+
return {
|
|
248
|
+
ixs,
|
|
249
|
+
txId: signature,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async prepareCreateRewardEntryInstructions({ stakePoolMint, stakePool, rewardPoolNonce, depositNonce }, extParams) {
|
|
253
|
+
const { stakePoolProgram, rewardPoolProgram } = this.programs;
|
|
254
|
+
const staker = extParams.invoker.publicKey;
|
|
255
|
+
invariant(staker, "Undefined invoker publicKey");
|
|
256
|
+
const instruction = await rewardPoolProgram.methods
|
|
257
|
+
.createEntry()
|
|
258
|
+
.accounts({
|
|
259
|
+
payer: staker,
|
|
260
|
+
authority: staker,
|
|
261
|
+
stakeEntry: (0, derive_accounts_js_1.deriveStakeEntryPDA)(stakePoolProgram.programId, pk(stakePool), staker, depositNonce),
|
|
262
|
+
rewardPool: (0, derive_accounts_js_1.deriveRewardPoolPDA)(rewardPoolProgram.programId, pk(stakePool), pk(stakePoolMint), rewardPoolNonce),
|
|
263
|
+
})
|
|
264
|
+
.instruction();
|
|
265
|
+
return { ixs: [instruction] };
|
|
266
|
+
}
|
|
267
|
+
async execute(ixs, extParams) {
|
|
268
|
+
const { tx, hash, context } = await (0, solana_1.prepareTransaction)(this.connection, ixs, extParams.invoker.publicKey);
|
|
269
|
+
try {
|
|
270
|
+
const signature = await (0, solana_1.signAndExecuteTransaction)(this.connection, extParams.invoker, tx, {
|
|
271
|
+
hash,
|
|
272
|
+
context,
|
|
273
|
+
commitment: this.getCommitment(),
|
|
274
|
+
}, { sendThrottler: this.sendThrottler });
|
|
275
|
+
return { signature };
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
if (err instanceof Error) {
|
|
279
|
+
const parsed = (0, anchor_1.translateError)(err, (0, anchor_1.parseIdlErrors)(this.programs.stakePoolProgram.idl));
|
|
280
|
+
if (parsed) {
|
|
281
|
+
throw new common_1.ContractError(err, parsed.name, parsed.message);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
exports.default = SolanaStakingClient;
|
|
289
|
+
function pk(address) {
|
|
290
|
+
return typeof address === "string" ? new web3_js_1.PublicKey(address) : address;
|
|
291
|
+
}
|
|
292
|
+
const prefix = "Assertion failed";
|
|
293
|
+
function invariant(condition, message) {
|
|
294
|
+
if (condition) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const provided = typeof message === "function" ? message() : message;
|
|
298
|
+
const value = provided ? `${prefix}: ${provided}` : prefix;
|
|
299
|
+
throw new Error(value);
|
|
300
|
+
}
|
|
301
|
+
function getFilters(criteria, byteOffsets) {
|
|
302
|
+
return Object.entries(criteria).reduce((acc, [key, value]) => {
|
|
303
|
+
const criteriaKey = key;
|
|
304
|
+
const effectiveByteOffset = byteOffsets[criteriaKey];
|
|
305
|
+
if (criteria[criteriaKey] && effectiveByteOffset) {
|
|
306
|
+
acc.push({
|
|
307
|
+
memcmp: {
|
|
308
|
+
offset: effectiveByteOffset,
|
|
309
|
+
bytes: value.toString(),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return acc;
|
|
314
|
+
}, []);
|
|
315
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REWARD_POOL_PROGRAM_ID = exports.STAKE_POOL_PROGRAM_ID = exports.REWARD_ENTRY_BYTE_OFFSETS = exports.REWARD_ENTRY_STAKE_ENTRY_OFFSET = exports.REWARD_ENTRY_REWARD_POOL_OFFSET = exports.REWARD_POOL_BYTE_OFFSETS = exports.REWARD_POOL_MINT_OFFSET = exports.REWARD_POOL_STAKE_POOL_OFFSET = exports.STAKE_POOL_BYTE_OFFSETS = exports.STAKE_POOL_CREATOR_OFFSET = exports.STAKE_POOL_MINT_OFFSET = exports.STAKE_ENTRY_BYTE_OFFSETS = exports.STAKE_ENTRY_OWNER_OFFSET = exports.STAKE_ENTRY_STAKE_POOL_OFFSET = exports.ANCHOR_DISCRIMINATOR_OFFSET = exports.STREAMFLOW_TREASURY_PUBLIC_KEY = exports.FEE_VALUE_PREFIX = exports.CONFIG_PREFIX = exports.REWARD_ENTRY_PREFIX = exports.REWARD_VAULT_PREFIX = exports.REWARD_POOL_PREFIX = exports.STAKE_VAULT_PREFIX = exports.STAKE_MINT_PREFIX = exports.STAKE_POOL_PREFIX = exports.STAKE_ENTRY_PREFIX = exports.STAKE_ENTRY_DISCRIMINATOR = exports.U64_MAX = exports.SCALE_PRECISION_FACTOR_BN = exports.SCALE_PRECISION_FACTOR = exports.DEFAULT_FEE_BN = exports.DEFAULT_FEE = exports.FEE_PRECISION_FACTOR_BN = exports.FEE_PRECISION_FACTOR = void 0;
|
|
4
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
5
|
+
const common_1 = require("@streamflow/common");
|
|
6
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
7
|
+
exports.FEE_PRECISION_FACTOR = 10000;
|
|
8
|
+
exports.FEE_PRECISION_FACTOR_BN = new anchor_1.BN(exports.FEE_PRECISION_FACTOR);
|
|
9
|
+
exports.DEFAULT_FEE = 99;
|
|
10
|
+
exports.DEFAULT_FEE_BN = new anchor_1.BN(exports.DEFAULT_FEE);
|
|
11
|
+
exports.SCALE_PRECISION_FACTOR = 1000000000;
|
|
12
|
+
exports.SCALE_PRECISION_FACTOR_BN = new anchor_1.BN(exports.SCALE_PRECISION_FACTOR);
|
|
13
|
+
exports.U64_MAX = 18446744073709551615n;
|
|
14
|
+
exports.STAKE_ENTRY_DISCRIMINATOR = [187, 127, 9, 35, 155, 68, 86, 40];
|
|
15
|
+
exports.STAKE_ENTRY_PREFIX = Buffer.from("stake-entry", "utf-8");
|
|
16
|
+
exports.STAKE_POOL_PREFIX = Buffer.from("stake-pool", "utf-8");
|
|
17
|
+
exports.STAKE_MINT_PREFIX = Buffer.from("stake-mint", "utf-8");
|
|
18
|
+
exports.STAKE_VAULT_PREFIX = Buffer.from("stake-vault", "utf-8");
|
|
19
|
+
exports.REWARD_POOL_PREFIX = Buffer.from("reward-pool", "utf-8");
|
|
20
|
+
exports.REWARD_VAULT_PREFIX = Buffer.from("reward-vault", "utf-8");
|
|
21
|
+
exports.REWARD_ENTRY_PREFIX = Buffer.from("reward-entry", "utf-8");
|
|
22
|
+
exports.CONFIG_PREFIX = Buffer.from("config", "utf-8");
|
|
23
|
+
exports.FEE_VALUE_PREFIX = Buffer.from("fee-value", "utf-8");
|
|
24
|
+
exports.STREAMFLOW_TREASURY_PUBLIC_KEY = new web3_js_1.PublicKey("5SEpbdjFK5FxwTvfsGMXVQTD2v4M2c5tyRTxhdsPkgDw");
|
|
25
|
+
exports.ANCHOR_DISCRIMINATOR_OFFSET = 8;
|
|
26
|
+
exports.STAKE_ENTRY_STAKE_POOL_OFFSET = exports.ANCHOR_DISCRIMINATOR_OFFSET + 4;
|
|
27
|
+
exports.STAKE_ENTRY_OWNER_OFFSET = exports.STAKE_ENTRY_STAKE_POOL_OFFSET + 32;
|
|
28
|
+
exports.STAKE_ENTRY_BYTE_OFFSETS = {
|
|
29
|
+
payer: exports.STAKE_ENTRY_OWNER_OFFSET,
|
|
30
|
+
stakePool: exports.STAKE_ENTRY_STAKE_POOL_OFFSET,
|
|
31
|
+
};
|
|
32
|
+
exports.STAKE_POOL_MINT_OFFSET = exports.ANCHOR_DISCRIMINATOR_OFFSET + 8;
|
|
33
|
+
exports.STAKE_POOL_CREATOR_OFFSET = exports.STAKE_POOL_MINT_OFFSET + 32;
|
|
34
|
+
exports.STAKE_POOL_BYTE_OFFSETS = {
|
|
35
|
+
mint: exports.STAKE_POOL_MINT_OFFSET,
|
|
36
|
+
creator: exports.STAKE_POOL_CREATOR_OFFSET,
|
|
37
|
+
};
|
|
38
|
+
exports.REWARD_POOL_STAKE_POOL_OFFSET = exports.ANCHOR_DISCRIMINATOR_OFFSET + 2;
|
|
39
|
+
exports.REWARD_POOL_MINT_OFFSET = exports.REWARD_POOL_STAKE_POOL_OFFSET + 32;
|
|
40
|
+
exports.REWARD_POOL_BYTE_OFFSETS = {
|
|
41
|
+
stakePool: exports.REWARD_POOL_STAKE_POOL_OFFSET,
|
|
42
|
+
mint: exports.REWARD_POOL_MINT_OFFSET,
|
|
43
|
+
};
|
|
44
|
+
exports.REWARD_ENTRY_REWARD_POOL_OFFSET = exports.ANCHOR_DISCRIMINATOR_OFFSET;
|
|
45
|
+
exports.REWARD_ENTRY_STAKE_ENTRY_OFFSET = exports.REWARD_ENTRY_REWARD_POOL_OFFSET + 32;
|
|
46
|
+
exports.REWARD_ENTRY_BYTE_OFFSETS = {
|
|
47
|
+
stakeEntry: exports.REWARD_ENTRY_STAKE_ENTRY_OFFSET,
|
|
48
|
+
rewardPool: exports.REWARD_ENTRY_REWARD_POOL_OFFSET,
|
|
49
|
+
};
|
|
50
|
+
exports.STAKE_POOL_PROGRAM_ID = {
|
|
51
|
+
[common_1.ICluster.Mainnet]: "STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH",
|
|
52
|
+
[common_1.ICluster.Devnet]: "STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH",
|
|
53
|
+
[common_1.ICluster.Testnet]: "STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH",
|
|
54
|
+
[common_1.ICluster.Local]: "STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH",
|
|
55
|
+
};
|
|
56
|
+
exports.REWARD_POOL_PROGRAM_ID = {
|
|
57
|
+
[common_1.ICluster.Mainnet]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
58
|
+
[common_1.ICluster.Devnet]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
59
|
+
[common_1.ICluster.Testnet]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
60
|
+
[common_1.ICluster.Local]: "RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj",
|
|
61
|
+
};
|