123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- import assert from "assert";
- import { splStakePoolProgram } from "@coral-xyz/spl-stake-pool";
- import { splTokenProgram } from "@coral-xyz/spl-token";
- import { BN } from "@coral-xyz/anchor";
- import {
- Keypair,
- LAMPORTS_PER_SOL,
- PublicKey,
- StakeProgram,
- STAKE_CONFIG_ID,
- SystemProgram,
- SYSVAR_CLOCK_PUBKEY,
- SYSVAR_RENT_PUBKEY,
- SYSVAR_STAKE_HISTORY_PUBKEY,
- VoteProgram,
- } from "@solana/web3.js";
- import { SPL_STAKE_POOL_PROGRAM_ID, SPL_TOKEN_PROGRAM_ID } from "../constants";
- import {
- createAta,
- createMint,
- createTokenAccount,
- getProvider,
- loadKp,
- sendAndConfirmTx,
- simulateTx,
- test,
- } from "../utils";
- export async function stakePoolTests() {
- const provider = await getProvider();
- const program = splStakePoolProgram({
- provider,
- programId: SPL_STAKE_POOL_PROGRAM_ID,
- });
- const tokenProgram = splTokenProgram({
- provider,
- programId: SPL_TOKEN_PROGRAM_ID,
- });
- const kp = await loadKp();
- const VALIDATOR_LIST_COUNT = 10;
- const TRANSIENT_STAKE_SEED = 0;
- let stakePoolPk: PublicKey;
- let withdrawAuthorityPk: PublicKey;
- let poolMintPk: PublicKey;
- let managerPoolTokenAccountPk: PublicKey;
- let validatorListPk: PublicKey;
- let reserveStakePk: PublicKey;
- let stakeAccountPk: PublicKey;
- let voteAccountPk: PublicKey;
- let transientStakePk: PublicKey;
- let userPoolTokenAccountPk: PublicKey;
- async function initialize() {
- const stakePoolKp = new Keypair();
- stakePoolPk = stakePoolKp.publicKey;
- const createStakePoolAccountIx =
- await program.account.stakePool.createInstruction(stakePoolKp, 5000);
- [withdrawAuthorityPk] = await PublicKey.findProgramAddress(
- [stakePoolPk.toBuffer(), Buffer.from("withdraw")],
- program.programId
- );
- poolMintPk = await createMint(withdrawAuthorityPk);
- userPoolTokenAccountPk = await createAta(poolMintPk, kp.publicKey);
- // Fee account
- managerPoolTokenAccountPk = await createTokenAccount(poolMintPk);
- const validatorListKp = new Keypair();
- validatorListPk = validatorListKp.publicKey;
- const createValidatorListAccountIx =
- await program.account.validatorList.createInstruction(
- validatorListKp,
- 5 + 4 + 73 * VALIDATOR_LIST_COUNT
- );
- const reserveStakeKp = new Keypair();
- reserveStakePk = reserveStakeKp.publicKey;
- const createReserveStakeAccountIxs = StakeProgram.createAccount({
- authorized: {
- staker: withdrawAuthorityPk,
- withdrawer: withdrawAuthorityPk,
- },
- fromPubkey: kp.publicKey,
- lamports:
- (await provider.connection.getMinimumBalanceForRentExemption(
- StakeProgram.space
- )) +
- LAMPORTS_PER_SOL * 11,
- stakePubkey: reserveStakePk,
- }).instructions;
- const initStakePoolIx = await program.methods
- .initialize(
- {
- denominator: new BN(10),
- numerator: new BN(1),
- },
- {
- denominator: new BN(10),
- numerator: new BN(1),
- },
- {
- denominator: new BN(10),
- numerator: new BN(1),
- },
- 10,
- VALIDATOR_LIST_COUNT
- )
- .accounts({
- stakePool: stakePoolKp.publicKey,
- manager: kp.publicKey,
- staker: kp.publicKey,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- validatorList: validatorListKp.publicKey,
- reserveStake: reserveStakePk,
- poolMint: poolMintPk,
- managerPoolAccount: managerPoolTokenAccountPk,
- tokenProgram: tokenProgram.programId,
- })
- .instruction();
- await sendAndConfirmTx(
- [
- createStakePoolAccountIx,
- ...createReserveStakeAccountIxs,
- createValidatorListAccountIx,
- initStakePoolIx,
- ],
- [kp, stakePoolKp, validatorListKp, reserveStakeKp]
- );
- }
- async function addValidatorToPool() {
- const voteAccountKp = new Keypair();
- voteAccountPk = voteAccountKp.publicKey;
- const identityKp = new Keypair();
- const createVoteAccountIxs = VoteProgram.createAccount({
- fromPubkey: kp.publicKey,
- lamports: await provider.connection.getMinimumBalanceForRentExemption(
- VoteProgram.space
- ),
- voteInit: {
- authorizedVoter: kp.publicKey,
- authorizedWithdrawer: kp.publicKey,
- commission: 1,
- nodePubkey: identityKp.publicKey,
- },
- votePubkey: voteAccountKp.publicKey,
- }).instructions;
- [stakeAccountPk] = await PublicKey.findProgramAddress(
- [voteAccountPk.toBuffer(), stakePoolPk.toBuffer()],
- program.programId
- );
- [transientStakePk] = await PublicKey.findProgramAddress(
- [
- Buffer.from("transient"),
- voteAccountPk.toBuffer(),
- stakePoolPk.toBuffer(),
- new BN(TRANSIENT_STAKE_SEED).toBuffer("le", 8), // Transient seed suffix start
- ],
- program.programId
- );
- const addValidatorIx = await program.methods
- .addValidatorToPool()
- .accounts({
- stakePool: stakePoolPk,
- staker: kp.publicKey,
- funder: kp.publicKey,
- stakePoolWithdraw: withdrawAuthorityPk,
- validatorList: validatorListPk,
- stake: stakeAccountPk,
- validator: voteAccountPk,
- rent: SYSVAR_RENT_PUBKEY,
- clock: SYSVAR_CLOCK_PUBKEY,
- sysvarStakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
- stakeConfig: STAKE_CONFIG_ID,
- systemProgram: SystemProgram.programId,
- stakeProgram: StakeProgram.programId,
- })
- .instruction();
- await sendAndConfirmTx(
- [...createVoteAccountIxs, addValidatorIx],
- [kp, voteAccountKp, identityKp]
- );
- }
- async function removeValidatorFromPool() {
- const destinationStakeAccountKp = new Keypair();
- const createDestinationStakeAccountIx = SystemProgram.createAccount({
- fromPubkey: kp.publicKey,
- lamports: 0,
- newAccountPubkey: destinationStakeAccountKp.publicKey,
- programId: StakeProgram.programId,
- space: StakeProgram.space,
- });
- const removeValidatorIx = await program.methods
- .removeValidatorFromPool()
- .accounts({
- stakePool: stakePoolPk,
- staker: kp.publicKey,
- stakePoolWithdraw: withdrawAuthorityPk,
- newStakeAuthority: kp.publicKey,
- validatorList: validatorListPk,
- stakeAccount: stakeAccountPk,
- transientStakeAccount: transientStakePk,
- destinationStakeAccount: destinationStakeAccountKp.publicKey,
- clock: SYSVAR_CLOCK_PUBKEY,
- stakeProgram: StakeProgram.programId,
- })
- .instruction();
- await sendAndConfirmTx(
- [createDestinationStakeAccountIx, removeValidatorIx],
- [kp, destinationStakeAccountKp]
- );
- }
- async function increaseValidatorStake() {
- await program.methods
- .increaseValidatorStake(
- new BN(LAMPORTS_PER_SOL),
- new BN(TRANSIENT_STAKE_SEED)
- )
- .accounts({
- stakePool: stakePoolPk,
- staker: kp.publicKey,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- validatorList: validatorListPk,
- reserveStake: reserveStakePk,
- transientStake: transientStakePk,
- validatorStake: stakeAccountPk,
- validator: voteAccountPk,
- clock: SYSVAR_CLOCK_PUBKEY,
- rent: SYSVAR_RENT_PUBKEY,
- sysvarStakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
- stakeConfig: STAKE_CONFIG_ID,
- systemProgram: SystemProgram.programId,
- stakeProgram: StakeProgram.programId,
- })
- .rpc();
- }
- async function decreaseValidatorStake() {
- const decreaseValidatorStakeIx = await program.methods
- .decreaseValidatorStake(
- new BN(LAMPORTS_PER_SOL),
- new BN(TRANSIENT_STAKE_SEED + 1)
- )
- .accounts({
- stakePool: stakePoolPk,
- staker: kp.publicKey,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- validatorList: validatorListPk,
- validatorStake: stakeAccountPk,
- transientStake: (
- await PublicKey.findProgramAddress(
- [
- Buffer.from("transient"),
- voteAccountPk.toBuffer(),
- stakePoolPk.toBuffer(),
- new BN(TRANSIENT_STAKE_SEED + 1).toBuffer("le", 8),
- ],
- program.programId
- )
- )[0],
- clock: SYSVAR_CLOCK_PUBKEY,
- rent: SYSVAR_RENT_PUBKEY,
- systemProgram: SystemProgram.programId,
- stakeProgram: StakeProgram.programId,
- })
- .instruction();
- await simulateTx([decreaseValidatorStakeIx], [kp]);
- }
- async function setPreferredValidator() {
- await program.methods
- .setPreferredValidator({ deposit: {} }, null)
- .accounts({
- stakePoolAddress: stakePoolPk,
- staker: kp.publicKey,
- validatorListAddress: validatorListPk,
- })
- .rpc();
- }
- async function updateValidatorListBalance() {
- await program.methods
- .updateValidatorListBalance(0, true)
- .accounts({
- stakePool: stakePoolPk,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- validatorListAddress: validatorListPk,
- reserveStake: reserveStakePk,
- clock: SYSVAR_CLOCK_PUBKEY,
- sysvarStakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
- stakeProgram: StakeProgram.programId,
- })
- .rpc();
- }
- async function updateStakePoolBalance() {
- await program.methods
- .updateStakePoolBalance()
- .accounts({
- stakePool: stakePoolPk,
- withdrawAuthority: withdrawAuthorityPk,
- validatorListStorage: validatorListPk,
- reserveStake: reserveStakePk,
- managerFeeAccount: managerPoolTokenAccountPk,
- stakePoolMint: poolMintPk,
- tokenProgram: tokenProgram.programId,
- })
- .rpc();
- }
- async function cleanupRemovedValidatorEntries() {
- await program.methods
- .cleanupRemovedValidatorEntries()
- .accounts({
- stakePool: stakePoolPk,
- validatorListStorage: validatorListPk,
- })
- .rpc();
- }
- async function depositStake() {
- const DEPOSIT_AMOUNT = LAMPORTS_PER_SOL;
- const [poolDepositAuthorityPk] = await PublicKey.findProgramAddress(
- [stakePoolPk.toBuffer(), Buffer.from("deposit")],
- program.programId
- );
- const userStakeAccountKp = new Keypair();
- const createUserStakeAccountIxs = StakeProgram.createAccount({
- authorized: { staker: kp.publicKey, withdrawer: kp.publicKey },
- fromPubkey: kp.publicKey,
- lamports:
- (await provider.connection.getMinimumBalanceForRentExemption(
- StakeProgram.space
- )) + DEPOSIT_AMOUNT,
- stakePubkey: userStakeAccountKp.publicKey,
- }).instructions;
- const delegateUserStakeAccountIxs = StakeProgram.delegate({
- authorizedPubkey: kp.publicKey,
- stakePubkey: userStakeAccountKp.publicKey,
- votePubkey: voteAccountPk,
- }).instructions;
- const authorizeStakerIxs = StakeProgram.authorize({
- authorizedPubkey: kp.publicKey,
- newAuthorizedPubkey: poolDepositAuthorityPk,
- stakeAuthorizationType: { index: 0 },
- stakePubkey: userStakeAccountKp.publicKey,
- }).instructions;
- const authorizeWithdrawerIxs = StakeProgram.authorize({
- authorizedPubkey: kp.publicKey,
- newAuthorizedPubkey: poolDepositAuthorityPk,
- stakeAuthorizationType: { index: 1 },
- stakePubkey: userStakeAccountKp.publicKey,
- }).instructions;
- const depositStakeIx = await program.methods
- .depositStake()
- .accounts({
- stakePool: stakePoolPk,
- validatorListStorage: validatorListPk,
- stakePoolDepositAuthority: poolDepositAuthorityPk,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- depositStakeAddress: userStakeAccountKp.publicKey,
- validatorStakeAccount: stakeAccountPk,
- reserveStakeAccount: reserveStakePk,
- poolTokensTo: userPoolTokenAccountPk,
- managerFeeAccount: managerPoolTokenAccountPk,
- referrerPoolTokensAccount: managerPoolTokenAccountPk,
- poolMint: poolMintPk,
- clock: SYSVAR_CLOCK_PUBKEY,
- sysvarStakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
- tokenProgram: tokenProgram.programId,
- stakeProgram: StakeProgram.programId,
- })
- .instruction();
- await sendAndConfirmTx(
- [
- ...createUserStakeAccountIxs,
- ...delegateUserStakeAccountIxs,
- ...authorizeStakerIxs,
- ...authorizeWithdrawerIxs,
- depositStakeIx,
- ],
- [kp, userStakeAccountKp]
- );
- }
- async function withdrawStake() {
- const userStakeAccountKp = new Keypair();
- const createUserStakeAccountIx = SystemProgram.createAccount({
- fromPubkey: kp.publicKey,
- lamports: await provider.connection.getMinimumBalanceForRentExemption(
- StakeProgram.space
- ),
- newAccountPubkey: userStakeAccountKp.publicKey,
- programId: StakeProgram.programId,
- space: StakeProgram.space,
- });
- const withdrawStakeIx = await program.methods
- .withdrawStake(new BN(1))
- .accounts({
- stakePool: stakePoolPk,
- validatorListStorage: validatorListPk,
- stakePoolWithdraw: withdrawAuthorityPk,
- stakeToSplit: stakeAccountPk,
- stakeToReceive: userStakeAccountKp.publicKey,
- userStakeAuthority: kp.publicKey,
- userTransferAuthority: kp.publicKey,
- userPoolTokenAccount: userPoolTokenAccountPk,
- managerFeeAccount: managerPoolTokenAccountPk,
- poolMint: poolMintPk,
- clock: SYSVAR_CLOCK_PUBKEY,
- tokenProgram: tokenProgram.programId,
- stakeProgram: StakeProgram.programId,
- })
- .instruction();
- await sendAndConfirmTx(
- [createUserStakeAccountIx, withdrawStakeIx],
- [kp, userStakeAccountKp]
- );
- }
- async function setManager() {
- await program.methods
- .setManager()
- .accounts({
- stakePool: stakePoolPk,
- manager: kp.publicKey,
- newManager: kp.publicKey,
- newFeeReceiver: managerPoolTokenAccountPk,
- })
- .rpc();
- }
- async function setFee() {
- await program.methods
- .setFee({ solReferral: [5] })
- .accounts({
- stakePool: stakePoolPk,
- manager: kp.publicKey,
- })
- .rpc();
- }
- async function setStaker() {
- await program.methods
- .setStaker()
- .accounts({
- stakePool: stakePoolPk,
- setStakerAuthority: kp.publicKey,
- newStaker: kp.publicKey,
- })
- .rpc();
- }
- async function depositSol() {
- await program.methods
- .depositSol(new BN(1))
- .accounts({
- stakePool: stakePoolPk,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- reserveStakeAccount: reserveStakePk,
- lamportsFrom: kp.publicKey,
- poolTokensTo: userPoolTokenAccountPk,
- managerFeeAccount: managerPoolTokenAccountPk,
- referrerPoolTokensAccount: managerPoolTokenAccountPk,
- poolMint: poolMintPk,
- systemProgram: SystemProgram.programId,
- tokenProgram: tokenProgram.programId,
- })
- .rpc();
- }
- async function setFundingAuthority() {
- await program.methods
- .setFundingAuthority({ stakeDeposit: {} })
- .accounts({
- stakePool: stakePoolPk,
- manager: kp.publicKey,
- })
- .rpc();
- }
- async function withdrawSol() {
- await program.methods
- .withdrawSol(new BN(1))
- .accounts({
- stakePool: stakePoolPk,
- stakePoolWithdrawAuthority: withdrawAuthorityPk,
- userTransferAuthority: kp.publicKey,
- poolTokensFrom: userPoolTokenAccountPk,
- reserveStakeAccount: reserveStakePk,
- lamportsTo: kp.publicKey,
- managerFeeAccount: managerPoolTokenAccountPk,
- poolMint: poolMintPk,
- clock: SYSVAR_CLOCK_PUBKEY,
- sysvarStakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
- stakeProgram: StakeProgram.programId,
- tokenProgram: tokenProgram.programId,
- })
- .rpc();
- }
- // TODO: this should work but it's not tested
- // async function createTokenMetadata() {
- // await program.methods.createTokenMetadata(
- // "acheron",
- // "ACH",
- // "https://github.com/acheroncrypto"
- // ).accounts({
- // stakePool: stakePoolPk,
- // manager: kp.publicKey,
- // poolMint: poolMintPk,
- // payer: kp.publicKey,
- // tokenMetadata: ,
- // mplTokenMetadata: ,
- // systemProgram: SystemProgram.programId,
- // rent: SYSVAR_RENT_PUBKEY
- // }).rpc();
- // }
- // TODO: this should work but it's not tested
- // async function updateTokenMetadata() {
- // await program.methods.updateTokenMetadata(
- // "acheron",
- // "ACH",
- // "https://twitter.com/acheroncrypto"
- // ).accounts({
- // stakePool: stakePoolPk,
- // manager: kp.publicKey,
- // stakePoolWithdrawAuthority: withdrawAuthorityPk,
- // tokenMetadata: ,
- // mplTokenMetadata: ,
- // }).rpc();
- // }
- async function fetchStakePool() {
- const stakePool = await program.account.stakePool.fetch(stakePoolPk);
- assert(stakePool.manager.equals(kp.publicKey));
- }
- async function fetchValidatorList() {
- const validatorList = await program.account.validatorList.fetch(
- validatorListPk
- );
- assert(validatorList.header.maxValidators === VALIDATOR_LIST_COUNT);
- }
- await test(initialize);
- await test(addValidatorToPool);
- await test(removeValidatorFromPool);
- // Re-adding validator for other tests to pass
- await test(addValidatorToPool);
- await test(increaseValidatorStake);
- await test(decreaseValidatorStake);
- await test(setPreferredValidator);
- await test(updateValidatorListBalance);
- await test(updateStakePoolBalance);
- await test(cleanupRemovedValidatorEntries);
- await test(depositStake);
- await test(withdrawStake);
- await test(setManager);
- await test(setFee);
- await test(setStaker);
- await test(depositSol);
- await test(setFundingAuthority);
- await test(withdrawSol);
- // await test(createTokenMetadata);
- // await test(updateTokenMetadata);
- await test(fetchStakePool);
- await test(fetchValidatorList);
- }
|