@emblemvault/primitives-stake 0.5.0 → 0.7.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/client.d.ts +178 -30
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +370 -59
- package/dist/client.js.map +1 -1
- package/dist/codec.d.ts +52 -44
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +193 -68
- package/dist/codec.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/pda.d.ts +39 -3
- package/dist/pda.d.ts.map +1 -1
- package/dist/pda.js +51 -4
- package/dist/pda.js.map +1 -1
- package/dist/types.d.ts +63 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction, } from '@solana/web3.js';
|
|
19
19
|
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, } from '@solana/spl-token';
|
|
20
20
|
import { EmblemError, decodeSignedPayload, } from '@emblemvault/primitives-shared';
|
|
21
|
-
import { decodeRewardDistributionAccount, decodeStakePoolAccount, decodeStakeVaultAccount, MULTIPLIER_SCALE, PENALTY_TAG_BURN, PENALTY_TAG_REDISTRIBUTE, PENALTY_TAG_TREASURY_PDA, REWARD_CADENCE_DAILY, REWARD_CADENCE_HOURLY, REWARD_CADENCE_WEEKLY, REWARD_FORMULA_LINEAR_V1, REWARD_FORMULA_TIME_WEIGHTED_V1, REWARD_SOURCE_TAG_DIST_VAULT, REWARD_SOURCE_TAG_SPLIT_RECIPIENT, STAKE_POOL_DISCRIMINATOR, STAKE_VAULT_DISCRIMINATOR, } from './codec.js';
|
|
21
|
+
import { decodePlatformAccrualAccount, decodeRewardDistributionAccount, decodeStakePlatformConfigAccount, decodeStakePoolAccount, decodeStakeVaultAccount, MULTIPLIER_SCALE, PENALTY_TAG_BURN, PENALTY_TAG_REDISTRIBUTE, PENALTY_TAG_TREASURY_PDA, REWARD_CADENCE_DAILY, REWARD_CADENCE_HOURLY, REWARD_CADENCE_WEEKLY, REWARD_DIST_DISCRIMINATOR, REWARD_FORMULA_LINEAR_V1, REWARD_FORMULA_TIME_WEIGHTED_V1, REWARD_SOURCE_TAG_DIST_VAULT, REWARD_SOURCE_TAG_SPLIT_RECIPIENT, STAKE_POOL_DISCRIMINATOR, STAKE_VAULT_DISCRIMINATOR, } from './codec.js';
|
|
22
22
|
import { deriveRewardDistPda, deriveStakePoolPda, deriveStakeVaultPda, STAKE_PROGRAM_ID, } from './pda.js';
|
|
23
23
|
// =============================================================================
|
|
24
24
|
// Anchor instruction discriminators
|
|
@@ -26,15 +26,19 @@ import { deriveRewardDistPda, deriveStakePoolPda, deriveStakeVaultPda, STAKE_PRO
|
|
|
26
26
|
const IX_CREATE_POOL = Buffer.from([
|
|
27
27
|
0xe9, 0x92, 0xd1, 0x8e, 0xcf, 0x68, 0x40, 0xbc,
|
|
28
28
|
]);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
// Bundle 9+232 (2026-05-06): IX_REGISTER_REWARD_VAULT deleted with the
|
|
30
|
+
// Tier 2 tombstone. Per-stream reward vaults are init'd via
|
|
31
|
+
// IX_REGISTER_POOL_REWARD_MINT below.
|
|
32
32
|
const IX_REGISTER_PENDING_ACTION_VAULT = Buffer.from([
|
|
33
33
|
0x5b, 0x60, 0x22, 0xa1, 0x04, 0x7c, 0xa3, 0x62,
|
|
34
34
|
]);
|
|
35
35
|
const IX_REGISTER_POOL_TOKEN_ACCOUNTS = Buffer.from([
|
|
36
36
|
0x3c, 0xd3, 0x00, 0x47, 0x80, 0x7b, 0xc8, 0x52,
|
|
37
37
|
]);
|
|
38
|
+
/** Bundle 9+232 (2026-05-06) — sha256("global:register_pool_reward_mint"). */
|
|
39
|
+
const IX_REGISTER_POOL_REWARD_MINT = Buffer.from([
|
|
40
|
+
0xd3, 0xa8, 0x16, 0xd6, 0xda, 0xfb, 0xea, 0x78,
|
|
41
|
+
]);
|
|
38
42
|
const IX_STAKE = Buffer.from([
|
|
39
43
|
0xce, 0xb0, 0xca, 0x12, 0xc8, 0xd1, 0xb3, 0x6c,
|
|
40
44
|
]);
|
|
@@ -47,6 +51,14 @@ const IX_CLAIM = Buffer.from([
|
|
|
47
51
|
const IX_DISTRIBUTE_REWARDS = Buffer.from([
|
|
48
52
|
0x61, 0x06, 0xe3, 0xff, 0x7c, 0xa5, 0x03, 0x94,
|
|
49
53
|
]);
|
|
54
|
+
/** Bundle 9+232 (2026-05-06) — sha256("global:set_platform_fee_bps"). */
|
|
55
|
+
const IX_SET_PLATFORM_FEE_BPS = Buffer.from([
|
|
56
|
+
0x99, 0x37, 0xb5, 0x3e, 0x41, 0xfc, 0xf7, 0xdb,
|
|
57
|
+
]);
|
|
58
|
+
/** Bundle 9+232 (2026-05-06) — sha256("global:emblem_claim_platform_fee"). */
|
|
59
|
+
const IX_EMBLEM_CLAIM_PLATFORM_FEE = Buffer.from([
|
|
60
|
+
0x11, 0x6d, 0x7e, 0xd1, 0xd5, 0x67, 0xa7, 0x97,
|
|
61
|
+
]);
|
|
50
62
|
// ST-2 follow-on (2026-05-04): platform-config + emergency-halt ixs.
|
|
51
63
|
// All discriminators verified via Node crypto sha256("global:<name>").subarray(0, 8).
|
|
52
64
|
const IX_INIT_PLATFORM_CONFIG = Buffer.from([
|
|
@@ -166,13 +178,15 @@ function encodeRewardFormula(f) {
|
|
|
166
178
|
function deriveVaultAuthority(programId) {
|
|
167
179
|
return PublicKey.findProgramAddressSync([Buffer.from('vault-authority')], programId)[0];
|
|
168
180
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
/** Bundle 9+232 (2026-05-06) — per-mint reward vault. Pre-bundle
|
|
182
|
+
* single-arg helpers (`deriveRewardVault(vaultPubkey, ...)` and
|
|
183
|
+
* `deriveRewardVaultForPool(pool, ...)`) are gone — the seed shape is
|
|
184
|
+
* now `[b"reward-vault", pool, reward_mint]`. */
|
|
185
|
+
function deriveRewardVaultForMint(poolPda, rewardMint, programId) {
|
|
186
|
+
return PublicKey.findProgramAddressSync([Buffer.from('reward-vault'), poolPda.toBytes(), rewardMint.toBytes()], programId)[0];
|
|
172
187
|
}
|
|
173
|
-
function
|
|
174
|
-
|
|
175
|
-
return PublicKey.findProgramAddressSync([Buffer.from('reward-vault'), poolPda.toBytes()], programId)[0];
|
|
188
|
+
function derivePlatformAccrualForMint(poolPda, rewardMint, programId) {
|
|
189
|
+
return PublicKey.findProgramAddressSync([Buffer.from('platform-accrual'), poolPda.toBytes(), rewardMint.toBytes()], programId)[0];
|
|
176
190
|
}
|
|
177
191
|
function derivePendingActionVault(configId, mint, actionType, programId) {
|
|
178
192
|
return PublicKey.findProgramAddressSync([
|
|
@@ -223,14 +237,35 @@ export class EmblemStakeClient {
|
|
|
223
237
|
});
|
|
224
238
|
return { vaultPda: pda.toBase58(), bump };
|
|
225
239
|
}
|
|
240
|
+
/** Bundle 9+232 (2026-05-06) — RewardDistribution PDA now per
|
|
241
|
+
* `(pool, rewardMint, periodId)`. */
|
|
226
242
|
deriveDistributionPda(args) {
|
|
227
243
|
const { pda, bump } = deriveRewardDistPda({
|
|
228
244
|
poolPda: args.poolPda,
|
|
245
|
+
rewardMint: args.rewardMint,
|
|
229
246
|
periodId: args.periodId,
|
|
230
247
|
programId: this.programId.toBase58(),
|
|
231
248
|
});
|
|
232
249
|
return { distributionPda: pda.toBase58(), bump };
|
|
233
250
|
}
|
|
251
|
+
/** Bundle 9+232 (2026-05-06) — per-mint reward vault address. */
|
|
252
|
+
deriveRewardVaultPda(args) {
|
|
253
|
+
const [pda, bump] = PublicKey.findProgramAddressSync([
|
|
254
|
+
Buffer.from('reward-vault'),
|
|
255
|
+
new PublicKey(args.poolPda).toBytes(),
|
|
256
|
+
new PublicKey(args.rewardMint).toBytes(),
|
|
257
|
+
], this.programId);
|
|
258
|
+
return { rewardVaultPda: pda.toBase58(), bump };
|
|
259
|
+
}
|
|
260
|
+
/** Bundle 9+232 (2026-05-06) — per-stream platform accrual PDA. */
|
|
261
|
+
derivePlatformAccrualPda(args) {
|
|
262
|
+
const [pda, bump] = PublicKey.findProgramAddressSync([
|
|
263
|
+
Buffer.from('platform-accrual'),
|
|
264
|
+
new PublicKey(args.poolPda).toBytes(),
|
|
265
|
+
new PublicKey(args.rewardMint).toBytes(),
|
|
266
|
+
], this.programId);
|
|
267
|
+
return { accrualPda: pda.toBase58(), bump };
|
|
268
|
+
}
|
|
234
269
|
/** ST-2 follow-on (2026-05-04). The stake program's PlatformConfig
|
|
235
270
|
* PDA is a singleton — same shape as launch + sell. */
|
|
236
271
|
derivePlatformConfigPda() {
|
|
@@ -609,6 +644,10 @@ export class EmblemStakeClient {
|
|
|
609
644
|
// Convert decimal multiplier (1.5) to fixed-point ×1000 (1500)
|
|
610
645
|
multiplier: Math.round(t.multiplier * MULTIPLIER_SCALE),
|
|
611
646
|
}));
|
|
647
|
+
// Bundle 9+232 (2026-05-06) — initial reward mints registered at
|
|
648
|
+
// create_pool time. Empty Vec is valid; pool authority can append
|
|
649
|
+
// later via register_pool_reward_mint.
|
|
650
|
+
const rewardMints = (args.initialRewardMints ?? []).map((m) => new PublicKey(m));
|
|
612
651
|
const data = Buffer.concat([
|
|
613
652
|
IX_CREATE_POOL,
|
|
614
653
|
u32(poolId),
|
|
@@ -626,6 +665,9 @@ export class EmblemStakeClient {
|
|
|
626
665
|
...cfg.rewardSources.map(encodeRewardSource),
|
|
627
666
|
Buffer.from([encodeRewardCadence(cfg.rewardCadence)]),
|
|
628
667
|
Buffer.from([encodeRewardFormula(cfg.rewardFormula)]),
|
|
668
|
+
// Bundle 9+232 — reward_mints: Vec<Pubkey>
|
|
669
|
+
u32(rewardMints.length),
|
|
670
|
+
...rewardMints.map((m) => Buffer.from(m.toBytes())),
|
|
629
671
|
]);
|
|
630
672
|
return new TransactionInstruction({
|
|
631
673
|
programId: this.programId,
|
|
@@ -638,31 +680,105 @@ export class EmblemStakeClient {
|
|
|
638
680
|
});
|
|
639
681
|
}
|
|
640
682
|
/**
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* `
|
|
645
|
-
*
|
|
683
|
+
* Bundle 9+232 (2026-05-06) — register a reward token stream on an
|
|
684
|
+
* existing pool. Inits the per-mint reward vault PDA + the stream's
|
|
685
|
+
* PlatformAccrual PDA, and appends `reward_mint` to
|
|
686
|
+
* `pool.reward_mints`. Authority-only on chain (`has_one = authority`).
|
|
687
|
+
*
|
|
688
|
+
* Used by the `createPool()` self-submitting wrapper to chain one of
|
|
689
|
+
* these per `initialRewardMints` entry — but exposed here so callers
|
|
690
|
+
* can register additional streams later.
|
|
646
691
|
*/
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
const
|
|
692
|
+
buildRegisterPoolRewardMintIx(args) {
|
|
693
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
694
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
650
695
|
const vaultAuthority = deriveVaultAuthority(this.programId);
|
|
651
|
-
const
|
|
696
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
697
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
652
698
|
return new TransactionInstruction({
|
|
653
699
|
programId: this.programId,
|
|
654
700
|
keys: [
|
|
655
|
-
{ pubkey: new PublicKey(args.
|
|
656
|
-
{ pubkey:
|
|
701
|
+
{ pubkey: new PublicKey(args.payer), isSigner: true, isWritable: true },
|
|
702
|
+
{ pubkey: new PublicKey(args.authority), isSigner: true, isWritable: false },
|
|
703
|
+
{ pubkey: poolKey, isSigner: false, isWritable: true },
|
|
704
|
+
{ pubkey: mintKey, isSigner: false, isWritable: false },
|
|
657
705
|
{ pubkey: vaultAuthority, isSigner: false, isWritable: false },
|
|
658
|
-
{ pubkey:
|
|
706
|
+
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
707
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
659
708
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
660
709
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
661
710
|
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
|
|
662
711
|
],
|
|
663
|
-
data: Buffer.concat([
|
|
712
|
+
data: Buffer.concat([IX_REGISTER_POOL_REWARD_MINT, mintKey.toBuffer()]),
|
|
664
713
|
});
|
|
665
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Bundle 9+232 (2026-05-06) — admin sets the protocol fee on reward
|
|
717
|
+
* distributions, in basis points. Capped at
|
|
718
|
+
* `STAKE_PLATFORM_FEE_BPS_MAX = 3_000` (30%) by the on-chain ix.
|
|
719
|
+
* Mainnet ships at 0.
|
|
720
|
+
*/
|
|
721
|
+
buildSetPlatformFeeBpsIx(args) {
|
|
722
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
723
|
+
const data = Buffer.concat([IX_SET_PLATFORM_FEE_BPS, u16(args.feeBps)]);
|
|
724
|
+
return new TransactionInstruction({
|
|
725
|
+
programId: this.programId,
|
|
726
|
+
keys: [
|
|
727
|
+
{ pubkey: new PublicKey(args.admin), isSigner: true, isWritable: false },
|
|
728
|
+
{
|
|
729
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
730
|
+
isSigner: false,
|
|
731
|
+
isWritable: true,
|
|
732
|
+
},
|
|
733
|
+
],
|
|
734
|
+
data,
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Bundle 9+232 (2026-05-06) — drain accrued platform fees from one
|
|
739
|
+
* reward stream's `PlatformAccrual.pending` into the admin's treasury
|
|
740
|
+
* ATA. Admin-signed (`has_one = admin` on `PlatformConfig`); body
|
|
741
|
+
* also asserts `treasury.owner == admin` so the admin can't route
|
|
742
|
+
* Emblem fees to a third-party wallet. Idempotent-error at
|
|
743
|
+
* `pending == 0` with `StakePlatformAccrualNothingPending (519)`.
|
|
744
|
+
*/
|
|
745
|
+
buildEmblemClaimPlatformFeeIx(args) {
|
|
746
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
747
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
748
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
749
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
750
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
751
|
+
const vaultAuthority = deriveVaultAuthority(this.programId);
|
|
752
|
+
const data = Buffer.concat([
|
|
753
|
+
IX_EMBLEM_CLAIM_PLATFORM_FEE,
|
|
754
|
+
mintKey.toBuffer(),
|
|
755
|
+
]);
|
|
756
|
+
return new TransactionInstruction({
|
|
757
|
+
programId: this.programId,
|
|
758
|
+
keys: [
|
|
759
|
+
{ pubkey: new PublicKey(args.admin), isSigner: true, isWritable: false },
|
|
760
|
+
{
|
|
761
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
762
|
+
isSigner: false,
|
|
763
|
+
isWritable: false,
|
|
764
|
+
},
|
|
765
|
+
{ pubkey: poolKey, isSigner: false, isWritable: false },
|
|
766
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
767
|
+
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
768
|
+
{
|
|
769
|
+
pubkey: new PublicKey(args.treasuryTokenAccount),
|
|
770
|
+
isSigner: false,
|
|
771
|
+
isWritable: true,
|
|
772
|
+
},
|
|
773
|
+
{ pubkey: vaultAuthority, isSigner: false, isWritable: false },
|
|
774
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
775
|
+
],
|
|
776
|
+
data,
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
// Bundle 9+232 (2026-05-06): buildRegisterRewardVaultIx deleted with
|
|
780
|
+
// the Tier 2 tombstone. Use buildRegisterPoolRewardMintIx for per-stream
|
|
781
|
+
// reward vaults instead.
|
|
666
782
|
/** Build the register_pending_action_vault instruction (Tier 3 settlement bucket). */
|
|
667
783
|
buildRegisterPendingActionVaultIx(args) {
|
|
668
784
|
const mint = new PublicKey(args.mint);
|
|
@@ -687,17 +803,15 @@ export class EmblemStakeClient {
|
|
|
687
803
|
]),
|
|
688
804
|
});
|
|
689
805
|
}
|
|
690
|
-
/** Address of the staker-pool reward vault (Tier 2 destination for StakerPool recipients). */
|
|
691
|
-
deriveStakerPoolRewardVault(poolPda) {
|
|
692
|
-
return deriveRewardVaultForPool(new PublicKey(poolPda), this.programId).toBase58();
|
|
693
|
-
}
|
|
694
806
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
807
|
+
* Bundle 9+232 (2026-05-06) — per-mint reward vault address. Pre-bundle
|
|
808
|
+
* `deriveStakerPoolRewardVault(pool)` (single-stream) and
|
|
809
|
+
* `deriveDistributionVaultAddress(vaultPubkey)` (Tier 2 tombstone)
|
|
810
|
+
* helpers are gone — see `deriveRewardVaultPda` above for the canonical
|
|
811
|
+
* `(pool, rewardMint)` derivation.
|
|
698
812
|
*/
|
|
699
|
-
|
|
700
|
-
return
|
|
813
|
+
deriveStakerPoolRewardVault(args) {
|
|
814
|
+
return deriveRewardVaultForMint(new PublicKey(args.poolPda), new PublicKey(args.rewardMint), this.programId).toBase58();
|
|
701
815
|
}
|
|
702
816
|
/** Address of a Tier 3 pending-action vault. */
|
|
703
817
|
derivePendingActionVaultAddress(args) {
|
|
@@ -720,20 +834,27 @@ export class EmblemStakeClient {
|
|
|
720
834
|
new PublicKey(args.owner).toBytes(),
|
|
721
835
|
], this.programId)[0].toBase58();
|
|
722
836
|
}
|
|
723
|
-
/** RewardDistribution PDA for a
|
|
837
|
+
/** Bundle 9+232 (2026-05-06) — RewardDistribution PDA for a
|
|
838
|
+
* `(pool, rewardMint, periodId)` triple. */
|
|
724
839
|
deriveRewardDistributionAddress(args) {
|
|
725
840
|
const periodBytes = Buffer.alloc(4);
|
|
726
841
|
periodBytes.writeUInt32LE(args.periodId, 0);
|
|
727
|
-
return PublicKey.findProgramAddressSync([
|
|
842
|
+
return PublicKey.findProgramAddressSync([
|
|
843
|
+
Buffer.from('reward-dist'),
|
|
844
|
+
new PublicKey(args.poolPda).toBytes(),
|
|
845
|
+
new PublicKey(args.rewardMint).toBytes(),
|
|
846
|
+
periodBytes,
|
|
847
|
+
], this.programId)[0].toBase58();
|
|
728
848
|
}
|
|
729
849
|
// ==========================================================================
|
|
730
850
|
// Stake-flow ix builders
|
|
731
851
|
// ==========================================================================
|
|
732
|
-
/** Build the register_pool_token_accounts ix. Permissionless;
|
|
852
|
+
/** Build the register_pool_token_accounts ix. Permissionless;
|
|
853
|
+
* one-time per pool. Bundle 9+232 (2026-05-06) — principal-vault only;
|
|
854
|
+
* per-mint reward vaults moved to `register_pool_reward_mint`. */
|
|
733
855
|
buildRegisterPoolTokenAccountsIx(args) {
|
|
734
856
|
const poolKey = new PublicKey(args.poolPda);
|
|
735
857
|
const principalVault = new PublicKey(this.derivePrincipalVaultAddress(args.poolPda));
|
|
736
|
-
const rewardVault = new PublicKey(this.deriveStakerPoolRewardVault(args.poolPda));
|
|
737
858
|
return new TransactionInstruction({
|
|
738
859
|
programId: this.programId,
|
|
739
860
|
keys: [
|
|
@@ -746,7 +867,6 @@ export class EmblemStakeClient {
|
|
|
746
867
|
isWritable: false,
|
|
747
868
|
},
|
|
748
869
|
{ pubkey: principalVault, isSigner: false, isWritable: true },
|
|
749
|
-
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
750
870
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
751
871
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
752
872
|
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
|
|
@@ -846,16 +966,24 @@ export class EmblemStakeClient {
|
|
|
846
966
|
});
|
|
847
967
|
}
|
|
848
968
|
/**
|
|
849
|
-
* Build the claim ix.
|
|
850
|
-
*
|
|
851
|
-
*
|
|
969
|
+
* Build the claim ix. Bundle 9+232 (2026-05-06) — caller specifies
|
|
970
|
+
* `rewardMint` (the stream to claim against). The caller must pass
|
|
971
|
+
* RewardDistribution PDAs for every period from
|
|
972
|
+
* `vault.last_claim_periods[mint_idx]` to `pool.current_periods[mint_idx] - 1`
|
|
973
|
+
* inclusive in `remaining_accounts`. Per-stream PDAs are seeded with
|
|
974
|
+
* `(pool, rewardMint, periodId)`.
|
|
852
975
|
*/
|
|
853
976
|
buildClaimIx(args) {
|
|
854
977
|
const poolKey = new PublicKey(args.poolPda);
|
|
978
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
855
979
|
const stakeVault = new PublicKey(this.deriveStakeVaultAddress({ poolPda: args.poolPda, owner: args.user }));
|
|
856
|
-
const rewardVault =
|
|
980
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
857
981
|
const distAccounts = args.periodIds.map((pid) => ({
|
|
858
|
-
pubkey: new PublicKey(this.deriveRewardDistributionAddress({
|
|
982
|
+
pubkey: new PublicKey(this.deriveRewardDistributionAddress({
|
|
983
|
+
poolPda: args.poolPda,
|
|
984
|
+
rewardMint: args.rewardMint,
|
|
985
|
+
periodId: pid,
|
|
986
|
+
})),
|
|
859
987
|
isSigner: false,
|
|
860
988
|
isWritable: false,
|
|
861
989
|
}));
|
|
@@ -879,20 +1007,28 @@ export class EmblemStakeClient {
|
|
|
879
1007
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
880
1008
|
...distAccounts,
|
|
881
1009
|
],
|
|
882
|
-
|
|
1010
|
+
// Bundle 9+232 — claim ix takes reward_mint: Pubkey arg.
|
|
1011
|
+
data: Buffer.concat([IX_CLAIM, mintKey.toBuffer()]),
|
|
883
1012
|
});
|
|
884
1013
|
}
|
|
885
1014
|
/**
|
|
886
1015
|
* Build the distribute_rewards ix. Permissionless trigger — anyone
|
|
887
|
-
* pays tx fees to advance the period.
|
|
1016
|
+
* pays tx fees to advance the period for one stream. Bundle 9+232
|
|
1017
|
+
* (2026-05-06) — caller specifies `rewardMint`; ix data carries it
|
|
1018
|
+
* + reads `PlatformConfig.fee_bps` to bookkeep the staker-pool /
|
|
1019
|
+
* platform-accrual split.
|
|
888
1020
|
*/
|
|
889
1021
|
buildDistributeRewardsIx(args) {
|
|
890
1022
|
const poolKey = new PublicKey(args.poolPda);
|
|
891
|
-
const
|
|
1023
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1024
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
892
1025
|
const distribution = new PublicKey(this.deriveRewardDistributionAddress({
|
|
893
1026
|
poolPda: args.poolPda,
|
|
1027
|
+
rewardMint: args.rewardMint,
|
|
894
1028
|
periodId: args.periodId,
|
|
895
1029
|
}));
|
|
1030
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
1031
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
896
1032
|
return new TransactionInstruction({
|
|
897
1033
|
programId: this.programId,
|
|
898
1034
|
keys: [
|
|
@@ -900,31 +1036,136 @@ export class EmblemStakeClient {
|
|
|
900
1036
|
{ pubkey: poolKey, isSigner: false, isWritable: true },
|
|
901
1037
|
{ pubkey: rewardVault, isSigner: false, isWritable: false },
|
|
902
1038
|
{ pubkey: distribution, isSigner: false, isWritable: true },
|
|
1039
|
+
{
|
|
1040
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
1041
|
+
isSigner: false,
|
|
1042
|
+
isWritable: false,
|
|
1043
|
+
},
|
|
1044
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
903
1045
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
904
1046
|
],
|
|
905
|
-
data: IX_DISTRIBUTE_REWARDS,
|
|
1047
|
+
data: Buffer.concat([IX_DISTRIBUTE_REWARDS, mintKey.toBuffer()]),
|
|
906
1048
|
});
|
|
907
1049
|
}
|
|
908
1050
|
// ==========================================================================
|
|
909
1051
|
// Self-submitting wrappers
|
|
910
1052
|
// ==========================================================================
|
|
1053
|
+
/**
|
|
1054
|
+
* Create a stake pool AND chain `register_pool_token_accounts` so the
|
|
1055
|
+
* principal vault + staker-pool reward vault are initialized in the
|
|
1056
|
+
* same call. Without the chain, the very first `stake()` reverts with
|
|
1057
|
+
* `AccountNotInitialized` on `principal_vault`. Caller gets back a
|
|
1058
|
+
* pool that is immediately ready to receive stakes.
|
|
1059
|
+
*
|
|
1060
|
+
* register_pool_token_accounts is permissionless and one-time per
|
|
1061
|
+
* pool, so we always include it. If it ever becomes a no-op for an
|
|
1062
|
+
* existing pool, this is still safe.
|
|
1063
|
+
*/
|
|
1064
|
+
/**
|
|
1065
|
+
* Create a stake pool AND chain `register_pool_token_accounts` so the
|
|
1066
|
+
* principal vault is initialized in the same call. Bundle 9+232
|
|
1067
|
+
* (2026-05-06) — also chains one `register_pool_reward_mint` per
|
|
1068
|
+
* entry in `args.initialRewardMints`, initializing each stream's
|
|
1069
|
+
* reward vault + PlatformAccrual atomically with pool creation. If
|
|
1070
|
+
* `initialRewardMints` is empty, no reward streams are registered;
|
|
1071
|
+
* the pool authority can register them later via
|
|
1072
|
+
* `registerPoolRewardMint()`.
|
|
1073
|
+
*/
|
|
911
1074
|
async createPool(args) {
|
|
912
1075
|
const signer = this.requireSigner('createPool');
|
|
913
|
-
const ix = this.buildCreatePoolIx({ ...args, authority: signer.publicKey });
|
|
914
|
-
const tx = new Transaction().add(ix);
|
|
915
|
-
const txSignature = await this.signAndSubmit(tx, signer);
|
|
916
1076
|
const { poolPda } = this.derivePoolPda({
|
|
917
1077
|
asset: args.config.asset,
|
|
918
1078
|
poolId: args.poolId ?? 0,
|
|
919
1079
|
});
|
|
920
|
-
|
|
1080
|
+
const createIx = this.buildCreatePoolIx({
|
|
1081
|
+
...args,
|
|
1082
|
+
authority: signer.publicKey,
|
|
1083
|
+
});
|
|
1084
|
+
const registerIx = this.buildRegisterPoolTokenAccountsIx({
|
|
1085
|
+
payer: signer.publicKey,
|
|
1086
|
+
poolPda,
|
|
1087
|
+
mint: args.config.asset,
|
|
1088
|
+
});
|
|
1089
|
+
const tx = new Transaction().add(createIx).add(registerIx);
|
|
1090
|
+
const initialRewardMints = args.initialRewardMints ?? [];
|
|
1091
|
+
for (const rewardMint of initialRewardMints) {
|
|
1092
|
+
tx.add(this.buildRegisterPoolRewardMintIx({
|
|
1093
|
+
payer: signer.publicKey,
|
|
1094
|
+
authority: signer.publicKey,
|
|
1095
|
+
poolPda,
|
|
1096
|
+
rewardMint,
|
|
1097
|
+
}));
|
|
1098
|
+
}
|
|
1099
|
+
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1100
|
+
return {
|
|
1101
|
+
poolPda,
|
|
1102
|
+
principalVaultPda: this.derivePrincipalVaultAddress(poolPda),
|
|
1103
|
+
rewardVaultPdas: initialRewardMints.map((rewardMint) => ({
|
|
1104
|
+
rewardMint,
|
|
1105
|
+
rewardVaultPda: this.deriveStakerPoolRewardVault({ poolPda, rewardMint }),
|
|
1106
|
+
})),
|
|
1107
|
+
txSignature,
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Bundle 9+232 (2026-05-06) — register a new reward token stream on
|
|
1112
|
+
* an existing pool. Authority-only on chain. Inits the per-mint
|
|
1113
|
+
* reward vault PDA + PlatformAccrual; appends to `pool.reward_mints`.
|
|
1114
|
+
*/
|
|
1115
|
+
async registerPoolRewardMint(args) {
|
|
1116
|
+
const signer = this.requireSigner('registerPoolRewardMint');
|
|
1117
|
+
const ix = this.buildRegisterPoolRewardMintIx({
|
|
1118
|
+
payer: signer.publicKey,
|
|
1119
|
+
authority: signer.publicKey,
|
|
1120
|
+
poolPda: args.poolPda,
|
|
1121
|
+
rewardMint: args.rewardMint,
|
|
1122
|
+
});
|
|
1123
|
+
const tx = new Transaction().add(ix);
|
|
1124
|
+
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1125
|
+
return {
|
|
1126
|
+
rewardVaultPda: this.deriveStakerPoolRewardVault(args),
|
|
1127
|
+
accrualPda: this.derivePlatformAccrualPda(args).accrualPda,
|
|
1128
|
+
txSignature,
|
|
1129
|
+
};
|
|
921
1130
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1131
|
+
/**
|
|
1132
|
+
* Bundle 9+232 (2026-05-06) — admin updates Emblem's protocol fee
|
|
1133
|
+
* on reward distributions. Capped at 30% by the on-chain ix.
|
|
1134
|
+
* Mainnet ships at 0.
|
|
1135
|
+
*/
|
|
1136
|
+
async setPlatformFeeBps(feeBps) {
|
|
1137
|
+
const signer = this.requireSigner('setPlatformFeeBps');
|
|
1138
|
+
const ix = this.buildSetPlatformFeeBpsIx({ admin: signer.publicKey, feeBps });
|
|
925
1139
|
const tx = new Transaction().add(ix);
|
|
926
1140
|
const txSignature = await this.signAndSubmit(tx, signer);
|
|
927
|
-
return {
|
|
1141
|
+
return { txSignature };
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Bundle 9+232 (2026-05-06) — admin drains accrued platform fees from
|
|
1145
|
+
* one reward stream into a treasury ATA. Auto-derives the canonical
|
|
1146
|
+
* admin-owned ATA for `rewardMint` if `treasuryTokenAccount` is
|
|
1147
|
+
* omitted, and prepends an idempotent ATA-create ix.
|
|
1148
|
+
*/
|
|
1149
|
+
async emblemClaimPlatformFee(args) {
|
|
1150
|
+
const signer = this.requireSigner('emblemClaimPlatformFee');
|
|
1151
|
+
const tokenProgramId = await this.detectTokenProgramId(args.rewardMint);
|
|
1152
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1153
|
+
const adminKey = new PublicKey(signer.publicKey);
|
|
1154
|
+
const treasuryAta = args.treasuryTokenAccount ??
|
|
1155
|
+
getAssociatedTokenAddressSync(mintKey, adminKey, true, tokenProgramId).toBase58();
|
|
1156
|
+
const ataIx = createAssociatedTokenAccountIdempotentInstruction(adminKey, new PublicKey(treasuryAta), adminKey, mintKey, tokenProgramId);
|
|
1157
|
+
const claimIx = this.buildEmblemClaimPlatformFeeIx({
|
|
1158
|
+
admin: signer.publicKey,
|
|
1159
|
+
poolPda: args.poolPda,
|
|
1160
|
+
rewardMint: args.rewardMint,
|
|
1161
|
+
treasuryTokenAccount: treasuryAta,
|
|
1162
|
+
});
|
|
1163
|
+
const tx = new Transaction().add(ataIx).add(claimIx);
|
|
1164
|
+
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1165
|
+
return {
|
|
1166
|
+
accrualPda: this.derivePlatformAccrualPda(args).accrualPda,
|
|
1167
|
+
txSignature,
|
|
1168
|
+
};
|
|
928
1169
|
}
|
|
929
1170
|
async registerPendingActionVault(args) {
|
|
930
1171
|
const signer = this.requireSigner('registerPendingActionVault');
|
|
@@ -1024,9 +1265,11 @@ export class EmblemStakeClient {
|
|
|
1024
1265
|
return { txSignature };
|
|
1025
1266
|
}
|
|
1026
1267
|
/**
|
|
1027
|
-
* Claim accrued rewards for one or more periods.
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1268
|
+
* Claim accrued rewards for one stream over one or more periods.
|
|
1269
|
+
* Bundle 9+232 (2026-05-06) — claim is per-reward-stream; caller
|
|
1270
|
+
* specifies which `rewardMint` to pull. Auto-derives the user's ATA
|
|
1271
|
+
* for `rewardMint` (NOT pool.asset — these may differ) and prepends
|
|
1272
|
+
* an idempotent ATA-create ix.
|
|
1030
1273
|
*/
|
|
1031
1274
|
async claimRewards(args) {
|
|
1032
1275
|
const signer = this.requireSigner('claimRewards');
|
|
@@ -1034,8 +1277,11 @@ export class EmblemStakeClient {
|
|
|
1034
1277
|
if (!pool) {
|
|
1035
1278
|
throw new EmblemStakeError(EmblemError.MissingAccount, `Pool ${args.poolPda} not found`);
|
|
1036
1279
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1280
|
+
if (!pool.rewardMints.includes(args.rewardMint)) {
|
|
1281
|
+
throw new EmblemStakeError(EmblemError.StakeRewardSourceInvalid, `Pool ${args.poolPda} has not registered reward mint ${args.rewardMint}; registered: [${pool.rewardMints.join(', ')}]`);
|
|
1282
|
+
}
|
|
1283
|
+
const tokenProgramId = await this.detectTokenProgramId(args.rewardMint);
|
|
1284
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1039
1285
|
const ownerKey = new PublicKey(signer.publicKey);
|
|
1040
1286
|
const userAta = args.userTokenAccount ??
|
|
1041
1287
|
getAssociatedTokenAddressSync(mintKey, ownerKey, true, tokenProgramId).toBase58();
|
|
@@ -1043,6 +1289,7 @@ export class EmblemStakeClient {
|
|
|
1043
1289
|
const claimIx = this.buildClaimIx({
|
|
1044
1290
|
user: signer.publicKey,
|
|
1045
1291
|
poolPda: args.poolPda,
|
|
1292
|
+
rewardMint: args.rewardMint,
|
|
1046
1293
|
userTokenAccount: userAta,
|
|
1047
1294
|
periodIds: args.periodIds,
|
|
1048
1295
|
});
|
|
@@ -1050,11 +1297,17 @@ export class EmblemStakeClient {
|
|
|
1050
1297
|
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1051
1298
|
return { txSignature };
|
|
1052
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Bundle 9+232 (2026-05-06) — advance one reward stream's period
|
|
1302
|
+
* counter. Caller specifies `rewardMint`; the cadence floor + the
|
|
1303
|
+
* platform-fee bookkeeping are per-stream.
|
|
1304
|
+
*/
|
|
1053
1305
|
async distributeRewards(args) {
|
|
1054
1306
|
const signer = this.requireSigner('distributeRewards');
|
|
1055
1307
|
const ix = this.buildDistributeRewardsIx({
|
|
1056
1308
|
payer: signer.publicKey,
|
|
1057
1309
|
poolPda: args.poolPda,
|
|
1310
|
+
rewardMint: args.rewardMint,
|
|
1058
1311
|
periodId: args.periodId,
|
|
1059
1312
|
});
|
|
1060
1313
|
const tx = new Transaction().add(ix);
|
|
@@ -1062,11 +1315,69 @@ export class EmblemStakeClient {
|
|
|
1062
1315
|
return {
|
|
1063
1316
|
distributionPda: this.deriveRewardDistributionAddress({
|
|
1064
1317
|
poolPda: args.poolPda,
|
|
1318
|
+
rewardMint: args.rewardMint,
|
|
1065
1319
|
periodId: args.periodId,
|
|
1066
1320
|
}),
|
|
1067
1321
|
txSignature,
|
|
1068
1322
|
};
|
|
1069
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Bundle 9+232 (2026-05-06) — discovery helper. Lists every
|
|
1326
|
+
* RewardDistribution PDA for one pool, optionally filtered to a
|
|
1327
|
+
* single reward mint. Mirrors launch SDK's `listLaunchesByCreator`
|
|
1328
|
+
* pattern. Sorted by `(rewardMint, periodId ASC)` for deterministic
|
|
1329
|
+
* pagination on the agent side.
|
|
1330
|
+
*/
|
|
1331
|
+
async listDistributionsByPool(args) {
|
|
1332
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
1333
|
+
// pool_pda field offset = disc(8) + bump(1) = 9.
|
|
1334
|
+
const POOL_FIELD_OFFSET = 9;
|
|
1335
|
+
const filters = [
|
|
1336
|
+
{ memcmp: { offset: 0, bytes: bs58Encode(REWARD_DIST_DISCRIMINATOR) } },
|
|
1337
|
+
{ memcmp: { offset: POOL_FIELD_OFFSET, bytes: poolKey.toBase58() } },
|
|
1338
|
+
];
|
|
1339
|
+
if (args.rewardMint) {
|
|
1340
|
+
// reward_mint at offset disc(8) + bump(1) + pool_pda(32) + period_id(4) +
|
|
1341
|
+
// total_distributed(8) + weighted_stake_at_snapshot(8) + distributed_at(8) = 69.
|
|
1342
|
+
const REWARD_MINT_OFFSET = 69;
|
|
1343
|
+
filters.push({
|
|
1344
|
+
memcmp: { offset: REWARD_MINT_OFFSET, bytes: args.rewardMint },
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
const accounts = await this.connection.getProgramAccounts(this.programId, {
|
|
1348
|
+
commitment: this.commitment,
|
|
1349
|
+
filters,
|
|
1350
|
+
});
|
|
1351
|
+
const views = accounts.map((a) => {
|
|
1352
|
+
const decoded = decodeRewardDistributionAccount(a.account.data);
|
|
1353
|
+
return { ...decoded, distributionPda: a.pubkey.toBase58() };
|
|
1354
|
+
});
|
|
1355
|
+
views.sort((a, b) => {
|
|
1356
|
+
if (a.rewardMint !== b.rewardMint) {
|
|
1357
|
+
return a.rewardMint < b.rewardMint ? -1 : 1;
|
|
1358
|
+
}
|
|
1359
|
+
return a.periodId - b.periodId;
|
|
1360
|
+
});
|
|
1361
|
+
return views;
|
|
1362
|
+
}
|
|
1363
|
+
/** Bundle 9+232 (2026-05-06) — read PlatformAccrual for a stream. */
|
|
1364
|
+
async getPlatformAccrual(args) {
|
|
1365
|
+
const accrualPda = this.derivePlatformAccrualPda(args).accrualPda;
|
|
1366
|
+
const info = await this.connection.getAccountInfo(new PublicKey(accrualPda), this.commitment);
|
|
1367
|
+
if (!info)
|
|
1368
|
+
return null;
|
|
1369
|
+
const decoded = decodePlatformAccrualAccount(info.data);
|
|
1370
|
+
return { accrualPda, pending: decoded.pending };
|
|
1371
|
+
}
|
|
1372
|
+
/** Bundle 9+232 (2026-05-06) — read PlatformConfig (singleton). */
|
|
1373
|
+
async getPlatformConfig() {
|
|
1374
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
1375
|
+
const info = await this.connection.getAccountInfo(new PublicKey(platformConfigPda), this.commitment);
|
|
1376
|
+
if (!info)
|
|
1377
|
+
return null;
|
|
1378
|
+
const decoded = decodeStakePlatformConfigAccount(info.data);
|
|
1379
|
+
return { platformConfigPda, ...decoded };
|
|
1380
|
+
}
|
|
1070
1381
|
async signAndSubmit(tx, signer) {
|
|
1071
1382
|
const { blockhash } = await this.connection.getLatestBlockhash(this.commitment);
|
|
1072
1383
|
const payerKey = new PublicKey(signer.publicKey);
|