stake-pool: add redelegate js bindings (#3960)
* - add ts/js binding for redelegate functionality * - add redelegate instructions * - refactor * - force rebuild * - refactor * - force rebuild * - force rebuild
This commit is contained in:
parent
b33bcc055e
commit
6ab15b340e
|
@ -51,4 +51,4 @@ console.log(solanaStakePool);
|
||||||
```javascript
|
```javascript
|
||||||
// `solanaStakePool` is provided in the global namespace by the script bundle.
|
// `solanaStakePool` is provided in the global namespace by the script bundle.
|
||||||
console.log(solanaStakePool);
|
console.log(solanaStakePool);
|
||||||
```
|
```
|
||||||
|
|
|
@ -7,6 +7,9 @@ export const STAKE_POOL_PROGRAM_ID = new PublicKey('SPoo1Ku8WFXoNDMHPsrGSTSG1Y47
|
||||||
// Maximum number of validators to update during UpdateValidatorListBalance.
|
// Maximum number of validators to update during UpdateValidatorListBalance.
|
||||||
export const MAX_VALIDATORS_TO_UPDATE = 5;
|
export const MAX_VALIDATORS_TO_UPDATE = 5;
|
||||||
|
|
||||||
|
// Seed for ephemeral stake account
|
||||||
|
export const EPHEMERAL_STAKE_SEED_PREFIX = Buffer.from('ephemeral');
|
||||||
|
|
||||||
// Seed used to derive transient stake accounts.
|
// Seed used to derive transient stake accounts.
|
||||||
export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient');
|
export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient');
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
prepareWithdrawAccounts,
|
prepareWithdrawAccounts,
|
||||||
lamportsToSol,
|
lamportsToSol,
|
||||||
solToLamports,
|
solToLamports,
|
||||||
|
findEphemeralStakeProgramAddress,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { StakePoolInstruction } from './instructions';
|
import { StakePoolInstruction } from './instructions';
|
||||||
import {
|
import {
|
||||||
|
@ -36,6 +37,7 @@ import {
|
||||||
} from './layouts';
|
} from './layouts';
|
||||||
import { MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE, STAKE_POOL_PROGRAM_ID } from './constants';
|
import { MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE, STAKE_POOL_PROGRAM_ID } from './constants';
|
||||||
import { create } from 'superstruct';
|
import { create } from 'superstruct';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
export type { StakePool, AccountType, ValidatorList, ValidatorStakeInfo } from './layouts';
|
export type { StakePool, AccountType, ValidatorList, ValidatorStakeInfo } from './layouts';
|
||||||
export { STAKE_POOL_PROGRAM_ID } from './constants';
|
export { STAKE_POOL_PROGRAM_ID } from './constants';
|
||||||
|
@ -66,6 +68,17 @@ export interface StakePoolAccounts {
|
||||||
validatorList: ValidatorListAccount | undefined;
|
validatorList: ValidatorListAccount | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RedelegateProps {
|
||||||
|
connection: Connection;
|
||||||
|
stakePoolAddress: PublicKey;
|
||||||
|
sourceVoteAccount: PublicKey;
|
||||||
|
destinationVoteAccount: PublicKey;
|
||||||
|
sourceTransientStakeSeed: number | BN;
|
||||||
|
destinationTransientStakeSeed: number | BN;
|
||||||
|
ephemeralStakeSeed: number | BN;
|
||||||
|
lamports: number | BN;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves and deserializes a StakePool account using a web3js connection and the stake pool address.
|
* Retrieves and deserializes a StakePool account using a web3js connection and the stake pool address.
|
||||||
* @param connection: An active web3js connection.
|
* @param connection: An active web3js connection.
|
||||||
|
@ -401,7 +414,7 @@ export async function withdrawStake(
|
||||||
val.voteAccountAddress.equals(voteAccount),
|
val.voteAccountAddress.equals(voteAccount),
|
||||||
);
|
);
|
||||||
if (voteAccountAddress && voteAccountAddress !== voteAccount) {
|
if (voteAccountAddress && voteAccountAddress !== voteAccount) {
|
||||||
throw new Error(`Provided withdrawal vote account ${voteAccountAddress} does not match delegation on stake receiver account ${voteAccount},
|
throw new Error(`Provided withdrawal vote account ${voteAccountAddress} does not match delegation on stake receiver account ${voteAccount},
|
||||||
remove this flag or provide a different stake account delegated to ${voteAccountAddress}`);
|
remove this flag or provide a different stake account delegated to ${voteAccountAddress}`);
|
||||||
}
|
}
|
||||||
if (isValidVoter) {
|
if (isValidVoter) {
|
||||||
|
@ -668,6 +681,7 @@ export async function increaseValidatorStake(
|
||||||
stakePoolAddress: PublicKey,
|
stakePoolAddress: PublicKey,
|
||||||
validatorVote: PublicKey,
|
validatorVote: PublicKey,
|
||||||
lamports: number,
|
lamports: number,
|
||||||
|
ephemeralStakeSeed?: number,
|
||||||
) {
|
) {
|
||||||
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
|
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
|
||||||
|
|
||||||
|
@ -705,8 +719,14 @@ export async function increaseValidatorStake(
|
||||||
);
|
);
|
||||||
|
|
||||||
const instructions: TransactionInstruction[] = [];
|
const instructions: TransactionInstruction[] = [];
|
||||||
instructions.push(
|
|
||||||
StakePoolInstruction.increaseValidatorStake({
|
if (ephemeralStakeSeed != undefined) {
|
||||||
|
const ephemeralStake = await findEphemeralStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
stakePoolAddress,
|
||||||
|
new BN(ephemeralStakeSeed),
|
||||||
|
);
|
||||||
|
StakePoolInstruction.increaseAdditionalValidatorStake({
|
||||||
stakePool: stakePoolAddress,
|
stakePool: stakePoolAddress,
|
||||||
staker: stakePool.account.data.staker,
|
staker: stakePool.account.data.staker,
|
||||||
validatorList: stakePool.account.data.validatorList,
|
validatorList: stakePool.account.data.validatorList,
|
||||||
|
@ -717,8 +737,25 @@ export async function increaseValidatorStake(
|
||||||
validatorStake,
|
validatorStake,
|
||||||
validatorVote,
|
validatorVote,
|
||||||
lamports,
|
lamports,
|
||||||
}),
|
ephemeralStake,
|
||||||
);
|
ephemeralStakeSeed,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
instructions.push(
|
||||||
|
StakePoolInstruction.increaseValidatorStake({
|
||||||
|
stakePool: stakePoolAddress,
|
||||||
|
staker: stakePool.account.data.staker,
|
||||||
|
validatorList: stakePool.account.data.validatorList,
|
||||||
|
reserveStake: stakePool.account.data.reserveStake,
|
||||||
|
transientStakeSeed: transientStakeSeed.toNumber(),
|
||||||
|
withdrawAuthority,
|
||||||
|
transientStake,
|
||||||
|
validatorStake,
|
||||||
|
validatorVote,
|
||||||
|
lamports,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instructions,
|
instructions,
|
||||||
|
@ -733,6 +770,7 @@ export async function decreaseValidatorStake(
|
||||||
stakePoolAddress: PublicKey,
|
stakePoolAddress: PublicKey,
|
||||||
validatorVote: PublicKey,
|
validatorVote: PublicKey,
|
||||||
lamports: number,
|
lamports: number,
|
||||||
|
ephemeralStakeSeed?: number,
|
||||||
) {
|
) {
|
||||||
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
|
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
|
||||||
const validatorList = await getValidatorListAccount(
|
const validatorList = await getValidatorListAccount(
|
||||||
|
@ -769,18 +807,41 @@ export async function decreaseValidatorStake(
|
||||||
);
|
);
|
||||||
|
|
||||||
const instructions: TransactionInstruction[] = [];
|
const instructions: TransactionInstruction[] = [];
|
||||||
instructions.push(
|
|
||||||
StakePoolInstruction.decreaseValidatorStake({
|
if (ephemeralStakeSeed != undefined) {
|
||||||
stakePool: stakePoolAddress,
|
const ephemeralStake = await findEphemeralStakeProgramAddress(
|
||||||
staker: stakePool.account.data.staker,
|
STAKE_POOL_PROGRAM_ID,
|
||||||
validatorList: stakePool.account.data.validatorList,
|
stakePoolAddress,
|
||||||
transientStakeSeed: transientStakeSeed.toNumber(),
|
new BN(ephemeralStakeSeed),
|
||||||
withdrawAuthority,
|
);
|
||||||
validatorStake,
|
instructions.push(
|
||||||
transientStake,
|
StakePoolInstruction.decreaseAdditionalValidatorStake({
|
||||||
lamports,
|
stakePool: stakePoolAddress,
|
||||||
}),
|
staker: stakePool.account.data.staker,
|
||||||
);
|
validatorList: stakePool.account.data.validatorList,
|
||||||
|
transientStakeSeed: transientStakeSeed.toNumber(),
|
||||||
|
withdrawAuthority,
|
||||||
|
validatorStake,
|
||||||
|
transientStake,
|
||||||
|
lamports,
|
||||||
|
ephemeralStake,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
instructions.push(
|
||||||
|
StakePoolInstruction.decreaseValidatorStake({
|
||||||
|
stakePool: stakePoolAddress,
|
||||||
|
staker: stakePool.account.data.staker,
|
||||||
|
validatorList: stakePool.account.data.validatorList,
|
||||||
|
transientStakeSeed: transientStakeSeed.toNumber(),
|
||||||
|
withdrawAuthority,
|
||||||
|
validatorStake,
|
||||||
|
transientStake,
|
||||||
|
lamports,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instructions,
|
instructions,
|
||||||
|
@ -993,3 +1054,82 @@ export async function stakePoolInfo(connection: Connection, stakePoolAddress: Pu
|
||||||
}, // CliStakePoolDetails
|
}, // CliStakePoolDetails
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates instructions required to redelegate stake.
|
||||||
|
*/
|
||||||
|
export async function redelegate(props: RedelegateProps) {
|
||||||
|
const {
|
||||||
|
connection,
|
||||||
|
stakePoolAddress,
|
||||||
|
sourceVoteAccount,
|
||||||
|
sourceTransientStakeSeed,
|
||||||
|
destinationVoteAccount,
|
||||||
|
destinationTransientStakeSeed,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
lamports,
|
||||||
|
} = props;
|
||||||
|
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
|
||||||
|
|
||||||
|
const stakePoolWithdrawAuthority = await findWithdrawAuthorityProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
stakePoolAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceValidatorStake = await findStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
sourceVoteAccount,
|
||||||
|
stakePoolAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceTransientStake = await findTransientStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
sourceVoteAccount,
|
||||||
|
stakePoolAddress,
|
||||||
|
new BN(sourceTransientStakeSeed),
|
||||||
|
);
|
||||||
|
|
||||||
|
const destinationValidatorStake = await findStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
destinationVoteAccount,
|
||||||
|
stakePoolAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
const destinationTransientStake = await findTransientStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
destinationVoteAccount,
|
||||||
|
stakePoolAddress,
|
||||||
|
new BN(destinationTransientStakeSeed),
|
||||||
|
);
|
||||||
|
|
||||||
|
const ephemeralStake = await findEphemeralStakeProgramAddress(
|
||||||
|
STAKE_POOL_PROGRAM_ID,
|
||||||
|
stakePoolAddress,
|
||||||
|
new BN(ephemeralStakeSeed),
|
||||||
|
);
|
||||||
|
|
||||||
|
const instructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
StakePoolInstruction.redelegate({
|
||||||
|
stakePool: stakePool.pubkey,
|
||||||
|
staker: stakePool.account.data.staker,
|
||||||
|
validatorList: stakePool.account.data.validatorList,
|
||||||
|
stakePoolWithdrawAuthority,
|
||||||
|
ephemeralStake,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
sourceValidatorStake,
|
||||||
|
sourceTransientStake,
|
||||||
|
sourceTransientStakeSeed,
|
||||||
|
destinationValidatorStake,
|
||||||
|
destinationTransientStake,
|
||||||
|
destinationTransientStakeSeed,
|
||||||
|
validator: destinationVoteAccount,
|
||||||
|
lamports,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
instructions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import * as BufferLayout from '@solana/buffer-layout';
|
||||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||||
import { STAKE_POOL_PROGRAM_ID } from './constants';
|
import { STAKE_POOL_PROGRAM_ID } from './constants';
|
||||||
import { InstructionType, encodeData, decodeData } from './utils';
|
import { InstructionType, encodeData, decodeData } from './utils';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enumeration of valid StakePoolInstructionType's
|
* An enumeration of valid StakePoolInstructionType's
|
||||||
|
@ -25,7 +26,10 @@ export type StakePoolInstructionType =
|
||||||
| 'DepositStake'
|
| 'DepositStake'
|
||||||
| 'DepositSol'
|
| 'DepositSol'
|
||||||
| 'WithdrawStake'
|
| 'WithdrawStake'
|
||||||
| 'WithdrawSol';
|
| 'WithdrawSol'
|
||||||
|
| 'IncreaseAdditionalValidatorStake'
|
||||||
|
| 'DecreaseAdditionalValidatorStake'
|
||||||
|
| 'Redelegate';
|
||||||
|
|
||||||
const MOVE_STAKE_LAYOUT = BufferLayout.struct<any>([
|
const MOVE_STAKE_LAYOUT = BufferLayout.struct<any>([
|
||||||
BufferLayout.u8('instruction'),
|
BufferLayout.u8('instruction'),
|
||||||
|
@ -96,6 +100,40 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
|
||||||
BufferLayout.ns64('poolTokens'),
|
BufferLayout.ns64('poolTokens'),
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
|
IncreaseAdditionalValidatorStake: {
|
||||||
|
index: 19,
|
||||||
|
layout: BufferLayout.struct<any>([
|
||||||
|
BufferLayout.u8('instruction'),
|
||||||
|
BufferLayout.ns64('lamports'),
|
||||||
|
BufferLayout.ns64('transientStakeSeed'),
|
||||||
|
BufferLayout.ns64('ephemeralStakeSeed'),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
DecreaseAdditionalValidatorStake: {
|
||||||
|
index: 20,
|
||||||
|
layout: BufferLayout.struct<any>([
|
||||||
|
BufferLayout.u8('instruction'),
|
||||||
|
BufferLayout.ns64('lamports'),
|
||||||
|
BufferLayout.ns64('transientStakeSeed'),
|
||||||
|
BufferLayout.ns64('ephemeralStakeSeed'),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
Redelegate: {
|
||||||
|
index: 21,
|
||||||
|
layout: BufferLayout.struct<any>([
|
||||||
|
BufferLayout.u8('instruction'),
|
||||||
|
/// Amount of lamports to redelegate
|
||||||
|
BufferLayout.ns64('lamports'),
|
||||||
|
/// Seed used to create source transient stake account
|
||||||
|
BufferLayout.ns64('sourceTransientStakeSeed'),
|
||||||
|
/// Seed used to create destination ephemeral account.
|
||||||
|
BufferLayout.ns64('ephemeralStakeSeed'),
|
||||||
|
/// Seed used to create destination transient stake account. If there is
|
||||||
|
/// already transient stake, this must match the current seed, otherwise
|
||||||
|
/// it can be anything
|
||||||
|
BufferLayout.ns64('destinationTransientStakeSeed'),
|
||||||
|
]),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,12 +179,17 @@ export type DecreaseValidatorStakeParams = {
|
||||||
validatorList: PublicKey;
|
validatorList: PublicKey;
|
||||||
validatorStake: PublicKey;
|
validatorStake: PublicKey;
|
||||||
transientStake: PublicKey;
|
transientStake: PublicKey;
|
||||||
// Amount of lamports to split into the transient stake account.
|
// Amount of lamports to split into the transient stake account
|
||||||
lamports: number;
|
lamports: number;
|
||||||
// Seed to used to create the transient stake account.
|
// Seed to used to create the transient stake account
|
||||||
transientStakeSeed: number;
|
transientStakeSeed: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface DecreaseAdditionalValidatorStakeParams extends DecreaseValidatorStakeParams {
|
||||||
|
ephemeralStake: PublicKey;
|
||||||
|
ephemeralStakeSeed: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Staker only) Increase stake on a validator from the reserve account.
|
* (Staker only) Increase stake on a validator from the reserve account.
|
||||||
*/
|
*/
|
||||||
|
@ -159,12 +202,17 @@ export type IncreaseValidatorStakeParams = {
|
||||||
transientStake: PublicKey;
|
transientStake: PublicKey;
|
||||||
validatorStake: PublicKey;
|
validatorStake: PublicKey;
|
||||||
validatorVote: PublicKey;
|
validatorVote: PublicKey;
|
||||||
// Amount of lamports to split into the transient stake account.
|
// Amount of lamports to split into the transient stake account
|
||||||
lamports: number;
|
lamports: number;
|
||||||
// Seed to used to create the transient stake account.
|
// Seed to used to create the transient stake account
|
||||||
transientStakeSeed: number;
|
transientStakeSeed: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IncreaseAdditionalValidatorStakeParams extends IncreaseValidatorStakeParams {
|
||||||
|
ephemeralStake: PublicKey;
|
||||||
|
ephemeralStakeSeed: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deposits a stake account into the pool in exchange for pool tokens
|
* Deposits a stake account into the pool in exchange for pool tokens
|
||||||
*/
|
*/
|
||||||
|
@ -232,6 +280,29 @@ export type DepositSolParams = {
|
||||||
lamports: number;
|
lamports: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RedelegateParams = {
|
||||||
|
stakePool: PublicKey;
|
||||||
|
staker: PublicKey;
|
||||||
|
stakePoolWithdrawAuthority: PublicKey;
|
||||||
|
validatorList: PublicKey;
|
||||||
|
sourceValidatorStake: PublicKey;
|
||||||
|
sourceTransientStake: PublicKey;
|
||||||
|
ephemeralStake: PublicKey;
|
||||||
|
destinationTransientStake: PublicKey;
|
||||||
|
destinationValidatorStake: PublicKey;
|
||||||
|
validator: PublicKey;
|
||||||
|
// Amount of lamports to redelegate
|
||||||
|
lamports: number | BN;
|
||||||
|
// Seed used to create source transient stake account
|
||||||
|
sourceTransientStakeSeed: number | BN;
|
||||||
|
// Seed used to create destination ephemeral account
|
||||||
|
ephemeralStakeSeed: number | BN;
|
||||||
|
// Seed used to create destination transient stake account. If there is
|
||||||
|
// already transient stake, this must match the current seed, otherwise
|
||||||
|
// it can be anything
|
||||||
|
destinationTransientStakeSeed: number | BN;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stake Pool Instruction class
|
* Stake Pool Instruction class
|
||||||
*/
|
*/
|
||||||
|
@ -334,7 +405,8 @@ export class StakePoolInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instruction to increase the stake on a validator.
|
* Creates `IncreaseValidatorStake` instruction (rebalance from reserve account to
|
||||||
|
* transient account)
|
||||||
*/
|
*/
|
||||||
static increaseValidatorStake(params: IncreaseValidatorStakeParams): TransactionInstruction {
|
static increaseValidatorStake(params: IncreaseValidatorStakeParams): TransactionInstruction {
|
||||||
const {
|
const {
|
||||||
|
@ -378,7 +450,57 @@ export class StakePoolInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instruction to decrease the stake on a validator.
|
* Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from reserve account to
|
||||||
|
* transient account)
|
||||||
|
*/
|
||||||
|
static increaseAdditionalValidatorStake(
|
||||||
|
params: IncreaseAdditionalValidatorStakeParams,
|
||||||
|
): TransactionInstruction {
|
||||||
|
const {
|
||||||
|
stakePool,
|
||||||
|
staker,
|
||||||
|
withdrawAuthority,
|
||||||
|
validatorList,
|
||||||
|
reserveStake,
|
||||||
|
transientStake,
|
||||||
|
validatorStake,
|
||||||
|
validatorVote,
|
||||||
|
lamports,
|
||||||
|
transientStakeSeed,
|
||||||
|
ephemeralStake,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseAdditionalValidatorStake;
|
||||||
|
const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{ pubkey: stakePool, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: staker, isSigner: true, isWritable: false },
|
||||||
|
{ pubkey: withdrawAuthority, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: validatorList, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: reserveStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: ephemeralStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: transientStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: validatorStake, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: validatorVote, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: STAKE_CONFIG_ID, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
return new TransactionInstruction({
|
||||||
|
programId: STAKE_POOL_PROGRAM_ID,
|
||||||
|
keys,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates `DecreaseValidatorStake` instruction (rebalance from validator account to
|
||||||
|
* transient account)
|
||||||
*/
|
*/
|
||||||
static decreaseValidatorStake(params: DecreaseValidatorStakeParams): TransactionInstruction {
|
static decreaseValidatorStake(params: DecreaseValidatorStakeParams): TransactionInstruction {
|
||||||
const {
|
const {
|
||||||
|
@ -415,6 +537,50 @@ export class StakePoolInstruction {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from
|
||||||
|
* validator account to transient account)
|
||||||
|
*/
|
||||||
|
static decreaseAdditionalValidatorStake(
|
||||||
|
params: DecreaseAdditionalValidatorStakeParams,
|
||||||
|
): TransactionInstruction {
|
||||||
|
const {
|
||||||
|
stakePool,
|
||||||
|
staker,
|
||||||
|
withdrawAuthority,
|
||||||
|
validatorList,
|
||||||
|
validatorStake,
|
||||||
|
transientStake,
|
||||||
|
lamports,
|
||||||
|
transientStakeSeed,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
ephemeralStake,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseAdditionalValidatorStake;
|
||||||
|
const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{ pubkey: stakePool, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: staker, isSigner: true, isWritable: false },
|
||||||
|
{ pubkey: withdrawAuthority, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: validatorList, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: validatorStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: ephemeralStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: transientStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
return new TransactionInstruction({
|
||||||
|
programId: STAKE_POOL_PROGRAM_ID,
|
||||||
|
keys,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a transaction instruction to deposit a stake account into a stake pool.
|
* Creates a transaction instruction to deposit a stake account into a stake pool.
|
||||||
*/
|
*/
|
||||||
|
@ -603,6 +769,60 @@ export class StakePoolInstruction {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates `Redelegate` instruction (rebalance from one validator account to another)
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
static redelegate(params: RedelegateParams): TransactionInstruction {
|
||||||
|
const {
|
||||||
|
stakePool,
|
||||||
|
staker,
|
||||||
|
stakePoolWithdrawAuthority,
|
||||||
|
validatorList,
|
||||||
|
sourceValidatorStake,
|
||||||
|
sourceTransientStake,
|
||||||
|
ephemeralStake,
|
||||||
|
destinationTransientStake,
|
||||||
|
destinationValidatorStake,
|
||||||
|
validator,
|
||||||
|
lamports,
|
||||||
|
sourceTransientStakeSeed,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
destinationTransientStakeSeed,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{ pubkey: stakePool, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: staker, isSigner: true, isWritable: false },
|
||||||
|
{ pubkey: stakePoolWithdrawAuthority, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: validatorList, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: sourceValidatorStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: sourceTransientStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: ephemeralStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: destinationTransientStake, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: destinationValidatorStake, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: validator, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: STAKE_CONFIG_ID, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const data = encodeData(STAKE_POOL_INSTRUCTION_LAYOUTS.Redelegate, {
|
||||||
|
lamports,
|
||||||
|
sourceTransientStakeSeed,
|
||||||
|
ephemeralStakeSeed,
|
||||||
|
destinationTransientStakeSeed,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new TransactionInstruction({
|
||||||
|
programId: STAKE_POOL_PROGRAM_ID,
|
||||||
|
keys,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a deposit stake pool instruction and retrieve the instruction params.
|
* Decode a deposit stake pool instruction and retrieve the instruction params.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import { TRANSIENT_STAKE_SEED_PREFIX } from '../constants';
|
import { EPHEMERAL_STAKE_SEED_PREFIX, TRANSIENT_STAKE_SEED_PREFIX } from '../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the withdraw authority program address for the stake pool
|
* Generates the withdraw authority program address for the stake pool
|
||||||
|
@ -46,9 +46,24 @@ export async function findTransientStakeProgramAddress(
|
||||||
TRANSIENT_STAKE_SEED_PREFIX,
|
TRANSIENT_STAKE_SEED_PREFIX,
|
||||||
voteAccountAddress.toBuffer(),
|
voteAccountAddress.toBuffer(),
|
||||||
stakePoolAddress.toBuffer(),
|
stakePoolAddress.toBuffer(),
|
||||||
new Uint8Array(seed.toArray('le', 8)),
|
seed.toBuffer('le', 8),
|
||||||
],
|
],
|
||||||
programId,
|
programId,
|
||||||
);
|
);
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ephemeral program address for stake pool redelegation
|
||||||
|
*/
|
||||||
|
export async function findEphemeralStakeProgramAddress(
|
||||||
|
programId: PublicKey,
|
||||||
|
stakePoolAddress: PublicKey,
|
||||||
|
seed: BN,
|
||||||
|
) {
|
||||||
|
const [publicKey] = await PublicKey.findProgramAddress(
|
||||||
|
[EPHEMERAL_STAKE_SEED_PREFIX, stakePoolAddress.toBuffer(), seed.toBuffer('le', 8)],
|
||||||
|
programId,
|
||||||
|
);
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
depositSol,
|
depositSol,
|
||||||
withdrawSol,
|
withdrawSol,
|
||||||
withdrawStake,
|
withdrawStake,
|
||||||
|
redelegate,
|
||||||
getStakeAccount,
|
getStakeAccount,
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
|
@ -317,4 +318,30 @@ describe('StakePoolProgram', () => {
|
||||||
expect(parsedStakeAccount).toEqual(uninitializedStakeAccount.parsed);
|
expect(parsedStakeAccount).toEqual(uninitializedStakeAccount.parsed);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('redelegation', () => {
|
||||||
|
it.only('should call successfully', async () => {
|
||||||
|
const data = {
|
||||||
|
connection,
|
||||||
|
stakePoolAddress,
|
||||||
|
sourceVoteAccount: PublicKey.default,
|
||||||
|
sourceTransientStakeSeed: 10,
|
||||||
|
destinationVoteAccount: PublicKey.default,
|
||||||
|
destinationTransientStakeSeed: 20,
|
||||||
|
ephemeralStakeSeed: 100,
|
||||||
|
lamports: 100,
|
||||||
|
};
|
||||||
|
const res = await redelegate(data);
|
||||||
|
|
||||||
|
const decodedData = STAKE_POOL_INSTRUCTION_LAYOUTS.Redelegate.layout.decode(
|
||||||
|
res.instructions[0].data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(decodedData.instruction).toBe(21);
|
||||||
|
expect(decodedData.lamports).toBe(data.lamports);
|
||||||
|
expect(decodedData.sourceTransientStakeSeed).toBe(data.sourceTransientStakeSeed);
|
||||||
|
expect(decodedData.destinationTransientStakeSeed).toBe(data.destinationTransientStakeSeed);
|
||||||
|
expect(decodedData.ephemeralStakeSeed).toBe(data.ephemeralStakeSeed);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AccountInfo, LAMPORTS_PER_SOL, PublicKey, StakeProgram } from '@solana/web3.js';
|
import { AccountInfo, LAMPORTS_PER_SOL, PublicKey, StakeProgram } from '@solana/web3.js';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { ValidatorStakeInfo } from '../src';
|
import { ValidatorStakeInfo } from '../src';
|
||||||
import { ValidatorStakeInfoStatus, AccountLayout, ValidatorListLayout } from '../src/layouts';
|
import { AccountLayout, ValidatorListLayout, ValidatorStakeInfoStatus } from '../src/layouts';
|
||||||
|
|
||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
poolTokenAccount: new PublicKey(
|
poolTokenAccount: new PublicKey(
|
||||||
|
@ -149,13 +149,12 @@ export const mockRpc = (data: any): any => {
|
||||||
executable: false,
|
executable: false,
|
||||||
rentEpoch: 0,
|
rentEpoch: 0,
|
||||||
};
|
};
|
||||||
const result = {
|
return {
|
||||||
context: {
|
context: {
|
||||||
slot: 11,
|
slot: 11,
|
||||||
},
|
},
|
||||||
value: value,
|
value: value,
|
||||||
};
|
};
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stakeAccountData = {
|
export const stakeAccountData = {
|
||||||
|
|
Loading…
Reference in New Issue