Skip to content

Commit

Permalink
Staking SDK (#199)
Browse files Browse the repository at this point in the history
* Staking SDK
  • Loading branch information
RolginRoman authored Sep 20, 2024
1 parent 1f0ce4e commit acf1db0
Show file tree
Hide file tree
Showing 30 changed files with 5,216 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

. "$(dirname -- "$0")/_/husky.sh"

npm run lint
pnpm lint --parallel
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"printWidth": 110,
"useTabs": false
}
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
}
],
"cSpell.words": [
"Streamflow"
"permissionless",
"Solana",
"Streamflow",
"Unstake"
],
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.importModuleSpecifierEnding": "js",
Expand Down
5 changes: 5 additions & 0 deletions packages/staking/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": [
"@streamflow/eslint-config"
]
}
4 changes: 4 additions & 0 deletions packages/staking/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*
!dist/**/*
!package.json
!README.md
2 changes: 2 additions & 0 deletions packages/staking/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
coverage
216 changes: 216 additions & 0 deletions packages/staking/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Streamflow Staking

## JS SDK to interact with Streamflow Staking.

This package allows you to
- `create staking pools and rewards pools`;
- `claim rewards`;
- `stake`;
- `unstake`;
- `fund rewards pools`;

with the Streamflow Staking protocol.

This protocol is the complex of several programs which ensures flexibility and accountability for stakers and respective incentives for them.

aforementioned programs are:
- Stake Pools Program
- Reward Pools Program
- Fee Management Program (for streamflow usage, non-required and ommited from further docs)


