solana/web3.js/test/stake-program.test.js

475 lines
15 KiB
JavaScript
Raw Normal View History

2019-12-23 15:50:27 -08:00
// @flow
import {
Account,
Authorized,
Connection,
2019-12-23 15:50:27 -08:00
Lockup,
PublicKey,
sendAndConfirmTransaction,
LAMPORTS_PER_SOL,
2019-12-23 15:50:27 -08:00
StakeAuthorizationLayout,
StakeInstruction,
StakeProgram,
SystemInstruction,
Transaction,
} from '../src';
import {mockRpcEnabled} from './__mocks__/node-fetch';
2020-12-24 10:43:45 -08:00
import {newAccountWithLamports} from './new-account-with-lamports';
import {url} from './url';
if (!mockRpcEnabled) {
// Testing max commitment level takes around 20s to complete
2020-12-24 10:43:45 -08:00
jest.setTimeout(60000);
}
2019-12-23 15:50:27 -08:00
2020-03-16 02:44:12 -07:00
test('createAccountWithSeed', async () => {
const fromPubkey = new Account().publicKey;
2019-12-23 15:50:27 -08:00
const seed = 'test string';
2020-03-16 02:44:12 -07:00
const newAccountPubkey = await PublicKey.createWithSeed(
fromPubkey,
2019-12-23 15:50:27 -08:00
seed,
StakeProgram.programId,
);
const authorizedPubkey = new Account().publicKey;
const authorized = new Authorized(authorizedPubkey, authorizedPubkey);
const lockup = new Lockup(0, 0, fromPubkey);
const lamports = 123;
const transaction = StakeProgram.createAccountWithSeed({
fromPubkey,
stakePubkey: newAccountPubkey,
basePubkey: fromPubkey,
2019-12-23 15:50:27 -08:00
seed,
authorized,
lockup,
lamports,
});
2019-12-23 15:50:27 -08:00
expect(transaction.instructions).toHaveLength(2);
const [systemInstruction, stakeInstruction] = transaction.instructions;
const systemParams = {
fromPubkey,
newAccountPubkey,
basePubkey: fromPubkey,
seed,
lamports,
space: StakeProgram.space,
programId: StakeProgram.programId,
};
expect(systemParams).toEqual(
SystemInstruction.decodeCreateWithSeed(systemInstruction),
);
const initParams = {stakePubkey: newAccountPubkey, authorized, lockup};
expect(initParams).toEqual(
StakeInstruction.decodeInitialize(stakeInstruction),
);
2019-12-23 15:50:27 -08:00
});
test('createAccount', () => {
const fromPubkey = new Account().publicKey;
const newAccountPubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const authorized = new Authorized(authorizedPubkey, authorizedPubkey);
const lockup = new Lockup(0, 0, fromPubkey);
const lamports = 123;
const transaction = StakeProgram.createAccount({
fromPubkey,
stakePubkey: newAccountPubkey,
authorized,
lockup,
lamports,
});
2019-12-23 15:50:27 -08:00
expect(transaction.instructions).toHaveLength(2);
const [systemInstruction, stakeInstruction] = transaction.instructions;
const systemParams = {
fromPubkey,
newAccountPubkey,
lamports,
space: StakeProgram.space,
programId: StakeProgram.programId,
};
expect(systemParams).toEqual(
SystemInstruction.decodeCreateAccount(systemInstruction),
);
const initParams = {stakePubkey: newAccountPubkey, authorized, lockup};
expect(initParams).toEqual(
StakeInstruction.decodeInitialize(stakeInstruction),
);
2019-12-23 15:50:27 -08:00
});
test('delegate', () => {
const stakePubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const votePubkey = new Account().publicKey;
const params = {
stakePubkey,
authorizedPubkey,
votePubkey,
};
const transaction = StakeProgram.delegate(params);
expect(transaction.instructions).toHaveLength(1);
const [stakeInstruction] = transaction.instructions;
expect(params).toEqual(StakeInstruction.decodeDelegate(stakeInstruction));
2019-12-23 15:50:27 -08:00
});
test('authorize', () => {
const stakePubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const newAuthorizedPubkey = new Account().publicKey;
const stakeAuthorizationType = StakeAuthorizationLayout.Staker;
const params = {
stakePubkey,
authorizedPubkey,
newAuthorizedPubkey,
stakeAuthorizationType,
};
const transaction = StakeProgram.authorize(params);
expect(transaction.instructions).toHaveLength(1);
const [stakeInstruction] = transaction.instructions;
expect(params).toEqual(StakeInstruction.decodeAuthorize(stakeInstruction));
2019-12-23 15:50:27 -08:00
});
test('authorizeWithSeed', () => {
const stakePubkey = new Account().publicKey;
const authorityBase = new Account().publicKey;
const authoritySeed = 'test string';
const authorityOwner = new Account().publicKey;
const newAuthorizedPubkey = new Account().publicKey;
const stakeAuthorizationType = StakeAuthorizationLayout.Staker;
const params = {
stakePubkey,
authorityBase,
authoritySeed,
authorityOwner,
newAuthorizedPubkey,
stakeAuthorizationType,
};
const transaction = StakeProgram.authorizeWithSeed(params);
expect(transaction.instructions).toHaveLength(1);
const [stakeInstruction] = transaction.instructions;
2020-08-25 21:58:32 -07:00
expect(params).toEqual(
StakeInstruction.decodeAuthorizeWithSeed(stakeInstruction),
);
});
2019-12-23 15:50:27 -08:00
test('split', () => {
const stakePubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const splitStakePubkey = new Account().publicKey;
const params = {
stakePubkey,
authorizedPubkey,
splitStakePubkey,
lamports: 123,
};
const transaction = StakeProgram.split(params);
2019-12-23 15:50:27 -08:00
expect(transaction.instructions).toHaveLength(2);
const [systemInstruction, stakeInstruction] = transaction.instructions;
const systemParams = {
fromPubkey: authorizedPubkey,
newAccountPubkey: splitStakePubkey,
lamports: 0,
space: StakeProgram.space,
programId: StakeProgram.programId,
};
expect(systemParams).toEqual(
SystemInstruction.decodeCreateAccount(systemInstruction),
);
expect(params).toEqual(StakeInstruction.decodeSplit(stakeInstruction));
2019-12-23 15:50:27 -08:00
});
test('withdraw', () => {
const stakePubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const toPubkey = new Account().publicKey;
const params = {
stakePubkey,
authorizedPubkey,
toPubkey,
lamports: 123,
};
const transaction = StakeProgram.withdraw(params);
expect(transaction.instructions).toHaveLength(1);
const [stakeInstruction] = transaction.instructions;
expect(params).toEqual(StakeInstruction.decodeWithdraw(stakeInstruction));
2019-12-23 15:50:27 -08:00
});
test('deactivate', () => {
const stakePubkey = new Account().publicKey;
const authorizedPubkey = new Account().publicKey;
const params = {stakePubkey, authorizedPubkey};
const transaction = StakeProgram.deactivate(params);
expect(transaction.instructions).toHaveLength(1);
const [stakeInstruction] = transaction.instructions;
expect(params).toEqual(StakeInstruction.decodeDeactivate(stakeInstruction));
2019-12-23 15:50:27 -08:00
});
2020-03-16 02:44:12 -07:00
test('StakeInstructions', async () => {
2019-12-23 15:50:27 -08:00
const from = new Account();
const seed = 'test string';
2020-03-16 02:44:12 -07:00
const newAccountPubkey = await PublicKey.createWithSeed(
2019-12-23 15:50:27 -08:00
from.publicKey,
seed,
StakeProgram.programId,
);
const authorized = new Account();
const amount = 123;
const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash
const createWithSeed = StakeProgram.createAccountWithSeed({
fromPubkey: from.publicKey,
stakePubkey: newAccountPubkey,
basePubkey: from.publicKey,
2019-12-23 15:50:27 -08:00
seed,
authorized: new Authorized(authorized.publicKey, authorized.publicKey),
lockup: new Lockup(0, 0, from.publicKey),
lamports: amount,
});
2019-12-23 15:50:27 -08:00
const createWithSeedTransaction = new Transaction({recentBlockhash}).add(
createWithSeed,
);
expect(createWithSeedTransaction.instructions).toHaveLength(2);
const systemInstructionType = SystemInstruction.decodeInstructionType(
2019-12-23 15:50:27 -08:00
createWithSeedTransaction.instructions[0],
);
expect(systemInstructionType).toEqual('CreateWithSeed');
2019-12-23 15:50:27 -08:00
const stakeInstructionType = StakeInstruction.decodeInstructionType(
2019-12-23 15:50:27 -08:00
createWithSeedTransaction.instructions[1],
);
expect(stakeInstructionType).toEqual('Initialize');
2019-12-23 15:50:27 -08:00
expect(() => {
StakeInstruction.decodeInstructionType(
createWithSeedTransaction.instructions[0],
);
2019-12-23 15:50:27 -08:00
}).toThrow();
const stake = new Account();
const vote = new Account();
const delegate = StakeProgram.delegate({
stakePubkey: stake.publicKey,
authorizedPubkey: authorized.publicKey,
votePubkey: vote.publicKey,
});
2019-12-23 15:50:27 -08:00
const delegateTransaction = new Transaction({recentBlockhash}).add(delegate);
const anotherStakeInstructionType = StakeInstruction.decodeInstructionType(
2019-12-23 15:50:27 -08:00
delegateTransaction.instructions[0],
);
expect(anotherStakeInstructionType).toEqual('Delegate');
2019-12-23 15:50:27 -08:00
});
test('live staking actions', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
2020-12-24 10:43:45 -08:00
const connection = new Connection(url, 'singleGossip');
const voteAccounts = await connection.getVoteAccounts();
const voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0];
const votePubkey = new PublicKey(voteAccount.votePubkey);
2020-12-24 10:43:45 -08:00
const from = await newAccountWithLamports(connection, 2 * LAMPORTS_PER_SOL);
const authorized = await newAccountWithLamports(
connection,
2 * LAMPORTS_PER_SOL,
);
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space,
2020-12-24 10:43:45 -08:00
);
expect(await connection.getBalance(from.publicKey)).toEqual(
2 * LAMPORTS_PER_SOL,
);
expect(await connection.getBalance(authorized.publicKey)).toEqual(
2 * LAMPORTS_PER_SOL,
);
{
// Create Stake account without seed
const newStakeAccount = new Account();
let createAndInitialize = StakeProgram.createAccount({
fromPubkey: from.publicKey,
stakePubkey: newStakeAccount.publicKey,
authorized: new Authorized(authorized.publicKey, authorized.publicKey),
2020-08-14 00:02:00 -07:00
lockup: new Lockup(0, 0, new PublicKey(0)),
lamports: minimumAmount + 42,
});
await sendAndConfirmTransaction(
connection,
createAndInitialize,
[from, newStakeAccount],
2020-12-24 10:43:45 -08:00
{commitment: 'singleGossip'},
);
expect(await connection.getBalance(newStakeAccount.publicKey)).toEqual(
minimumAmount + 42,
);
let delegation = StakeProgram.delegate({
stakePubkey: newStakeAccount.publicKey,
authorizedPubkey: authorized.publicKey,
votePubkey,
});
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, delegation, [authorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
});
}
// Create Stake account with seed
const seed = 'test string';
2020-03-16 02:44:12 -07:00
const newAccountPubkey = await PublicKey.createWithSeed(
from.publicKey,
seed,
StakeProgram.programId,
);
let createAndInitializeWithSeed = StakeProgram.createAccountWithSeed({
fromPubkey: from.publicKey,
stakePubkey: newAccountPubkey,
basePubkey: from.publicKey,
seed,
authorized: new Authorized(authorized.publicKey, authorized.publicKey),
2020-08-14 00:02:00 -07:00
lockup: new Lockup(0, 0, new PublicKey(0)),
lamports: 3 * minimumAmount + 42,
});
await sendAndConfirmTransaction(
connection,
createAndInitializeWithSeed,
[from],
2020-12-24 10:43:45 -08:00
{commitment: 'singleGossip'},
);
let originalStakeBalance = await connection.getBalance(newAccountPubkey);
2019-12-27 11:13:45 -08:00
expect(originalStakeBalance).toEqual(3 * minimumAmount + 42);
let delegation = StakeProgram.delegate({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
votePubkey,
});
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, delegation, [authorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
});
// Test that withdraw fails before deactivation
const recipient = new Account();
let withdraw = StakeProgram.withdraw({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
toPubkey: recipient.publicKey,
lamports: 1000,
});
await expect(
2020-06-03 04:55:42 -07:00
sendAndConfirmTransaction(connection, withdraw, [authorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
}),
).rejects.toThrow();
// Deactivate stake
let deactivate = StakeProgram.deactivate({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
});
await sendAndConfirmTransaction(connection, deactivate, [authorized], {
commitment: 'singleGossip',
});
let stakeActivationState;
do {
stakeActivationState = await connection.getStakeActivation(
newAccountPubkey,
);
} while (stakeActivationState.state != 'inactive');
// Test that withdraw succeeds after deactivation
withdraw = StakeProgram.withdraw({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
toPubkey: recipient.publicKey,
lamports: minimumAmount + 20,
});
await sendAndConfirmTransaction(connection, withdraw, [authorized], {
commitment: 'singleGossip',
});
const recipientBalance = await connection.getBalance(recipient.publicKey);
expect(recipientBalance).toEqual(minimumAmount + 20);
2019-12-27 11:13:45 -08:00
// Split stake
const newStake = new Account();
let split = StakeProgram.split({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
splitStakePubkey: newStake.publicKey,
lamports: minimumAmount + 20,
});
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, split, [authorized, newStake], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
});
const balance = await connection.getBalance(newAccountPubkey);
expect(balance).toEqual(minimumAmount + 2);
2019-12-27 11:13:45 -08:00
// Authorize to new account
const newAuthorized = new Account();
await connection.requestAirdrop(newAuthorized.publicKey, LAMPORTS_PER_SOL);
let authorize = StakeProgram.authorize({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
newAuthorizedPubkey: newAuthorized.publicKey,
stakeAuthorizationType: StakeAuthorizationLayout.Withdrawer,
});
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, authorize, [authorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
});
authorize = StakeProgram.authorize({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
newAuthorizedPubkey: newAuthorized.publicKey,
stakeAuthorizationType: StakeAuthorizationLayout.Staker,
});
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, authorize, [authorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-06-03 04:55:42 -07:00
});
// Test old authorized can't delegate
let delegateNotAuthorized = StakeProgram.delegate({
stakePubkey: newAccountPubkey,
authorizedPubkey: authorized.publicKey,
votePubkey,
});
await expect(
sendAndConfirmTransaction(connection, delegateNotAuthorized, [authorized], {
commitment: 'singleGossip',
}),
).rejects.toThrow();
2020-09-09 16:32:50 -07:00
// Authorize a derived address
authorize = StakeProgram.authorize({
stakePubkey: newAccountPubkey,
authorizedPubkey: newAuthorized.publicKey,
newAuthorizedPubkey: newAccountPubkey,
stakeAuthorizationType: StakeAuthorizationLayout.Withdrawer,
});
await sendAndConfirmTransaction(connection, authorize, [newAuthorized], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-09-09 16:32:50 -07:00
});
2020-12-24 10:43:45 -08:00
// Restore the previous authority using a derived address
2020-09-09 16:32:50 -07:00
authorize = StakeProgram.authorizeWithSeed({
stakePubkey: newAccountPubkey,
authorityBase: from.publicKey,
authoritySeed: seed,
authorityOwner: StakeProgram.programId,
newAuthorizedPubkey: newAuthorized.publicKey,
stakeAuthorizationType: StakeAuthorizationLayout.Withdrawer,
});
await sendAndConfirmTransaction(connection, authorize, [from], {
2020-12-24 10:43:45 -08:00
commitment: 'singleGossip',
2020-09-09 16:32:50 -07:00
});
});