@star-factory/sdk-staking 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -0
- package/dist/cli/set-allowed-mint.d.ts +2 -0
- package/dist/cli/set-allowed-mint.d.ts.map +1 -0
- package/dist/cli/set-allowed-mint.js +78 -0
- package/dist/cli/set-allowed-mint.js.map +1 -0
- package/dist/idl/staking.json +1407 -0
- package/dist/index.d.ts +141 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +598 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +380 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +35 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
### sdk-staking
|
|
2
|
+
|
|
3
|
+
Typed client for the on-chain staking program. It mirrors PDA seeds and instruction accounts, and adds read and convenience helpers.
|
|
4
|
+
|
|
5
|
+
- **Entry**: `sdk-staking/index.ts`
|
|
6
|
+
- **Types**: `sdk-staking/types.ts`
|
|
7
|
+
- **Peer deps**: `@coral-xyz/anchor`, `@solana/spl-token`
|
|
8
|
+
|
|
9
|
+
### Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
yarn add @coral-xyz/anchor @solana/spl-token
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { AnchorProvider, BN } from '@coral-xyz/anchor';
|
|
19
|
+
import { PublicKey } from '@solana/web3.js';
|
|
20
|
+
import { StakingSDK } from '.';
|
|
21
|
+
|
|
22
|
+
const provider = AnchorProvider.env();
|
|
23
|
+
const sdk = await StakingSDK.create(provider); // or pass custom programId
|
|
24
|
+
|
|
25
|
+
// Initialize pool (authority must sign)
|
|
26
|
+
await sdk.initialize(new PublicKey('...accepted_mint...'));
|
|
27
|
+
|
|
28
|
+
// Stake
|
|
29
|
+
await sdk.stake(new BN(1_000_000));
|
|
30
|
+
|
|
31
|
+
// Unstake (starts 30-day unlock)
|
|
32
|
+
await sdk.unstake(new BN(500_000));
|
|
33
|
+
|
|
34
|
+
// Claim when eligible
|
|
35
|
+
if (await sdk.canClaimUnstake()) {
|
|
36
|
+
await sdk.claimUnstake();
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### API
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
class StakingSDK {
|
|
44
|
+
constructor(program: Program<StakeProgram>, provider: AnchorProvider);
|
|
45
|
+
static create(provider: AnchorProvider, programId?: PublicKey): Promise<StakingSDK>;
|
|
46
|
+
|
|
47
|
+
// PDAs
|
|
48
|
+
getStakePoolPDA(): PDAWithBump; // [b"stake_pool"]
|
|
49
|
+
getUserStakePDA(user: Address): PDAWithBump; // [b"user_stake", user]
|
|
50
|
+
getStakeVaultPDA(stakePool: Address): PDAWithBump; // [b"stake_vault", stake_pool]
|
|
51
|
+
|
|
52
|
+
// Writes
|
|
53
|
+
initialize(acceptedMint: Address, authority?: Address): Promise<TransactionSignature>;
|
|
54
|
+
stake(amount: Amount, user?: Address): Promise<TransactionSignature>;
|
|
55
|
+
unstake(amount: Amount, user?: Address): Promise<TransactionSignature>;
|
|
56
|
+
claimUnstake(user?: Address): Promise<TransactionSignature>;
|
|
57
|
+
|
|
58
|
+
// Reads
|
|
59
|
+
getStakePool(): Promise<StakePoolAccount>;
|
|
60
|
+
getUserStake(user?: Address): Promise<UserStakeAccount | null>;
|
|
61
|
+
|
|
62
|
+
// Derived helpers
|
|
63
|
+
getUserPoolShare(user?: Address): Promise<number>; // percentage 0..100
|
|
64
|
+
canClaimUnstake(user?: Address): Promise<boolean>;
|
|
65
|
+
getTimeUntilClaim(user?: Address): Promise<number | null>; // seconds
|
|
66
|
+
|
|
67
|
+
// Events
|
|
68
|
+
onStakeEvent(cb: EventCallback<StakeEvent>): ListenerId;
|
|
69
|
+
onUnstakeEvent(cb: EventCallback<UnstakeEvent>): ListenerId;
|
|
70
|
+
onClaimUnstakeEvent(cb: EventCallback<ClaimUnstakeEvent>): ListenerId;
|
|
71
|
+
removeEventListener(listenerId: ListenerId): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Types (subset)
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
type Address = web3.PublicKey;
|
|
79
|
+
type Amount = BN;
|
|
80
|
+
|
|
81
|
+
interface StakePoolAccount { acceptedMint: Address; totalStaked: BN }
|
|
82
|
+
interface UserStakeAccount { stakedAmount: BN; pendingUnstake: BN; unlockTimestamp: BN }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Behavior notes
|
|
86
|
+
|
|
87
|
+
- Loads IDL from `../target/idl/stake_program.json`; use `StakingSDK.create(provider, programId)` to override address.
|
|
88
|
+
- Uses `.accountsPartial(...)` (Anchor v0.29+). If needed, switch to `.accounts(...)`.
|
|
89
|
+
- `getUserStake()` returns `null` if the PDA is not initialized.
|
|
90
|
+
- `getUserPoolShare()` computes `(stakedAmount / totalStaked) * 100` with two-decimal precision.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Staker Snapshot Script
|
|
95
|
+
|
|
96
|
+
Creates a snapshot of all stakers for pro-rata reward distribution. Outputs CSV compatible with the airdrop script.
|
|
97
|
+
|
|
98
|
+
### Usage
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# From sdk-staking directory
|
|
102
|
+
yarn snapshot --supply <AMOUNT> [options]
|
|
103
|
+
|
|
104
|
+
# Options:
|
|
105
|
+
# --supply <amount> Total reward tokens to distribute (whole tokens)
|
|
106
|
+
# --decimals <n> Reward token decimals (default: 6 for USDC)
|
|
107
|
+
# --output <path> Output CSV path (default: ./staker-snapshot.csv)
|
|
108
|
+
# --rpc <url> RPC endpoint (default: $RPC_URL or localhost)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Examples
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Distribute 1M USDC (6 decimals) to stakers
|
|
115
|
+
yarn snapshot --supply 1000000 --decimals 6 --output ./usdc-rewards.csv
|
|
116
|
+
|
|
117
|
+
# Distribute 500K tokens with 9 decimals on mainnet
|
|
118
|
+
yarn snapshot --supply 500000 --decimals 9 --rpc https://api.mainnet-beta.solana.com
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Output
|
|
122
|
+
|
|
123
|
+
Creates three files:
|
|
124
|
+
- `staker-snapshot.csv` - `wallet,amount` format for airdrop script
|
|
125
|
+
- `staker-snapshot-detailed.csv` - Full allocation details (staked amounts, percentages)
|
|
126
|
+
- `staker-snapshot-summary.json` - Verification data and audit trail
|
|
127
|
+
|
|
128
|
+
### Features
|
|
129
|
+
|
|
130
|
+
- **100% integer arithmetic** - No floating point for token amounts
|
|
131
|
+
- **Verified math** - `sum(distributed) + remainder = total_supply`
|
|
132
|
+
- **Deterministic ordering** - Sorted by wallet address for reproducibility
|
|
133
|
+
- **Randomized output** - CSV is shuffled so whales aren't airdropped first
|
|
134
|
+
|
|
135
|
+
### Programmatic Usage
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { StakingSDK } from '@star-vault/sdk-staking';
|
|
139
|
+
|
|
140
|
+
const sdk = await StakingSDK.create(provider);
|
|
141
|
+
|
|
142
|
+
// Get all stakers
|
|
143
|
+
const { stakers, totalStaked, activeStakers } = await sdk.getAllStakers();
|
|
144
|
+
|
|
145
|
+
// Each staker has:
|
|
146
|
+
// - wallet: PublicKey
|
|
147
|
+
// - stakedAmount: BN
|
|
148
|
+
// - pendingUnstake: BN
|
|
149
|
+
// - unlockTimestamp: BN
|
|
150
|
+
```
|
|
151
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-allowed-mint.d.ts","sourceRoot":"","sources":["../../src/cli/set-allowed-mint.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const index_1 = require("../index");
|
|
7
|
+
function getFlagValue(flag, argv) {
|
|
8
|
+
const index = argv.indexOf(flag);
|
|
9
|
+
if (index === -1)
|
|
10
|
+
return undefined;
|
|
11
|
+
return argv[index + 1];
|
|
12
|
+
}
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const mint = getFlagValue('--mint', argv);
|
|
15
|
+
const programId = getFlagValue('--program-id', argv);
|
|
16
|
+
const keypair = getFlagValue('--keypair', argv);
|
|
17
|
+
const url = getFlagValue('--url', argv);
|
|
18
|
+
if (!mint) {
|
|
19
|
+
throw new Error('--mint <ADDRESS> is required');
|
|
20
|
+
}
|
|
21
|
+
return { mint, programId, keypair, url };
|
|
22
|
+
}
|
|
23
|
+
function expandHome(path) {
|
|
24
|
+
if (path.startsWith('~')) {
|
|
25
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
26
|
+
return (home ? path.replace('~', home) : path);
|
|
27
|
+
}
|
|
28
|
+
if (path.startsWith('${HOME}')) {
|
|
29
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
30
|
+
return (home ? path.replace('${HOME}', home) : path);
|
|
31
|
+
}
|
|
32
|
+
return path;
|
|
33
|
+
}
|
|
34
|
+
function loadKeypairFromFile(filePath) {
|
|
35
|
+
const expanded = expandHome(filePath);
|
|
36
|
+
if (!(0, fs_1.existsSync)(expanded)) {
|
|
37
|
+
throw new Error(`Keypair file not found: ${expanded}`);
|
|
38
|
+
}
|
|
39
|
+
const raw = (0, fs_1.readFileSync)(expanded, { encoding: 'utf-8' });
|
|
40
|
+
const secret = JSON.parse(raw);
|
|
41
|
+
return web3_js_1.Keypair.fromSecretKey(Uint8Array.from(secret));
|
|
42
|
+
}
|
|
43
|
+
async function createProvider(args) {
|
|
44
|
+
if (!args.keypair && !args.url) {
|
|
45
|
+
return anchor_1.AnchorProvider.env();
|
|
46
|
+
}
|
|
47
|
+
const connection = new web3_js_1.Connection(args.url ?? (0, web3_js_1.clusterApiUrl)('devnet'), {
|
|
48
|
+
commitment: 'confirmed',
|
|
49
|
+
confirmTransactionInitialTimeout: 60_000,
|
|
50
|
+
});
|
|
51
|
+
const kp = args.keypair ? loadKeypairFromFile(args.keypair) : undefined;
|
|
52
|
+
const wallet = kp ? new anchor_1.Wallet(kp) : anchor_1.AnchorProvider.env().wallet;
|
|
53
|
+
return new anchor_1.AnchorProvider(connection, wallet, {
|
|
54
|
+
commitment: 'confirmed',
|
|
55
|
+
preflightCommitment: 'confirmed',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function main() {
|
|
59
|
+
const argv = process.argv.slice(2);
|
|
60
|
+
const args = parseArgs(argv);
|
|
61
|
+
const acceptedMint = new web3_js_1.PublicKey(args.mint);
|
|
62
|
+
const programId = args.programId ? new web3_js_1.PublicKey(args.programId) : undefined;
|
|
63
|
+
const provider = await createProvider(args);
|
|
64
|
+
const sdk = await index_1.StakingSDK.create(provider, programId);
|
|
65
|
+
const { signature: sig } = await sdk.initialize({ acceptedMint });
|
|
66
|
+
// eslint-disable-next-line no-console
|
|
67
|
+
console.log('Set allowed mint via initialize:', sig);
|
|
68
|
+
// Verify and print resulting pool state
|
|
69
|
+
const pool = await sdk.getStakePool();
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.log('Accepted mint:', pool.acceptedMint.toBase58());
|
|
72
|
+
}
|
|
73
|
+
main().catch((err) => {
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.error(err?.stack || err?.message || String(err));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=set-allowed-mint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-allowed-mint.js","sourceRoot":"","sources":["../../src/cli/set-allowed-mint.ts"],"names":[],"mappings":"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"}
|