## API Reference
API Documentation available here: [docs site →](https://streamflow-finance.github.io/js-sdk/)

The Stake Pool Program entities:
1. Stake Pool
2. Stake Entry (PDA which stores info about current stakes, durations and rewards structure)

The Reward Pool Program entities:
1. Reward Pool (must has a back-reference to a stake pool)
2. Reward Entry (PDA stores claims information and time-bound params)

1 Stake Pool can have N Reward Pools.

> [!NOTE]
> There is a limitation of 255 Reward Pools per a single token mint for a stake pool.

## Installation

Install the sdk with npm

```npm
npm install @streamflow/staking
```
```yarn
yarn install @streamflow/staking
```
```pnpm
pnpm add @streamflow/staking
```

## Usage/Examples

#### Create a client
```typescript
const client = new SolanaStakingClient({
clusterUrl: "https://api.mainnet-beta.solana.com",
cluster: ICluster.Mainnet
});
```

> [!WARNING]
> All operations expect ATAs to be created at the moment of execution and don't add these instructions.
> - Stake - staker's ATAs for stake mint and stake mint (see deriveStakeMintPDA fn)
> - Withdraw/Unstake - staker's ATAs for stake mint and stake mint (see deriveStakeMintPDA fn)
> - Claim rewards - staker's ATAs for reward mint
> - Fund Reward Pool - signer creates Streamflow Treasury's ATA for holding fee if defined
#### Read operations
```typescript

await client.searchStakePools({ mint, creator }) // returns results of lookup, `mint` and `creator` both optional. Omit the argument to get all pools

await client.searchStakeEntries({ payer, stakePool }) // returns all stake entries. Omit the argument to get all.

await client.searchRewardPools({ stakePool, mint })

await client.searchRewardEntries({ stakeEntry, rewardPool })

```

#### Create a staking pool

```typescript
const client = new SolanaStakingClient({
clusterUrl: "https://api.mainnet-beta.solana.com",
cluster: ICluster.Mainnet
});
/*
Rewards Multiplier powered by 10^9.
Example: if multiplier is 2_000_000_000 than stakes for maxDuration will have 2x more rewards than stakes for minDuration
*/
const multiplier = new BN(1_000_000_000);
/*
30 days - Unix time in seconds
*/
const maxDuration = new BN(2592000);
/*
1 day - Unix time in seconds
*/
const maxDuration = new BN(86400);
/*
Limits signers that can create/assign reward pools to this stake pool. True - anyone can
*/
const permissionless = false;
/*
[0;256) derive stake pool PDA account address.
If stake pool with the same mint already exists, it is required to pick a vacant nonce
*/
const nonce = 0;

const { metadataId: stakePoolPda } = await client.createStakePool({
maxWeight: multiplier,
maxDuration,
minDuration,
mint: MINT_ADDRESS,
permissionless,
nonce:
})

```

#### Create a rewardPool pool
```typescript
/*
[0;256) derive reward pool PDA account address.
If reward pool with the same mint already exists, it is required to pick a vacant nonce
*/
const nonce = 0;
/*
Amount of rewarding tokens stakers get in return for staking exactly 1 token to the staking pool
*/
const rewardAmount = new BN(100);
/*
1 day - Unix time in seconds. Period for rewarding stakers. Period starts counting from the moment of staking
*/
const rewardPeriod = new BN(86400);
const rewardMint = REWARD_MINT_ADDRESS; // rewarding token
/*
Whether to allow anyone to fund this reward pool. If true anyone can fund, otherwise only the creator can
*/
const permissionless = true;

client.createRewardPool({
nonce,
rewardAmount,
rewardPeriod,
rewardMint,
permissionless = false,
stakePool: stakePoolPda,
stakePoolMint: MINT_ADDRESS,
})
```


#### Deposit/Stake to a stake pool
```typescript
/*
[0;256) derive stake entry PDA account address.
If stake entry with the same nonce already exists, it is required to pick a vacant one
*/
const nonce = 0;
const amount = new BN(1000); // tokens to stake
const duration = new BN(86400 * 2) // 2 days, must be in the range of stakePool's min and max durations
await client.stake({ nonce, amount, duration, stakePool, stakePoolMint });
```

#### Unstake/Withdraw to a stake pool
```typescript
/*
Usually to achieve this the app already loaded available stakeEntries.
Stake Entry holds used `nonce`, so `nonce` below could be taken from the stake entry
*/

/*
[0;256) derived stake entry PDA account address.
If stake entry with the same nonce already exists, it is required to pick a vacant one
*/
const nonce = 0; //
await client.unstake({ stakePool, stakePoolMint, nonce });
```

#### Claim a reward
Since each stake entry can produce multiple rewards (single claim per each reward pool linked to the staking pool) this operation
can be triggered for every reward pool separately.

```typescript
await client.claimRewards({
rewardPoolNonce,
depositNonce,
stakePool,
rewardMint,
});
```

> [!Note]
> All operations have accompanying APIs for manual transaction building. Consult with API docs to find respective calls.
> For instance, prepareClaimRewardsInstructions.
> These APIs allow to aggregate multiple operations in a single transaction according to the app needs.
## Appendix

Streamflow Staking protocol program IDs

| Solana | |
| ------- | -------------------------------------------- |
| Staking Pools Mainnet | STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH |
| Reward Pools Mainnet | RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj |
| ---- | --- |
| Staking Pools Devnet | STAKEvGqQTtzJZH6BWDcbpzXXn2BBerPAgQ3EGLN2GH |
| Reward Pools Devnet | RWRDdfRbi3339VgKxTAXg4cjyniF7cbhNbMxZWiSKmj |

### IDLs
For further details you can consult with IDLs of protocols available at:
`@streamflow/staking/dist/esm/solana/descriptor`
6 changes: 6 additions & 0 deletions packages/staking/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { default as SolanaStakingClient } from "./solana/client.js";
export * from "./solana/utils.js";
export * from "./solana/types.js";
export * from "./solana/lib/derive-accounts.js";
export * from "./solana/lib/rewards.js";
export * as constants from "./solana/constants.js";
49 changes: 49 additions & 0 deletions packages/staking/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@streamflow/staking",
"version": "7.0.0-alpha.1",
"description": "JavaScript SDK to interact with Streamflow Staking protocol.",
"homepage": "https://github.com/streamflow-finance/js-sdk/",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/esm/index.d.ts"
},
"./solana": {
"import": "./dist/esm/solana/index.js",
"require": "./dist/cjs/solana/index.js",
"types": "./dist/esm/solana/index.d.ts"
}
},
"scripts": {
"build:cjs": "rm -rf dist/cjs; tsc -p tsconfig.cjs.json",
"build:esm": "rm -rf dist/esm; tsc -p tsconfig.esm.json",
"build": "rm -rf dist; pnpm run build:cjs && pnpm run build:esm",
"pack": "pnpm build && pnpm pack",
"lint": "eslint --fix .",
"prepublishOnly": "npm run lint && npm run build"
},
"gitHead": "a37306eba0e762af096db642fa22f07194014cfd",
"devDependencies": {
"@streamflow/eslint-config": "workspace:*",
"@types/bn.js": "5.1.1",
"date-fns": "2.28.0",
"typescript": "^4.9.5"
},
"dependencies": {
"@coral-xyz/borsh": "^0.29.0",
"@coral-xyz/anchor": "^0.30.0",
"@solana/buffer-layout": "4.0.1 ",
"@solana/spl-token": "0.3.6",
"@solana/wallet-adapter-base": "0.9.19",
"@solana/web3.js": "1.70.1",
"@streamflow/common": "workspace:*",
"bn.js": "5.2.1",
"borsh": "^2.0.0",
"bs58": "5.0.0",
"p-queue": "^8.0.1"
}
}
Loading

0 comments on commit acf1db0

Please sign in to comment.