@emblemvault/primitives-stake 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/dist/pda.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * PDA derivation for the stake program.
3
+ *
4
+ * Three derivations:
5
+ * StakePool: [b"stake-pool", asset_mint, pool_id_le_u32]
6
+ * StakeVault: [b"stake-vault", pool_pda, owner]
7
+ * RewardDistribution: [b"reward-dist", pool_pda, period_id_le_u32]
8
+ *
9
+ * Mirrors sdk/sell's pattern. Rust and TS sides derive identically.
10
+ */
11
+ import { PublicKey } from '@solana/web3.js';
12
+ import { STAKE_PROGRAM_ID } from '@emblemvault/primitives-shared';
13
+ export const STAKE_POOL_SEED = Buffer.from('stake-pool');
14
+ export const STAKE_VAULT_SEED = Buffer.from('stake-vault');
15
+ export const REWARD_DIST_SEED = Buffer.from('reward-dist');
16
+ /**
17
+ * Stake program id — derived from `keys/stake.json` and emitted by
18
+ * `scripts/derive-program-ids.mjs` into `shared/src/program-ids.generated.ts`.
19
+ * Re-exported here so SDK consumers don't need to know about `shared`.
20
+ *
21
+ * The Rust program's `declare_id!()` is held in lockstep via Anchor's
22
+ * keypair-vs-source check.
23
+ */
24
+ export { STAKE_PROGRAM_ID };
25
+ export function deriveStakePoolPda(args) {
26
+ const assetKey = new PublicKey(args.asset);
27
+ const poolIdBytes = encodeU32(args.poolId, 'poolId');
28
+ const programKey = new PublicKey(args.programId);
29
+ const [pda, bump] = PublicKey.findProgramAddressSync([STAKE_POOL_SEED, assetKey.toBytes(), poolIdBytes], programKey);
30
+ return { pda, bump };
31
+ }
32
+ export function deriveStakeVaultPda(args) {
33
+ const poolKey = new PublicKey(args.poolPda);
34
+ const ownerKey = new PublicKey(args.owner);
35
+ const programKey = new PublicKey(args.programId);
36
+ const [pda, bump] = PublicKey.findProgramAddressSync([STAKE_VAULT_SEED, poolKey.toBytes(), ownerKey.toBytes()], programKey);
37
+ return { pda, bump };
38
+ }
39
+ export function deriveRewardDistPda(args) {
40
+ const poolKey = new PublicKey(args.poolPda);
41
+ const periodBytes = encodeU32(args.periodId, 'periodId');
42
+ const programKey = new PublicKey(args.programId);
43
+ const [pda, bump] = PublicKey.findProgramAddressSync([REWARD_DIST_SEED, poolKey.toBytes(), periodBytes], programKey);
44
+ return { pda, bump };
45
+ }
46
+ // ============================================================================
47
+ // helpers
48
+ // ============================================================================
49
+ /** Encode a u32 as 4 little-endian bytes with a context-aware error message. */
50
+ export function encodeU32(value, fieldName) {
51
+ if (!Number.isInteger(value) || value < 0 || value > 0xffff_ffff) {
52
+ throw new RangeError(`${fieldName} must be an unsigned 32-bit integer (0..4294967295); got ${value}`);
53
+ }
54
+ const buf = Buffer.alloc(4);
55
+ buf.writeUInt32LE(value, 0);
56
+ return buf;
57
+ }
58
+ //# sourceMappingURL=pda.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pda.js","sourceRoot":"","sources":["../src/pda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAsB,MAAM,gCAAgC,CAAC;AAEtF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAiB5B,MAAM,UAAU,kBAAkB,CAAC,IAA4B;IAC7D,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,sBAAsB,CAClD,CAAC,eAAe,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,EAClD,UAAU,CACX,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACvB,CAAC;AAYD,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,sBAAsB,CAClD,CAAC,gBAAgB,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,EACzD,UAAU,CACX,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACvB,CAAC;AAYD,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,sBAAsB,CAClD,CAAC,gBAAgB,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,EAClD,UAAU,CACX,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,SAAiB;IACxD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,WAAW,EAAE,CAAC;QACjE,MAAM,IAAI,UAAU,CAClB,GAAG,SAAS,4DAA4D,KAAK,EAAE,CAChF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Stake SDK — types.
3
+ *
4
+ * Three account types — pool config, per-user vault, per-period reward
5
+ * distribution bookkeeping. The pool's interior config (lock tiers, reward
6
+ * sources, penalty destination, reward formula) reuses the cross-primitive
7
+ * types in @emblemvault/primitives-shared.
8
+ *
9
+ * Per 03-build-plan.md Phase 3, this is the most stateful primitive. Phase
10
+ * 1+ ships the TS shape so consumers can build against the on-chain layout
11
+ * before programs/stake deploys.
12
+ */
13
+ import type { PenaltyDestination, RewardFormula, SolanaAddress, StakeLockTier, StakePoolConfig, StakeRewardSource } from '@emblemvault/primitives-shared';
14
+ /**
15
+ * The on-chain StakePoolAccount.
16
+ *
17
+ * Seeds: [b"stake-pool", asset_mint, pool_id_le_u32]
18
+ *
19
+ * The pool_id is per-(asset, authority) unique — multiple pools can stake
20
+ * the same asset under different rules (different lock tiers, different
21
+ * reward formulas, etc.).
22
+ */
23
+ export interface StakePoolAccount {
24
+ /** Wallet that created the pool — also the authority for any future updates. */
25
+ authority: SolanaAddress;
26
+ /** Per-(asset, authority) sequential pool id. */
27
+ poolId: number;
28
+ /** Token mint that gets staked into this pool. */
29
+ asset: SolanaAddress;
30
+ /** Optional dashboard URL — surfaced by the SDK in some contexts. */
31
+ hostUrl: string | null;
32
+ /** Lock tiers, ordered from shortest to longest. Max 10 (per shared limit). */
33
+ lockOptions: StakeLockTier[];
34
+ /** Min stake amount per user (base units). */
35
+ minStake: bigint;
36
+ /** Optional max per-vault cap. null = unlimited. */
37
+ maxStakePerVault: bigint | null;
38
+ /** Penalty in bps applied to early-exit unstake. */
39
+ earlyExitPenaltyBps: number;
40
+ /** Where the penalty goes (redistribute / burn / treasury PDA). */
41
+ penaltyDestination: PenaltyDestination;
42
+ /** Reward sources funding this pool. */
43
+ rewardSources: StakeRewardSource[];
44
+ /** How often distribute_rewards is callable. */
45
+ rewardCadence: 'hourly' | 'daily' | 'weekly';
46
+ /** Reward formula identifier — "linear-v1" until sammy-curve-v1 lands. */
47
+ rewardFormula: RewardFormula;
48
+ /** Total amount currently staked across all vaults. */
49
+ totalStaked: bigint;
50
+ /** Total weighted-stake (sum of stake × multiplier across all vaults). */
51
+ totalWeightedStake: bigint;
52
+ /** Period counter — increments on each distribute_rewards. */
53
+ currentPeriod: number;
54
+ /** Monotonic version, incremented on update. */
55
+ version: number;
56
+ /** Unix-seconds at creation. */
57
+ createdAt: number;
58
+ }
59
+ export interface StakePoolView extends StakePoolAccount {
60
+ poolPda: SolanaAddress;
61
+ }
62
+ /**
63
+ * The on-chain StakeVaultAccount — one per (pool, owner).
64
+ *
65
+ * Seeds: [b"stake-vault", pool_pda, owner]
66
+ */
67
+ export interface StakeVaultAccount {
68
+ /** The pool this vault belongs to. */
69
+ poolPda: SolanaAddress;
70
+ /** The wallet that owns this stake. */
71
+ owner: SolanaAddress;
72
+ /** Amount staked (base units). */
73
+ stakedAmount: bigint;
74
+ /** Index into the pool's lockOptions array. */
75
+ lockTierIdx: number;
76
+ /** Unix-seconds at which the lock ends. After this, unstake is penalty-free. */
77
+ lockEndsAt: number;
78
+ /** Accrued but-not-yet-claimed rewards (base units of asset OR reward token, depending on source). */
79
+ accruedRewards: bigint;
80
+ /** Last period this vault was credited. */
81
+ lastClaimPeriod: number;
82
+ /** Unix-seconds at last claim. */
83
+ lastClaimAt: number;
84
+ /** Unix-seconds at which the stake was first created. */
85
+ stakedAt: number;
86
+ }
87
+ export interface StakeVaultView extends StakeVaultAccount {
88
+ vaultPda: SolanaAddress;
89
+ }
90
+ /**
91
+ * Per-period bookkeeping. The program writes one of these on each
92
+ * distribute_rewards call and uses it to compute fair shares for vaults
93
+ * claiming during this period.
94
+ *
95
+ * Seeds: [b"reward-dist", pool_pda, period_id_le_u32]
96
+ */
97
+ export interface RewardDistributionAccount {
98
+ /** The pool this distribution belongs to. */
99
+ poolPda: SolanaAddress;
100
+ /** Sequential period id. */
101
+ periodId: number;
102
+ /** Total reward amount distributed during this period. */
103
+ totalDistributed: bigint;
104
+ /** Snapshot of the pool's totalWeightedStake at distribute time. */
105
+ weightedStakeAtSnapshot: bigint;
106
+ /** Unix-seconds at distribution. */
107
+ distributedAt: number;
108
+ /** The reward token mint (may differ from the staked asset). */
109
+ rewardMint: SolanaAddress;
110
+ }
111
+ export interface RewardDistributionView extends RewardDistributionAccount {
112
+ distributionPda: SolanaAddress;
113
+ }
114
+ /** Args to `createPool()` — accepts the same shape as the SDK's StakePoolConfig. */
115
+ export interface CreatePoolArgs {
116
+ config: StakePoolConfig;
117
+ /** Optional explicit pool id; if omitted, the SDK derives the next sequential value. */
118
+ poolId?: number;
119
+ }
120
+ export interface StakeArgs {
121
+ poolPda: SolanaAddress;
122
+ amount: bigint;
123
+ lockTierIdx: number;
124
+ }
125
+ export interface UnstakeArgs {
126
+ poolPda: SolanaAddress;
127
+ amount: bigint;
128
+ }
129
+ export interface ClaimRewardsArgs {
130
+ poolPda: SolanaAddress;
131
+ }
132
+ export interface DistributeRewardsArgs {
133
+ poolPda: SolanaAddress;
134
+ }
135
+ export interface GetVaultArgs {
136
+ poolPda: SolanaAddress;
137
+ /** Optional override; defaults to the configured signer. */
138
+ owner?: SolanaAddress;
139
+ }
140
+ /** Result of an off-chain "would this user pass a stake-mode gate?" check. */
141
+ export interface StakeGateEvaluationResult {
142
+ /** True iff stakedAmount >= min AND lock is still active. */
143
+ granted: boolean;
144
+ evidence: {
145
+ stakedAmount: number;
146
+ lockEndsAt: number;
147
+ /** True if lock has expired — the user could unstake now without penalty. */
148
+ lockExpired: boolean;
149
+ };
150
+ }
151
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Stake SDK — types.
3
+ *
4
+ * Three account types — pool config, per-user vault, per-period reward
5
+ * distribution bookkeeping. The pool's interior config (lock tiers, reward
6
+ * sources, penalty destination, reward formula) reuses the cross-primitive
7
+ * types in @emblemvault/primitives-shared.
8
+ *
9
+ * Per 03-build-plan.md Phase 3, this is the most stateful primitive. Phase
10
+ * 1+ ships the TS shape so consumers can build against the on-chain layout
11
+ * before programs/stake deploys.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@emblemvault/primitives-stake",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for the Emblem stake primitive — lock-up pools, reward vaults, multipliers, and early-exit penalties.",
5
+ "keywords": [
6
+ "emblem",
7
+ "emblemvault",
8
+ "solana",
9
+ "anchor",
10
+ "stake",
11
+ "staking",
12
+ "primitives"
13
+ ],
14
+ "license": "Apache-2.0",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "dist",
20
+ "LICENSE",
21
+ "README.md"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "dependencies": {
27
+ "@solana/spl-token": "^0.4.6",
28
+ "@solana/web3.js": "^1.91.0",
29
+ "@emblemvault/primitives-shared": "^0.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^20.0.0",
33
+ "typescript": "^5.4.0",
34
+ "vitest": "^1.6.0"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "test": "vitest run --passWithNoTests"
39
+ }
40
+ }