From 5726f42a7cf0faacbceafc19b2293397b99017f1 Mon Sep 17 00:00:00 2001 From: mooori Date: Thu, 17 Feb 2022 20:21:07 +0100 Subject: [PATCH] feat(stake-program): support `splitWithSeed` (#23213) --- web3.js/src/stake-program.ts | 80 ++++++++++++++++++++++++------ web3.js/test/stake-program.test.ts | 73 +++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 18 deletions(-) diff --git a/web3.js/src/stake-program.ts b/web3.js/src/stake-program.ts index 736923136..1caf18224 100644 --- a/web3.js/src/stake-program.ts +++ b/web3.js/src/stake-program.ts @@ -147,6 +147,18 @@ export type SplitStakeParams = { lamports: number; }; +/** + * Split with seed transaction params + */ +export type SplitStakeWithSeedParams = { + stakePubkey: PublicKey; + authorizedPubkey: PublicKey; + splitStakePubkey: PublicKey; + basePubkey: PublicKey; + seed: string; + lamports: number; +}; + /** * Withdraw stake instruction params */ @@ -706,25 +718,13 @@ export class StakeProgram { } /** - * Generate a Transaction that splits Stake tokens into another stake account + * @internal */ - static split(params: SplitStakeParams): Transaction { + static splitInstruction(params: SplitStakeParams): TransactionInstruction { const {stakePubkey, authorizedPubkey, splitStakePubkey, lamports} = params; - - const transaction = new Transaction(); - transaction.add( - SystemProgram.createAccount({ - fromPubkey: authorizedPubkey, - newAccountPubkey: splitStakePubkey, - lamports: 0, - space: this.space, - programId: this.programId, - }), - ); const type = STAKE_INSTRUCTION_LAYOUTS.Split; const data = encodeData(type, {lamports}); - - return transaction.add({ + return new TransactionInstruction({ keys: [ {pubkey: stakePubkey, isSigner: false, isWritable: true}, {pubkey: splitStakePubkey, isSigner: false, isWritable: true}, @@ -735,6 +735,56 @@ export class StakeProgram { }); } + /** + * Generate a Transaction that splits Stake tokens into another stake account + */ + static split(params: SplitStakeParams): Transaction { + const transaction = new Transaction(); + transaction.add( + SystemProgram.createAccount({ + fromPubkey: params.authorizedPubkey, + newAccountPubkey: params.splitStakePubkey, + lamports: 0, + space: this.space, + programId: this.programId, + }), + ); + return transaction.add(this.splitInstruction(params)); + } + + /** + * Generate a Transaction that splits Stake tokens into another account + * derived from a base public key and seed + */ + static splitWithSeed(params: SplitStakeWithSeedParams): Transaction { + const { + stakePubkey, + authorizedPubkey, + splitStakePubkey, + basePubkey, + seed, + lamports, + } = params; + const transaction = new Transaction(); + transaction.add( + SystemProgram.allocate({ + accountPubkey: splitStakePubkey, + basePubkey, + seed, + space: this.space, + programId: this.programId, + }), + ); + return transaction.add( + this.splitInstruction({ + stakePubkey, + authorizedPubkey, + splitStakePubkey, + lamports, + }), + ); + } + /** * Generate a Transaction that merges Stake accounts. */ diff --git a/web3.js/test/stake-program.test.ts b/web3.js/test/stake-program.test.ts index 8cf581d25..e2481fb6e 100644 --- a/web3.js/test/stake-program.test.ts +++ b/web3.js/test/stake-program.test.ts @@ -220,6 +220,46 @@ describe('StakeProgram', () => { expect(params).to.eql(StakeInstruction.decodeSplit(stakeInstruction)); }); + it('splitWithSeed', async () => { + const stakePubkey = Keypair.generate().publicKey; + const authorizedPubkey = Keypair.generate().publicKey; + const lamports = 123; + const seed = 'test string'; + const basePubkey = Keypair.generate().publicKey; + const splitStakePubkey = await PublicKey.createWithSeed( + basePubkey, + seed, + StakeProgram.programId, + ); + const transaction = StakeProgram.splitWithSeed({ + stakePubkey, + authorizedPubkey, + lamports, + splitStakePubkey, + basePubkey, + seed, + }); + expect(transaction.instructions).to.have.length(2); + const [systemInstruction, stakeInstruction] = transaction.instructions; + const systemParams = { + accountPubkey: splitStakePubkey, + basePubkey, + seed, + space: StakeProgram.space, + programId: StakeProgram.programId, + }; + expect(systemParams).to.eql( + SystemInstruction.decodeAllocateWithSeed(systemInstruction), + ); + const splitParams = { + stakePubkey, + authorizedPubkey, + splitStakePubkey, + lamports, + }; + expect(splitParams).to.eql(StakeInstruction.decodeSplit(stakeInstruction)); + }); + it('merge', () => { const stakePubkey = Keypair.generate().publicKey; const sourceStakePubKey = Keypair.generate().publicKey; @@ -420,7 +460,7 @@ describe('StakeProgram', () => { seed, authorized: new Authorized(authorized.publicKey, authorized.publicKey), lockup: new Lockup(0, 0, new PublicKey(0)), - lamports: 3 * minimumAmount + 42, + lamports: 4 * minimumAmount + 62, }); await sendAndConfirmTransaction( @@ -430,7 +470,7 @@ describe('StakeProgram', () => { {preflightCommitment: 'confirmed'}, ); let originalStakeBalance = await connection.getBalance(newAccountPubkey); - expect(originalStakeBalance).to.eq(3 * minimumAmount + 42); + expect(originalStakeBalance).to.eq(4 * minimumAmount + 62); let delegation = StakeProgram.delegate({ stakePubkey: newAccountPubkey, @@ -502,7 +542,34 @@ describe('StakeProgram', () => { }, ); const balance = await connection.getBalance(newAccountPubkey); - expect(balance).to.eq(minimumAmount + 2); + expect(balance).to.eq(2 * minimumAmount + 22); + + // Split stake with seed + const seed2 = 'test string 2'; + const newStake2 = await PublicKey.createWithSeed( + payer.publicKey, + seed2, + StakeProgram.programId, + ); + let splitWithSeed = StakeProgram.splitWithSeed({ + stakePubkey: newAccountPubkey, + authorizedPubkey: authorized.publicKey, + lamports: minimumAmount + 20, + splitStakePubkey: newStake2, + basePubkey: payer.publicKey, + seed: seed2, + }); + await sendAndConfirmTransaction( + connection, + splitWithSeed, + [payer, authorized], + { + preflightCommitment: 'confirmed', + }, + ); + expect(await connection.getBalance(newAccountPubkey)).to.eq( + minimumAmount + 2, + ); // Merge stake let merge = StakeProgram.merge({