@emblemvault/primitives-stake 0.5.1 → 0.7.1
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 +165 -30
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +348 -56
- 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,16 @@ 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) — `create_pool`'s reward_mints arg is
|
|
648
|
+
// always encoded EMPTY here. The on-chain `create_pool` body only
|
|
649
|
+
// appends to `pool.reward_mints`; it does NOT init the per-mint
|
|
650
|
+
// reward-vault TokenAccount (that's `register_pool_reward_mint`'s
|
|
651
|
+
// job). If we wrote the mints here AND the caller chained
|
|
652
|
+
// register_pool_reward_mint per mint, the second ix would revert
|
|
653
|
+
// with StakePoolShapeInvalid (duplicate). So mints flow through the
|
|
654
|
+
// register-per-mint path exclusively, and `args.initialRewardMints`
|
|
655
|
+
// is consumed by the higher-level `createPool()` wrapper to build
|
|
656
|
+
// those follow-up ixs.
|
|
612
657
|
const data = Buffer.concat([
|
|
613
658
|
IX_CREATE_POOL,
|
|
614
659
|
u32(poolId),
|
|
@@ -626,6 +671,8 @@ export class EmblemStakeClient {
|
|
|
626
671
|
...cfg.rewardSources.map(encodeRewardSource),
|
|
627
672
|
Buffer.from([encodeRewardCadence(cfg.rewardCadence)]),
|
|
628
673
|
Buffer.from([encodeRewardFormula(cfg.rewardFormula)]),
|
|
674
|
+
// Bundle 9+232 — reward_mints: Vec<Pubkey> always empty here.
|
|
675
|
+
u32(0),
|
|
629
676
|
]);
|
|
630
677
|
return new TransactionInstruction({
|
|
631
678
|
programId: this.programId,
|
|
@@ -638,31 +685,105 @@ export class EmblemStakeClient {
|
|
|
638
685
|
});
|
|
639
686
|
}
|
|
640
687
|
/**
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* `
|
|
645
|
-
*
|
|
688
|
+
* Bundle 9+232 (2026-05-06) — register a reward token stream on an
|
|
689
|
+
* existing pool. Inits the per-mint reward vault PDA + the stream's
|
|
690
|
+
* PlatformAccrual PDA, and appends `reward_mint` to
|
|
691
|
+
* `pool.reward_mints`. Authority-only on chain (`has_one = authority`).
|
|
692
|
+
*
|
|
693
|
+
* Used by the `createPool()` self-submitting wrapper to chain one of
|
|
694
|
+
* these per `initialRewardMints` entry — but exposed here so callers
|
|
695
|
+
* can register additional streams later.
|
|
646
696
|
*/
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
const
|
|
697
|
+
buildRegisterPoolRewardMintIx(args) {
|
|
698
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
699
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
650
700
|
const vaultAuthority = deriveVaultAuthority(this.programId);
|
|
651
|
-
const
|
|
701
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
702
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
652
703
|
return new TransactionInstruction({
|
|
653
704
|
programId: this.programId,
|
|
654
705
|
keys: [
|
|
655
|
-
{ pubkey: new PublicKey(args.
|
|
656
|
-
{ pubkey:
|
|
706
|
+
{ pubkey: new PublicKey(args.payer), isSigner: true, isWritable: true },
|
|
707
|
+
{ pubkey: new PublicKey(args.authority), isSigner: true, isWritable: false },
|
|
708
|
+
{ pubkey: poolKey, isSigner: false, isWritable: true },
|
|
709
|
+
{ pubkey: mintKey, isSigner: false, isWritable: false },
|
|
657
710
|
{ pubkey: vaultAuthority, isSigner: false, isWritable: false },
|
|
658
|
-
{ pubkey:
|
|
711
|
+
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
712
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
659
713
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
660
714
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
661
715
|
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
|
|
662
716
|
],
|
|
663
|
-
data: Buffer.concat([
|
|
717
|
+
data: Buffer.concat([IX_REGISTER_POOL_REWARD_MINT, mintKey.toBuffer()]),
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Bundle 9+232 (2026-05-06) — admin sets the protocol fee on reward
|
|
722
|
+
* distributions, in basis points. Capped at
|
|
723
|
+
* `STAKE_PLATFORM_FEE_BPS_MAX = 3_000` (30%) by the on-chain ix.
|
|
724
|
+
* Mainnet ships at 0.
|
|
725
|
+
*/
|
|
726
|
+
buildSetPlatformFeeBpsIx(args) {
|
|
727
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
728
|
+
const data = Buffer.concat([IX_SET_PLATFORM_FEE_BPS, u16(args.feeBps)]);
|
|
729
|
+
return new TransactionInstruction({
|
|
730
|
+
programId: this.programId,
|
|
731
|
+
keys: [
|
|
732
|
+
{ pubkey: new PublicKey(args.admin), isSigner: true, isWritable: false },
|
|
733
|
+
{
|
|
734
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
735
|
+
isSigner: false,
|
|
736
|
+
isWritable: true,
|
|
737
|
+
},
|
|
738
|
+
],
|
|
739
|
+
data,
|
|
664
740
|
});
|
|
665
741
|
}
|
|
742
|
+
/**
|
|
743
|
+
* Bundle 9+232 (2026-05-06) — drain accrued platform fees from one
|
|
744
|
+
* reward stream's `PlatformAccrual.pending` into the admin's treasury
|
|
745
|
+
* ATA. Admin-signed (`has_one = admin` on `PlatformConfig`); body
|
|
746
|
+
* also asserts `treasury.owner == admin` so the admin can't route
|
|
747
|
+
* Emblem fees to a third-party wallet. Idempotent-error at
|
|
748
|
+
* `pending == 0` with `StakePlatformAccrualNothingPending (519)`.
|
|
749
|
+
*/
|
|
750
|
+
buildEmblemClaimPlatformFeeIx(args) {
|
|
751
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
752
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
753
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
754
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
755
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
756
|
+
const vaultAuthority = deriveVaultAuthority(this.programId);
|
|
757
|
+
const data = Buffer.concat([
|
|
758
|
+
IX_EMBLEM_CLAIM_PLATFORM_FEE,
|
|
759
|
+
mintKey.toBuffer(),
|
|
760
|
+
]);
|
|
761
|
+
return new TransactionInstruction({
|
|
762
|
+
programId: this.programId,
|
|
763
|
+
keys: [
|
|
764
|
+
{ pubkey: new PublicKey(args.admin), isSigner: true, isWritable: false },
|
|
765
|
+
{
|
|
766
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
767
|
+
isSigner: false,
|
|
768
|
+
isWritable: false,
|
|
769
|
+
},
|
|
770
|
+
{ pubkey: poolKey, isSigner: false, isWritable: false },
|
|
771
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
772
|
+
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
773
|
+
{
|
|
774
|
+
pubkey: new PublicKey(args.treasuryTokenAccount),
|
|
775
|
+
isSigner: false,
|
|
776
|
+
isWritable: true,
|
|
777
|
+
},
|
|
778
|
+
{ pubkey: vaultAuthority, isSigner: false, isWritable: false },
|
|
779
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
780
|
+
],
|
|
781
|
+
data,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
// Bundle 9+232 (2026-05-06): buildRegisterRewardVaultIx deleted with
|
|
785
|
+
// the Tier 2 tombstone. Use buildRegisterPoolRewardMintIx for per-stream
|
|
786
|
+
// reward vaults instead.
|
|
666
787
|
/** Build the register_pending_action_vault instruction (Tier 3 settlement bucket). */
|
|
667
788
|
buildRegisterPendingActionVaultIx(args) {
|
|
668
789
|
const mint = new PublicKey(args.mint);
|
|
@@ -687,17 +808,15 @@ export class EmblemStakeClient {
|
|
|
687
808
|
]),
|
|
688
809
|
});
|
|
689
810
|
}
|
|
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
811
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
812
|
+
* Bundle 9+232 (2026-05-06) — per-mint reward vault address. Pre-bundle
|
|
813
|
+
* `deriveStakerPoolRewardVault(pool)` (single-stream) and
|
|
814
|
+
* `deriveDistributionVaultAddress(vaultPubkey)` (Tier 2 tombstone)
|
|
815
|
+
* helpers are gone — see `deriveRewardVaultPda` above for the canonical
|
|
816
|
+
* `(pool, rewardMint)` derivation.
|
|
698
817
|
*/
|
|
699
|
-
|
|
700
|
-
return
|
|
818
|
+
deriveStakerPoolRewardVault(args) {
|
|
819
|
+
return deriveRewardVaultForMint(new PublicKey(args.poolPda), new PublicKey(args.rewardMint), this.programId).toBase58();
|
|
701
820
|
}
|
|
702
821
|
/** Address of a Tier 3 pending-action vault. */
|
|
703
822
|
derivePendingActionVaultAddress(args) {
|
|
@@ -720,20 +839,27 @@ export class EmblemStakeClient {
|
|
|
720
839
|
new PublicKey(args.owner).toBytes(),
|
|
721
840
|
], this.programId)[0].toBase58();
|
|
722
841
|
}
|
|
723
|
-
/** RewardDistribution PDA for a
|
|
842
|
+
/** Bundle 9+232 (2026-05-06) — RewardDistribution PDA for a
|
|
843
|
+
* `(pool, rewardMint, periodId)` triple. */
|
|
724
844
|
deriveRewardDistributionAddress(args) {
|
|
725
845
|
const periodBytes = Buffer.alloc(4);
|
|
726
846
|
periodBytes.writeUInt32LE(args.periodId, 0);
|
|
727
|
-
return PublicKey.findProgramAddressSync([
|
|
847
|
+
return PublicKey.findProgramAddressSync([
|
|
848
|
+
Buffer.from('reward-dist'),
|
|
849
|
+
new PublicKey(args.poolPda).toBytes(),
|
|
850
|
+
new PublicKey(args.rewardMint).toBytes(),
|
|
851
|
+
periodBytes,
|
|
852
|
+
], this.programId)[0].toBase58();
|
|
728
853
|
}
|
|
729
854
|
// ==========================================================================
|
|
730
855
|
// Stake-flow ix builders
|
|
731
856
|
// ==========================================================================
|
|
732
|
-
/** Build the register_pool_token_accounts ix. Permissionless;
|
|
857
|
+
/** Build the register_pool_token_accounts ix. Permissionless;
|
|
858
|
+
* one-time per pool. Bundle 9+232 (2026-05-06) — principal-vault only;
|
|
859
|
+
* per-mint reward vaults moved to `register_pool_reward_mint`. */
|
|
733
860
|
buildRegisterPoolTokenAccountsIx(args) {
|
|
734
861
|
const poolKey = new PublicKey(args.poolPda);
|
|
735
862
|
const principalVault = new PublicKey(this.derivePrincipalVaultAddress(args.poolPda));
|
|
736
|
-
const rewardVault = new PublicKey(this.deriveStakerPoolRewardVault(args.poolPda));
|
|
737
863
|
return new TransactionInstruction({
|
|
738
864
|
programId: this.programId,
|
|
739
865
|
keys: [
|
|
@@ -746,7 +872,6 @@ export class EmblemStakeClient {
|
|
|
746
872
|
isWritable: false,
|
|
747
873
|
},
|
|
748
874
|
{ pubkey: principalVault, isSigner: false, isWritable: true },
|
|
749
|
-
{ pubkey: rewardVault, isSigner: false, isWritable: true },
|
|
750
875
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
751
876
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
752
877
|
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
|
|
@@ -846,16 +971,24 @@ export class EmblemStakeClient {
|
|
|
846
971
|
});
|
|
847
972
|
}
|
|
848
973
|
/**
|
|
849
|
-
* Build the claim ix.
|
|
850
|
-
*
|
|
851
|
-
*
|
|
974
|
+
* Build the claim ix. Bundle 9+232 (2026-05-06) — caller specifies
|
|
975
|
+
* `rewardMint` (the stream to claim against). The caller must pass
|
|
976
|
+
* RewardDistribution PDAs for every period from
|
|
977
|
+
* `vault.last_claim_periods[mint_idx]` to `pool.current_periods[mint_idx] - 1`
|
|
978
|
+
* inclusive in `remaining_accounts`. Per-stream PDAs are seeded with
|
|
979
|
+
* `(pool, rewardMint, periodId)`.
|
|
852
980
|
*/
|
|
853
981
|
buildClaimIx(args) {
|
|
854
982
|
const poolKey = new PublicKey(args.poolPda);
|
|
983
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
855
984
|
const stakeVault = new PublicKey(this.deriveStakeVaultAddress({ poolPda: args.poolPda, owner: args.user }));
|
|
856
|
-
const rewardVault =
|
|
985
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
857
986
|
const distAccounts = args.periodIds.map((pid) => ({
|
|
858
|
-
pubkey: new PublicKey(this.deriveRewardDistributionAddress({
|
|
987
|
+
pubkey: new PublicKey(this.deriveRewardDistributionAddress({
|
|
988
|
+
poolPda: args.poolPda,
|
|
989
|
+
rewardMint: args.rewardMint,
|
|
990
|
+
periodId: pid,
|
|
991
|
+
})),
|
|
859
992
|
isSigner: false,
|
|
860
993
|
isWritable: false,
|
|
861
994
|
}));
|
|
@@ -879,20 +1012,28 @@ export class EmblemStakeClient {
|
|
|
879
1012
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
880
1013
|
...distAccounts,
|
|
881
1014
|
],
|
|
882
|
-
|
|
1015
|
+
// Bundle 9+232 — claim ix takes reward_mint: Pubkey arg.
|
|
1016
|
+
data: Buffer.concat([IX_CLAIM, mintKey.toBuffer()]),
|
|
883
1017
|
});
|
|
884
1018
|
}
|
|
885
1019
|
/**
|
|
886
1020
|
* Build the distribute_rewards ix. Permissionless trigger — anyone
|
|
887
|
-
* pays tx fees to advance the period.
|
|
1021
|
+
* pays tx fees to advance the period for one stream. Bundle 9+232
|
|
1022
|
+
* (2026-05-06) — caller specifies `rewardMint`; ix data carries it
|
|
1023
|
+
* + reads `PlatformConfig.fee_bps` to bookkeep the staker-pool /
|
|
1024
|
+
* platform-accrual split.
|
|
888
1025
|
*/
|
|
889
1026
|
buildDistributeRewardsIx(args) {
|
|
890
1027
|
const poolKey = new PublicKey(args.poolPda);
|
|
891
|
-
const
|
|
1028
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1029
|
+
const rewardVault = deriveRewardVaultForMint(poolKey, mintKey, this.programId);
|
|
892
1030
|
const distribution = new PublicKey(this.deriveRewardDistributionAddress({
|
|
893
1031
|
poolPda: args.poolPda,
|
|
1032
|
+
rewardMint: args.rewardMint,
|
|
894
1033
|
periodId: args.periodId,
|
|
895
1034
|
}));
|
|
1035
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
1036
|
+
const platformAccrual = derivePlatformAccrualForMint(poolKey, mintKey, this.programId);
|
|
896
1037
|
return new TransactionInstruction({
|
|
897
1038
|
programId: this.programId,
|
|
898
1039
|
keys: [
|
|
@@ -900,9 +1041,15 @@ export class EmblemStakeClient {
|
|
|
900
1041
|
{ pubkey: poolKey, isSigner: false, isWritable: true },
|
|
901
1042
|
{ pubkey: rewardVault, isSigner: false, isWritable: false },
|
|
902
1043
|
{ pubkey: distribution, isSigner: false, isWritable: true },
|
|
1044
|
+
{
|
|
1045
|
+
pubkey: new PublicKey(platformConfigPda),
|
|
1046
|
+
isSigner: false,
|
|
1047
|
+
isWritable: false,
|
|
1048
|
+
},
|
|
1049
|
+
{ pubkey: platformAccrual, isSigner: false, isWritable: true },
|
|
903
1050
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
904
1051
|
],
|
|
905
|
-
data: IX_DISTRIBUTE_REWARDS,
|
|
1052
|
+
data: Buffer.concat([IX_DISTRIBUTE_REWARDS, mintKey.toBuffer()]),
|
|
906
1053
|
});
|
|
907
1054
|
}
|
|
908
1055
|
// ==========================================================================
|
|
@@ -919,6 +1066,16 @@ export class EmblemStakeClient {
|
|
|
919
1066
|
* pool, so we always include it. If it ever becomes a no-op for an
|
|
920
1067
|
* existing pool, this is still safe.
|
|
921
1068
|
*/
|
|
1069
|
+
/**
|
|
1070
|
+
* Create a stake pool AND chain `register_pool_token_accounts` so the
|
|
1071
|
+
* principal vault is initialized in the same call. Bundle 9+232
|
|
1072
|
+
* (2026-05-06) — also chains one `register_pool_reward_mint` per
|
|
1073
|
+
* entry in `args.initialRewardMints`, initializing each stream's
|
|
1074
|
+
* reward vault + PlatformAccrual atomically with pool creation. If
|
|
1075
|
+
* `initialRewardMints` is empty, no reward streams are registered;
|
|
1076
|
+
* the pool authority can register them later via
|
|
1077
|
+
* `registerPoolRewardMint()`.
|
|
1078
|
+
*/
|
|
922
1079
|
async createPool(args) {
|
|
923
1080
|
const signer = this.requireSigner('createPool');
|
|
924
1081
|
const { poolPda } = this.derivePoolPda({
|
|
@@ -935,20 +1092,85 @@ export class EmblemStakeClient {
|
|
|
935
1092
|
mint: args.config.asset,
|
|
936
1093
|
});
|
|
937
1094
|
const tx = new Transaction().add(createIx).add(registerIx);
|
|
1095
|
+
const initialRewardMints = args.initialRewardMints ?? [];
|
|
1096
|
+
for (const rewardMint of initialRewardMints) {
|
|
1097
|
+
tx.add(this.buildRegisterPoolRewardMintIx({
|
|
1098
|
+
payer: signer.publicKey,
|
|
1099
|
+
authority: signer.publicKey,
|
|
1100
|
+
poolPda,
|
|
1101
|
+
rewardMint,
|
|
1102
|
+
}));
|
|
1103
|
+
}
|
|
938
1104
|
const txSignature = await this.signAndSubmit(tx, signer);
|
|
939
1105
|
return {
|
|
940
1106
|
poolPda,
|
|
941
1107
|
principalVaultPda: this.derivePrincipalVaultAddress(poolPda),
|
|
942
|
-
|
|
1108
|
+
rewardVaultPdas: initialRewardMints.map((rewardMint) => ({
|
|
1109
|
+
rewardMint,
|
|
1110
|
+
rewardVaultPda: this.deriveStakerPoolRewardVault({ poolPda, rewardMint }),
|
|
1111
|
+
})),
|
|
943
1112
|
txSignature,
|
|
944
1113
|
};
|
|
945
1114
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1115
|
+
/**
|
|
1116
|
+
* Bundle 9+232 (2026-05-06) — register a new reward token stream on
|
|
1117
|
+
* an existing pool. Authority-only on chain. Inits the per-mint
|
|
1118
|
+
* reward vault PDA + PlatformAccrual; appends to `pool.reward_mints`.
|
|
1119
|
+
*/
|
|
1120
|
+
async registerPoolRewardMint(args) {
|
|
1121
|
+
const signer = this.requireSigner('registerPoolRewardMint');
|
|
1122
|
+
const ix = this.buildRegisterPoolRewardMintIx({
|
|
1123
|
+
payer: signer.publicKey,
|
|
1124
|
+
authority: signer.publicKey,
|
|
1125
|
+
poolPda: args.poolPda,
|
|
1126
|
+
rewardMint: args.rewardMint,
|
|
1127
|
+
});
|
|
1128
|
+
const tx = new Transaction().add(ix);
|
|
1129
|
+
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1130
|
+
return {
|
|
1131
|
+
rewardVaultPda: this.deriveStakerPoolRewardVault(args),
|
|
1132
|
+
accrualPda: this.derivePlatformAccrualPda(args).accrualPda,
|
|
1133
|
+
txSignature,
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Bundle 9+232 (2026-05-06) — admin updates Emblem's protocol fee
|
|
1138
|
+
* on reward distributions. Capped at 30% by the on-chain ix.
|
|
1139
|
+
* Mainnet ships at 0.
|
|
1140
|
+
*/
|
|
1141
|
+
async setPlatformFeeBps(feeBps) {
|
|
1142
|
+
const signer = this.requireSigner('setPlatformFeeBps');
|
|
1143
|
+
const ix = this.buildSetPlatformFeeBpsIx({ admin: signer.publicKey, feeBps });
|
|
949
1144
|
const tx = new Transaction().add(ix);
|
|
950
1145
|
const txSignature = await this.signAndSubmit(tx, signer);
|
|
951
|
-
return {
|
|
1146
|
+
return { txSignature };
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Bundle 9+232 (2026-05-06) — admin drains accrued platform fees from
|
|
1150
|
+
* one reward stream into a treasury ATA. Auto-derives the canonical
|
|
1151
|
+
* admin-owned ATA for `rewardMint` if `treasuryTokenAccount` is
|
|
1152
|
+
* omitted, and prepends an idempotent ATA-create ix.
|
|
1153
|
+
*/
|
|
1154
|
+
async emblemClaimPlatformFee(args) {
|
|
1155
|
+
const signer = this.requireSigner('emblemClaimPlatformFee');
|
|
1156
|
+
const tokenProgramId = await this.detectTokenProgramId(args.rewardMint);
|
|
1157
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1158
|
+
const adminKey = new PublicKey(signer.publicKey);
|
|
1159
|
+
const treasuryAta = args.treasuryTokenAccount ??
|
|
1160
|
+
getAssociatedTokenAddressSync(mintKey, adminKey, true, tokenProgramId).toBase58();
|
|
1161
|
+
const ataIx = createAssociatedTokenAccountIdempotentInstruction(adminKey, new PublicKey(treasuryAta), adminKey, mintKey, tokenProgramId);
|
|
1162
|
+
const claimIx = this.buildEmblemClaimPlatformFeeIx({
|
|
1163
|
+
admin: signer.publicKey,
|
|
1164
|
+
poolPda: args.poolPda,
|
|
1165
|
+
rewardMint: args.rewardMint,
|
|
1166
|
+
treasuryTokenAccount: treasuryAta,
|
|
1167
|
+
});
|
|
1168
|
+
const tx = new Transaction().add(ataIx).add(claimIx);
|
|
1169
|
+
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1170
|
+
return {
|
|
1171
|
+
accrualPda: this.derivePlatformAccrualPda(args).accrualPda,
|
|
1172
|
+
txSignature,
|
|
1173
|
+
};
|
|
952
1174
|
}
|
|
953
1175
|
async registerPendingActionVault(args) {
|
|
954
1176
|
const signer = this.requireSigner('registerPendingActionVault');
|
|
@@ -1048,9 +1270,11 @@ export class EmblemStakeClient {
|
|
|
1048
1270
|
return { txSignature };
|
|
1049
1271
|
}
|
|
1050
1272
|
/**
|
|
1051
|
-
* Claim accrued rewards for one or more periods.
|
|
1052
|
-
*
|
|
1053
|
-
*
|
|
1273
|
+
* Claim accrued rewards for one stream over one or more periods.
|
|
1274
|
+
* Bundle 9+232 (2026-05-06) — claim is per-reward-stream; caller
|
|
1275
|
+
* specifies which `rewardMint` to pull. Auto-derives the user's ATA
|
|
1276
|
+
* for `rewardMint` (NOT pool.asset — these may differ) and prepends
|
|
1277
|
+
* an idempotent ATA-create ix.
|
|
1054
1278
|
*/
|
|
1055
1279
|
async claimRewards(args) {
|
|
1056
1280
|
const signer = this.requireSigner('claimRewards');
|
|
@@ -1058,8 +1282,11 @@ export class EmblemStakeClient {
|
|
|
1058
1282
|
if (!pool) {
|
|
1059
1283
|
throw new EmblemStakeError(EmblemError.MissingAccount, `Pool ${args.poolPda} not found`);
|
|
1060
1284
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1285
|
+
if (!pool.rewardMints.includes(args.rewardMint)) {
|
|
1286
|
+
throw new EmblemStakeError(EmblemError.StakeRewardSourceInvalid, `Pool ${args.poolPda} has not registered reward mint ${args.rewardMint}; registered: [${pool.rewardMints.join(', ')}]`);
|
|
1287
|
+
}
|
|
1288
|
+
const tokenProgramId = await this.detectTokenProgramId(args.rewardMint);
|
|
1289
|
+
const mintKey = new PublicKey(args.rewardMint);
|
|
1063
1290
|
const ownerKey = new PublicKey(signer.publicKey);
|
|
1064
1291
|
const userAta = args.userTokenAccount ??
|
|
1065
1292
|
getAssociatedTokenAddressSync(mintKey, ownerKey, true, tokenProgramId).toBase58();
|
|
@@ -1067,6 +1294,7 @@ export class EmblemStakeClient {
|
|
|
1067
1294
|
const claimIx = this.buildClaimIx({
|
|
1068
1295
|
user: signer.publicKey,
|
|
1069
1296
|
poolPda: args.poolPda,
|
|
1297
|
+
rewardMint: args.rewardMint,
|
|
1070
1298
|
userTokenAccount: userAta,
|
|
1071
1299
|
periodIds: args.periodIds,
|
|
1072
1300
|
});
|
|
@@ -1074,11 +1302,17 @@ export class EmblemStakeClient {
|
|
|
1074
1302
|
const txSignature = await this.signAndSubmit(tx, signer);
|
|
1075
1303
|
return { txSignature };
|
|
1076
1304
|
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Bundle 9+232 (2026-05-06) — advance one reward stream's period
|
|
1307
|
+
* counter. Caller specifies `rewardMint`; the cadence floor + the
|
|
1308
|
+
* platform-fee bookkeeping are per-stream.
|
|
1309
|
+
*/
|
|
1077
1310
|
async distributeRewards(args) {
|
|
1078
1311
|
const signer = this.requireSigner('distributeRewards');
|
|
1079
1312
|
const ix = this.buildDistributeRewardsIx({
|
|
1080
1313
|
payer: signer.publicKey,
|
|
1081
1314
|
poolPda: args.poolPda,
|
|
1315
|
+
rewardMint: args.rewardMint,
|
|
1082
1316
|
periodId: args.periodId,
|
|
1083
1317
|
});
|
|
1084
1318
|
const tx = new Transaction().add(ix);
|
|
@@ -1086,11 +1320,69 @@ export class EmblemStakeClient {
|
|
|
1086
1320
|
return {
|
|
1087
1321
|
distributionPda: this.deriveRewardDistributionAddress({
|
|
1088
1322
|
poolPda: args.poolPda,
|
|
1323
|
+
rewardMint: args.rewardMint,
|
|
1089
1324
|
periodId: args.periodId,
|
|
1090
1325
|
}),
|
|
1091
1326
|
txSignature,
|
|
1092
1327
|
};
|
|
1093
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Bundle 9+232 (2026-05-06) — discovery helper. Lists every
|
|
1331
|
+
* RewardDistribution PDA for one pool, optionally filtered to a
|
|
1332
|
+
* single reward mint. Mirrors launch SDK's `listLaunchesByCreator`
|
|
1333
|
+
* pattern. Sorted by `(rewardMint, periodId ASC)` for deterministic
|
|
1334
|
+
* pagination on the agent side.
|
|
1335
|
+
*/
|
|
1336
|
+
async listDistributionsByPool(args) {
|
|
1337
|
+
const poolKey = new PublicKey(args.poolPda);
|
|
1338
|
+
// pool_pda field offset = disc(8) + bump(1) = 9.
|
|
1339
|
+
const POOL_FIELD_OFFSET = 9;
|
|
1340
|
+
const filters = [
|
|
1341
|
+
{ memcmp: { offset: 0, bytes: bs58Encode(REWARD_DIST_DISCRIMINATOR) } },
|
|
1342
|
+
{ memcmp: { offset: POOL_FIELD_OFFSET, bytes: poolKey.toBase58() } },
|
|
1343
|
+
];
|
|
1344
|
+
if (args.rewardMint) {
|
|
1345
|
+
// reward_mint at offset disc(8) + bump(1) + pool_pda(32) + period_id(4) +
|
|
1346
|
+
// total_distributed(8) + weighted_stake_at_snapshot(8) + distributed_at(8) = 69.
|
|
1347
|
+
const REWARD_MINT_OFFSET = 69;
|
|
1348
|
+
filters.push({
|
|
1349
|
+
memcmp: { offset: REWARD_MINT_OFFSET, bytes: args.rewardMint },
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
const accounts = await this.connection.getProgramAccounts(this.programId, {
|
|
1353
|
+
commitment: this.commitment,
|
|
1354
|
+
filters,
|
|
1355
|
+
});
|
|
1356
|
+
const views = accounts.map((a) => {
|
|
1357
|
+
const decoded = decodeRewardDistributionAccount(a.account.data);
|
|
1358
|
+
return { ...decoded, distributionPda: a.pubkey.toBase58() };
|
|
1359
|
+
});
|
|
1360
|
+
views.sort((a, b) => {
|
|
1361
|
+
if (a.rewardMint !== b.rewardMint) {
|
|
1362
|
+
return a.rewardMint < b.rewardMint ? -1 : 1;
|
|
1363
|
+
}
|
|
1364
|
+
return a.periodId - b.periodId;
|
|
1365
|
+
});
|
|
1366
|
+
return views;
|
|
1367
|
+
}
|
|
1368
|
+
/** Bundle 9+232 (2026-05-06) — read PlatformAccrual for a stream. */
|
|
1369
|
+
async getPlatformAccrual(args) {
|
|
1370
|
+
const accrualPda = this.derivePlatformAccrualPda(args).accrualPda;
|
|
1371
|
+
const info = await this.connection.getAccountInfo(new PublicKey(accrualPda), this.commitment);
|
|
1372
|
+
if (!info)
|
|
1373
|
+
return null;
|
|
1374
|
+
const decoded = decodePlatformAccrualAccount(info.data);
|
|
1375
|
+
return { accrualPda, pending: decoded.pending };
|
|
1376
|
+
}
|
|
1377
|
+
/** Bundle 9+232 (2026-05-06) — read PlatformConfig (singleton). */
|
|
1378
|
+
async getPlatformConfig() {
|
|
1379
|
+
const { platformConfigPda } = this.derivePlatformConfigPda();
|
|
1380
|
+
const info = await this.connection.getAccountInfo(new PublicKey(platformConfigPda), this.commitment);
|
|
1381
|
+
if (!info)
|
|
1382
|
+
return null;
|
|
1383
|
+
const decoded = decodeStakePlatformConfigAccount(info.data);
|
|
1384
|
+
return { platformConfigPda, ...decoded };
|
|
1385
|
+
}
|
|
1094
1386
|
async signAndSubmit(tx, signer) {
|
|
1095
1387
|
const { blockhash } = await this.connection.getLatestBlockhash(this.commitment);
|
|
1096
1388
|
const payerKey = new PublicKey(signer.publicKey);
|