Skip to content

Commit 40cbd43

Browse files
authored
feat: update staking idls, support new methods, add groupped methods (#297)
- update staking idls to support rent sponsor account in methods and support `close_entry` in stake-pool; - add reward pool dynamic IDL with basic exposure in the StakingClient; - add higher level methods to group staking/unstaking with reward pool actions; - update anchor to 0.31.1 - no major changes, better error handling; - update README; - make `execute` public method, move all tx estimation and base ixs logic into it - I think this method is good enough to be used for any kind of transactions;
1 parent 30e5cd2 commit 40cbd43

File tree

22 files changed

+8144
-1854
lines changed

22 files changed

+8144
-1854
lines changed

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"packages": [
33
"packages/*"
44
],
5-
"version": "8.2.2",
5+
"version": "8.3.0",
66
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
77
"command": {
88
"run": {

packages/common/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/common",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "Common utilities and types used by streamflow packages.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"type": "module",
@@ -50,7 +50,7 @@
5050
"typescript": "^5.6.3"
5151
},
5252
"dependencies": {
53-
"@coral-xyz/borsh": "0.30.1",
53+
"@coral-xyz/borsh": "0.31.1",
5454
"@solana/buffer-layout": "4.0.1",
5555
"@solana/spl-token": "0.4.9",
5656
"@solana/wallet-adapter-base": "0.9.19",

packages/distributor/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/distributor",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "JavaScript SDK to interact with Streamflow Airdrop protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",
@@ -75,8 +75,8 @@
7575
"typescript": "^5.6.3"
7676
},
7777
"dependencies": {
78-
"@coral-xyz/anchor": "^0.30.1",
79-
"@coral-xyz/borsh": "0.30.1",
78+
"@coral-xyz/anchor": "^0.31.1",
79+
"@coral-xyz/borsh": "0.31.1",
8080
"@solana/buffer-layout": "4.0.1",
8181
"@solana/spl-token": "0.4.9",
8282
"@solana/wallet-adapter-base": "0.9.19",

packages/eslint-config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/eslint-config",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "ESLint configuration for Streamflow protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"engines": {

packages/launchpad/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/launchpad",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "JavaScript SDK to interact with Streamflow Launchpad protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",
@@ -53,8 +53,8 @@
5353
"typescript": "^5.6.3"
5454
},
5555
"dependencies": {
56-
"@coral-xyz/anchor": "^0.30.1",
57-
"@coral-xyz/borsh": "^0.30.1",
56+
"@coral-xyz/anchor": "^0.31.1",
57+
"@coral-xyz/borsh": "^0.31.1",
5858
"@solana/buffer-layout": "4.0.1",
5959
"@solana/spl-token": "0.4.9",
6060
"@solana/wallet-adapter-base": "0.9.19",

packages/staking/README.md

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ await client.searchStakeEntries({ payer, stakePool }) // returns all stake entri
7777
await client.searchRewardPools({ stakePool, mint })
7878

7979
await client.searchRewardEntries({ stakeEntry, rewardPool })
80-
8180
```
8281

8382
#### Create a staking pool
@@ -87,6 +86,11 @@ const client = new SolanaStakingClient({
8786
clusterUrl: "https://api.mainnet-beta.solana.com",
8887
cluster: ICluster.Mainnet
8988
});
89+
/*
90+
invoker should be of type SignerWalletAdapter | Keypair
91+
computePrice and computeLimit are optional
92+
*/
93+
const extParams = { invoker, computePrice: 10_000, computeLimit: 'autoSimulate' };
9094
/*
9195
Rewards Multiplier powered by 10^9.
9296
Example: if multiplier is 2_000_000_000 than stakes for maxDuration will have 2x more rewards than stakes for minDuration
@@ -116,8 +120,8 @@ const { metadataId: stakePoolPda } = await client.createStakePool({
116120
minDuration,
117121
mint: MINT_ADDRESS,
118122
permissionless,
119-
nonce:
120-
})
123+
nonce,
124+
}, extParams)
121125

122126
```
123127

@@ -158,18 +162,17 @@ const permissionless = true;
158162
const freezeStakeMint = true;
159163

160164
client.createRewardPool({
161-
nonce,
162-
rewardAmount,
163-
rewardPeriod,
164-
rewardMint,
165-
permissionless,
166-
freezeStakeMint,
167-
stakePool: stakePoolPda,
168-
stakePoolMint: MINT_ADDRESS,
169-
})
165+
nonce,
166+
rewardAmount,
167+
rewardPeriod,
168+
rewardMint,
169+
permissionless,
170+
freezeStakeMint,
171+
stakePool: stakePoolPda,
172+
stakePoolMint: MINT_ADDRESS,
173+
}, extParams)
170174
```
171175

172-
173176
#### Reward Amount configuration (in-depth)
174177

175178
`rewardAmount` represents a 10^9 fraction of a raw token distributed for every **effective staked raw token** - it's important to account for both reward and stake token decimals when creating staking pool because of that.
@@ -215,9 +218,31 @@ We recommend to use the `calculateRewardAmountFromRate` function exposed by the
215218
const nonce = 0;
216219
const amount = new BN(1000); // tokens to stake
217220
const duration = new BN(86400 * 2) // 2 days, must be in the range of stakePool's min and max durations
218-
await client.stake({ nonce, amount, duration, stakePool, stakePoolMint });
221+
await client.stake({ nonce, amount, duration, stakePool, stakePoolMint }, extParams);
222+
223+
// Create Reward Entry to track rewards, call instruction for every reward pool on `stake`
224+
await client.createRewardEntry({ stakePool, rewardPoolNonce, depositNonce: nonce, rewardMint }, extParams);
225+
```
226+
227+
> [!WARNING]
228+
> For every Reward Pool a Reward entry should be created prior to claim and ideally at the same time when user stakes. Without a Reward Entry pool won't be able to properly track reward distribution.
229+
230+
You can also bundle multiple instructions with `prepare` calls to stake and create entries in one transaction:
231+
232+
```typescript
233+
const nonce = 1;
234+
const stakeIxs = await client.prepareStakeInstructions({ nonce, amount, duration, stakePool, stakePoolMint }, extParams);
235+
const rewardPoolNonce1 = 0;
236+
const rewardPoolNonce2 = 1;
237+
const reward1Ixs = await this.prepareCreateRewardEntryInstructions({ stakePool, rewardPoolNonce: rewardPoolNonce1, depositNonce: nonce, rewardMint }, extParams);
238+
const reward2Ixs = await this.prepareCreateRewardEntryInstructions({ stakePool, rewardPoolNonce: rewardPoolNonce2, depositNonce: nonce, rewardMint }, extParams);
239+
240+
await client.execute([...stakeIxs, ...reward1Ixs, ...reward2Ixs, ], extParams);
219241
```
220242

243+
> [!NOTE]
244+
> `execute` method will bundle instructions in a transaction, estimate compute price and execute the transaction.
245+
221246
#### Unstake/Withdraw to a stake pool
222247
```typescript
223248
/*
@@ -231,6 +256,9 @@ await client.stake({ nonce, amount, duration, stakePool, stakePoolMint });
231256
*/
232257
const nonce = 0; //
233258
await client.unstake({ stakePool, stakePoolMint, nonce });
259+
260+
// Done separately, returns rent fee back to the user
261+
await client.closeStakeEntry({ stakePool, nonce })
234262
```
235263

236264
#### Claim a reward
@@ -251,6 +279,65 @@ await client.claimRewards({
251279
> For instance, prepareClaimRewardsInstructions.
252280
> These APIs allow to aggregate multiple operations in a single transaction according to the app needs.
253281
282+
### Grouped actions
283+
284+
Client also exposes methods to group staking/unstaking with reward pool actions.
285+
286+
```typescript
287+
/// Will stake into a Stake Pool and create Reward Entries for every passed pool - reward entries are used to track rewards, ideally should be created right after staking.
288+
{
289+
const { txId } = await client.stakeAndCreateEntries({
290+
stakePool,
291+
stakePoolMint: mint,
292+
amount: new BN(1),
293+
duration: new BN(0),
294+
nonce: stakeNonce,
295+
rewardPools: [{
296+
nonce: 0,
297+
mint,
298+
rewardPoolType: "fixed",
299+
}]
300+
}, extParams);
301+
console.log("Stake signature: ", txId);
302+
}
303+
304+
// Performs multiple actions needed to fully unstake:
305+
// 1. Claims all unclaimed rewards from all passed pools
306+
// 2. Unstakes from a Stake Pool and closes the stake entry
307+
// 3. Closes Reward Entries, returning the rent fee back
308+
{
309+
const { txId } = await client.unstakeAndClaim({
310+
stakePool,
311+
stakePoolMint: mint,
312+
nonce: stakeNonce,
313+
rewardPools: [{
314+
nonce: 0,
315+
mint,
316+
rewardPoolType: "fixed"
317+
}]
318+
}, extParams);
319+
console.log("Unstake signature: ", txId);
320+
}
321+
322+
// Useful when user can't claim rewards, but wants to unstake and close all created entries.
323+
{
324+
const { txId } = await client.unstakeAndClose({
325+
stakePool,
326+
stakePoolMint: mint,
327+
nonce: stakeNonce,
328+
rewardPools: [{
329+
nonce: 0,
330+
mint,
331+
rewardPoolType: "fixed"
332+
}]
333+
}, extParams);
334+
console.log("Unstake signature: ", txId);
335+
}
336+
```
337+
338+
> [!Note]
339+
> Transactions can become quite large if you have many rewards pools, that can make it impossible to do all actions in 1 transaction - in this case please stick with `prepare` methods to build custom instructions and execute them with `execute`.
340+
254341
### Set Token Metadata
255342

256343
SolanaStakingClient also exposes original IDL of all programs, so you can use some additional instructions, that are not wrapped by the client. Currently there is no method to update Token Metadata of the Staking Mint that stakers get in return for their stake, but you can call the instructions from the original IDL like so:
@@ -274,9 +361,8 @@ ix = await program.methods.setTokenMetadataT22("sToken", "sTST", "https://arweav
274361
authority: keypair.publicKey
275362
}).instruction();
276363

277-
const { tx, hash, context } = await prepareTransaction(client.connection, [ix], keypair.publicKey);
278-
const sig = await signAndExecuteTransaction(client.connection, keypair, tx, { hash, context }, {});
279-
console.log(sig);
364+
const { signature } = await client.execute([ix], {invoker: keypair});
365+
console.log(signature);
280366
```
281367

282368
## Appendix

packages/staking/__tests__/solana/rewards.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const populateRewardEntry = (
2626
new BN(0),
2727
new BN(0),
2828
new BN(0),
29+
false,
2930
[],
3031
);
3132
if (effectiveStakedAmount && rewardAmount && rewardPeriod) {

packages/staking/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/staking",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "JavaScript SDK to interact with Streamflow Staking protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",
@@ -79,8 +79,8 @@
7979
"typescript": "^5.6.3"
8080
},
8181
"dependencies": {
82-
"@coral-xyz/anchor": "^0.30.0",
83-
"@coral-xyz/borsh": "^0.30.1",
82+
"@coral-xyz/anchor": "^0.31.1",
83+
"@coral-xyz/borsh": "^0.31.1",
8484
"@solana/buffer-layout": "4.0.1",
8585
"@solana/spl-token": "0.4.9",
8686
"@solana/wallet-adapter-base": "0.9.19",

0 commit comments

Comments
 (0)