Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 6ab15b3

Browse files
authored
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
1 parent b33bcc0 commit 6ab15b3

File tree

7 files changed

+434
-30
lines changed

7 files changed

+434
-30
lines changed

stake-pool/js/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ console.log(solanaStakePool);
5151
```javascript
5252
// `solanaStakePool` is provided in the global namespace by the script bundle.
5353
console.log(solanaStakePool);
54-
```
54+
```

stake-pool/js/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export const STAKE_POOL_PROGRAM_ID = new PublicKey('SPoo1Ku8WFXoNDMHPsrGSTSG1Y47
77
// Maximum number of validators to update during UpdateValidatorListBalance.
88
export const MAX_VALIDATORS_TO_UPDATE = 5;
99

10+
// Seed for ephemeral stake account
11+
export const EPHEMERAL_STAKE_SEED_PREFIX = Buffer.from('ephemeral');
12+
1013
// Seed used to derive transient stake accounts.
1114
export const TRANSIENT_STAKE_SEED_PREFIX = Buffer.from('transient');
1215

stake-pool/js/src/index.ts

Lines changed: 157 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
prepareWithdrawAccounts,
2525
lamportsToSol,
2626
solToLamports,
27+
findEphemeralStakeProgramAddress,
2728
} from './utils';
2829
import { StakePoolInstruction } from './instructions';
2930
import {
@@ -36,6 +37,7 @@ import {
3637
} from './layouts';
3738
import { MAX_VALIDATORS_TO_UPDATE, MINIMUM_ACTIVE_STAKE, STAKE_POOL_PROGRAM_ID } from './constants';
3839
import { create } from 'superstruct';
40+
import BN from 'bn.js';
3941

4042
export type { StakePool, AccountType, ValidatorList, ValidatorStakeInfo } from './layouts';
4143
export { STAKE_POOL_PROGRAM_ID } from './constants';
@@ -66,6 +68,17 @@ export interface StakePoolAccounts {
6668
validatorList: ValidatorListAccount | undefined;
6769
}
6870

71+
interface RedelegateProps {
72+
connection: Connection;
73+
stakePoolAddress: PublicKey;
74+
sourceVoteAccount: PublicKey;
75+
destinationVoteAccount: PublicKey;
76+
sourceTransientStakeSeed: number | BN;
77+
destinationTransientStakeSeed: number | BN;
78+
ephemeralStakeSeed: number | BN;
79+
lamports: number | BN;
80+
}
81+
6982
/**
7083
* Retrieves and deserializes a StakePool account using a web3js connection and the stake pool address.
7184
* @param connection: An active web3js connection.
@@ -401,7 +414,7 @@ export async function withdrawStake(
401414
val.voteAccountAddress.equals(voteAccount),
402415
);
403416
if (voteAccountAddress && voteAccountAddress !== voteAccount) {
404-
throw new Error(`Provided withdrawal vote account ${voteAccountAddress} does not match delegation on stake receiver account ${voteAccount},
417+
throw new Error(`Provided withdrawal vote account ${voteAccountAddress} does not match delegation on stake receiver account ${voteAccount},
405418
remove this flag or provide a different stake account delegated to ${voteAccountAddress}`);
406419
}
407420
if (isValidVoter) {
@@ -668,6 +681,7 @@ export async function increaseValidatorStake(
668681
stakePoolAddress: PublicKey,
669682
validatorVote: PublicKey,
670683
lamports: number,
684+
ephemeralStakeSeed?: number,
671685
) {
672686
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
673687

@@ -705,8 +719,14 @@ export async function increaseValidatorStake(
705719
);
706720

707721
const instructions: TransactionInstruction[] = [];
708-
instructions.push(
709-
StakePoolInstruction.increaseValidatorStake({
722+
723+
if (ephemeralStakeSeed != undefined) {
724+
const ephemeralStake = await findEphemeralStakeProgramAddress(
725+
STAKE_POOL_PROGRAM_ID,
726+
stakePoolAddress,
727+
new BN(ephemeralStakeSeed),
728+
);
729+
StakePoolInstruction.increaseAdditionalValidatorStake({
710730
stakePool: stakePoolAddress,
711731
staker: stakePool.account.data.staker,
712732
validatorList: stakePool.account.data.validatorList,
@@ -717,8 +737,25 @@ export async function increaseValidatorStake(
717737
validatorStake,
718738
validatorVote,
719739
lamports,
720-
}),
721-
);
740+
ephemeralStake,
741+
ephemeralStakeSeed,
742+
});
743+
} else {
744+
instructions.push(
745+
StakePoolInstruction.increaseValidatorStake({
746+
stakePool: stakePoolAddress,
747+
staker: stakePool.account.data.staker,
748+
validatorList: stakePool.account.data.validatorList,
749+
reserveStake: stakePool.account.data.reserveStake,
750+
transientStakeSeed: transientStakeSeed.toNumber(),
751+
withdrawAuthority,
752+
transientStake,
753+
validatorStake,
754+
validatorVote,
755+
lamports,
756+
}),
757+
);
758+
}
722759

723760
return {
724761
instructions,
@@ -733,6 +770,7 @@ export async function decreaseValidatorStake(
733770
stakePoolAddress: PublicKey,
734771
validatorVote: PublicKey,
735772
lamports: number,
773+
ephemeralStakeSeed?: number,
736774
) {
737775
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
738776
const validatorList = await getValidatorListAccount(
@@ -769,18 +807,41 @@ export async function decreaseValidatorStake(
769807
);
770808

771809
const instructions: TransactionInstruction[] = [];
772-
instructions.push(
773-
StakePoolInstruction.decreaseValidatorStake({
774-
stakePool: stakePoolAddress,
775-
staker: stakePool.account.data.staker,
776-
validatorList: stakePool.account.data.validatorList,
777-
transientStakeSeed: transientStakeSeed.toNumber(),
778-
withdrawAuthority,
779-
validatorStake,
780-
transientStake,
781-
lamports,
782-
}),
783-
);
810+
811+
if (ephemeralStakeSeed != undefined) {
812+
const ephemeralStake = await findEphemeralStakeProgramAddress(
813+
STAKE_POOL_PROGRAM_ID,
814+
stakePoolAddress,
815+
new BN(ephemeralStakeSeed),
816+
);
817+
instructions.push(
818+
StakePoolInstruction.decreaseAdditionalValidatorStake({
819+
stakePool: stakePoolAddress,
820+
staker: stakePool.account.data.staker,
821+
validatorList: stakePool.account.data.validatorList,
822+
transientStakeSeed: transientStakeSeed.toNumber(),
823+
withdrawAuthority,
824+
validatorStake,
825+
transientStake,
826+
lamports,
827+
ephemeralStake,
828+
ephemeralStakeSeed,
829+
}),
830+
);
831+
} else {
832+
instructions.push(
833+
StakePoolInstruction.decreaseValidatorStake({
834+
stakePool: stakePoolAddress,
835+
staker: stakePool.account.data.staker,
836+
validatorList: stakePool.account.data.validatorList,
837+
transientStakeSeed: transientStakeSeed.toNumber(),
838+
withdrawAuthority,
839+
validatorStake,
840+
transientStake,
841+
lamports,
842+
}),
843+
);
844+
}
784845

785846
return {
786847
instructions,
@@ -993,3 +1054,82 @@ export async function stakePoolInfo(connection: Connection, stakePoolAddress: Pu
9931054
}, // CliStakePoolDetails
9941055
};
9951056
}
1057+
1058+
/**
1059+
* Creates instructions required to redelegate stake.
1060+
*/
1061+
export async function redelegate(props: RedelegateProps) {
1062+
const {
1063+
connection,
1064+
stakePoolAddress,
1065+
sourceVoteAccount,
1066+
sourceTransientStakeSeed,
1067+
destinationVoteAccount,
1068+
destinationTransientStakeSeed,
1069+
ephemeralStakeSeed,
1070+
lamports,
1071+
} = props;
1072+
const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
1073+
1074+
const stakePoolWithdrawAuthority = await findWithdrawAuthorityProgramAddress(
1075+
STAKE_POOL_PROGRAM_ID,
1076+
stakePoolAddress,
1077+
);
1078+
1079+
const sourceValidatorStake = await findStakeProgramAddress(
1080+
STAKE_POOL_PROGRAM_ID,
1081+
sourceVoteAccount,
1082+
stakePoolAddress,
1083+
);
1084+
1085+
const sourceTransientStake = await findTransientStakeProgramAddress(
1086+
STAKE_POOL_PROGRAM_ID,
1087+
sourceVoteAccount,
1088+
stakePoolAddress,
1089+
new BN(sourceTransientStakeSeed),
1090+
);
1091+
1092+
const destinationValidatorStake = await findStakeProgramAddress(
1093+
STAKE_POOL_PROGRAM_ID,
1094+
destinationVoteAccount,
1095+
stakePoolAddress,
1096+
);
1097+
1098+
const destinationTransientStake = await findTransientStakeProgramAddress(
1099+
STAKE_POOL_PROGRAM_ID,
1100+
destinationVoteAccount,
1101+
stakePoolAddress,
1102+
new BN(destinationTransientStakeSeed),
1103+
);
1104+
1105+
const ephemeralStake = await findEphemeralStakeProgramAddress(
1106+
STAKE_POOL_PROGRAM_ID,
1107+
stakePoolAddress,
1108+
new BN(ephemeralStakeSeed),
1109+
);
1110+
1111+
const instructions: TransactionInstruction[] = [];
1112+
1113+
instructions.push(
1114+
StakePoolInstruction.redelegate({
1115+
stakePool: stakePool.pubkey,
1116+
staker: stakePool.account.data.staker,
1117+
validatorList: stakePool.account.data.validatorList,
1118+
stakePoolWithdrawAuthority,
1119+
ephemeralStake,
1120+
ephemeralStakeSeed,
1121+
sourceValidatorStake,
1122+
sourceTransientStake,
1123+
sourceTransientStakeSeed,
1124+
destinationValidatorStake,
1125+
destinationTransientStake,
1126+
destinationTransientStakeSeed,
1127+
validator: destinationVoteAccount,
1128+
lamports,
1129+
}),
1130+
);
1131+
1132+
return {
1133+
instructions,
1134+
};
1135+
}

0 commit comments

Comments
 (0)