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

357 lines
9.6 KiB
JavaScript

// @flow
import {
Account,
Authorized,
Connection,
Lockup,
PublicKey,
sendAndConfirmRecentTransaction,
LAMPORTS_PER_SOL,
StakeAuthorizationLayout,
StakeInstruction,
StakeInstructionLayout,
StakeProgram,
SystemInstruction,
SystemProgram,
Transaction,
} from '../src';
import {mockRpcEnabled} from './__mocks__/node-fetch';
import {url} from './url';
if (!mockRpcEnabled) {
// Testing max commitment level takes around 20s to complete
jest.setTimeout(30000);
}
test('createAccountWithSeed', () => {
const from = new Account();
const seed = 'test string';
const newAccountPubkey = PublicKey.createWithSeed(
from.publicKey,
seed,
StakeProgram.programId,
);
const authorized = new Account();
let transaction;
transaction = StakeProgram.createAccountWithSeed(
from.publicKey,
newAccountPubkey,
from.publicKey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, 0, from.publicKey),
123,
);
expect(transaction.instructions).toHaveLength(2);
expect(transaction.instructions[0].programId).toEqual(
SystemProgram.programId,
);
expect(transaction.instructions[1].programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('createAccount', () => {
const from = new Account();
const newAccount = new Account();
const authorized = new Account();
let transaction;
transaction = StakeProgram.createAccount(
from.publicKey,
newAccount.publicKey,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, 0, from.publicKey),
123,
);
expect(transaction.instructions).toHaveLength(2);
expect(transaction.instructions[0].programId).toEqual(
SystemProgram.programId,
);
expect(transaction.instructions[1].programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('delegate', () => {
const stake = new Account();
const authorized = new Account();
const vote = new Account();
let transaction;
transaction = StakeProgram.delegate(
stake.publicKey,
authorized.publicKey,
vote.publicKey,
);
expect(transaction.keys).toHaveLength(6);
expect(transaction.programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('authorize', () => {
const stake = new Account();
const authorized = new Account();
const newAuthorized = new Account();
const type = StakeAuthorizationLayout.Staker;
let transaction;
transaction = StakeProgram.authorize(
stake.publicKey,
authorized.publicKey,
newAuthorized.publicKey,
type,
);
expect(transaction.keys).toHaveLength(3);
expect(transaction.programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('split', () => {
const stake = new Account();
const authorized = new Account();
const newStake = new Account();
let transaction;
transaction = StakeProgram.split(
stake.publicKey,
authorized.publicKey,
123,
newStake.publicKey,
);
expect(transaction.instructions).toHaveLength(2);
expect(transaction.instructions[0].programId).toEqual(
SystemProgram.programId,
);
expect(transaction.instructions[1].programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('withdraw', () => {
const stake = new Account();
const withdrawer = new Account();
const to = new Account();
let transaction;
transaction = StakeProgram.withdraw(
stake.publicKey,
withdrawer.publicKey,
to.publicKey,
123,
);
expect(transaction.keys).toHaveLength(5);
expect(transaction.programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('deactivate', () => {
const stake = new Account();
const authorized = new Account();
let transaction;
transaction = StakeProgram.deactivate(stake.publicKey, authorized.publicKey);
expect(transaction.keys).toHaveLength(3);
expect(transaction.programId).toEqual(StakeProgram.programId);
// TODO: Validate transaction contents more
});
test('StakeInstructions', () => {
const from = new Account();
const seed = 'test string';
const newAccountPubkey = PublicKey.createWithSeed(
from.publicKey,
seed,
StakeProgram.programId,
);
const authorized = new Account();
const amount = 123;
const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash
const createWithSeed = StakeProgram.createAccountWithSeed(
from.publicKey,
newAccountPubkey,
from.publicKey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, 0, from.publicKey),
amount,
);
const createWithSeedTransaction = new Transaction({recentBlockhash}).add(
createWithSeed,
);
const systemInstruction = SystemInstruction.from(
createWithSeedTransaction.instructions[0],
);
expect(systemInstruction.fromPublicKey).toEqual(from.publicKey);
expect(systemInstruction.toPublicKey).toEqual(newAccountPubkey);
expect(systemInstruction.amount).toEqual(amount);
expect(systemInstruction.programId).toEqual(SystemProgram.programId);
const stakeInstruction = StakeInstruction.from(
createWithSeedTransaction.instructions[1],
);
expect(stakeInstruction.type).toEqual(StakeInstructionLayout.Initialize);
expect(() => {
StakeInstruction.from(createWithSeedTransaction.instructions[0]);
}).toThrow();
const stake = new Account();
const vote = new Account();
const delegate = StakeProgram.delegate(
stake.publicKey,
authorized.publicKey,
vote.publicKey,
);
const delegateTransaction = new Transaction({recentBlockhash}).add(delegate);
const anotherStakeInstruction = StakeInstruction.from(
delegateTransaction.instructions[0],
);
expect(anotherStakeInstruction.type).toEqual(
StakeInstructionLayout.DelegateStake,
);
});
test('live staking actions', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url, 'recent');
const voteAccounts = await connection.getVoteAccounts();
const voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0];
const votePubkey = new PublicKey(voteAccount.votePubkey);
const from = new Account();
const authorized = new Account();
await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL);
await connection.requestAirdrop(authorized.publicKey, 2 * LAMPORTS_PER_SOL);
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space,
'recent',
);
// Create Stake account with seed
const seed = 'test string';
const newAccountPubkey = PublicKey.createWithSeed(
from.publicKey,
seed,
StakeProgram.programId,
);
let createAndInitializeWithSeed = StakeProgram.createAccountWithSeed(
from.publicKey,
newAccountPubkey,
from.publicKey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, 0, new PublicKey('0x00')),
3 * minimumAmount + 42,
);
await sendAndConfirmRecentTransaction(
connection,
createAndInitializeWithSeed,
from,
);
let originalStakeBalance = await connection.getBalance(newAccountPubkey);
expect(originalStakeBalance).toEqual(3 * minimumAmount + 42);
let delegation = StakeProgram.delegate(
newAccountPubkey,
authorized.publicKey,
votePubkey,
);
await sendAndConfirmRecentTransaction(connection, delegation, authorized);
// Test that withdraw fails before deactivation
const recipient = new Account();
let withdraw = StakeProgram.withdraw(
newAccountPubkey,
authorized.publicKey,
recipient.publicKey,
1000,
);
await expect(
sendAndConfirmRecentTransaction(connection, withdraw, authorized),
).rejects.toThrow();
// Split stake
const newStake = new Account();
let split = StakeProgram.split(
newAccountPubkey,
authorized.publicKey,
minimumAmount + 20,
newStake.publicKey,
);
await sendAndConfirmRecentTransaction(
connection,
split,
authorized,
newStake,
);
// Authorize to new account
const newAuthorized = new Account();
await connection.requestAirdrop(newAuthorized.publicKey, LAMPORTS_PER_SOL);
let authorize = StakeProgram.authorize(
newAccountPubkey,
authorized.publicKey,
newAuthorized.publicKey,
StakeAuthorizationLayout.Withdrawer,
);
await sendAndConfirmRecentTransaction(connection, authorize, authorized);
authorize = StakeProgram.authorize(
newAccountPubkey,
authorized.publicKey,
newAuthorized.publicKey,
StakeAuthorizationLayout.Staker,
);
await sendAndConfirmRecentTransaction(connection, authorize, authorized);
// Test old authorized can't deactivate
let deactivateNotAuthorized = StakeProgram.deactivate(
newAccountPubkey,
authorized.publicKey,
);
await expect(
sendAndConfirmRecentTransaction(
connection,
deactivateNotAuthorized,
authorized,
),
).rejects.toThrow();
// Deactivate stake
let deactivate = StakeProgram.deactivate(
newAccountPubkey,
newAuthorized.publicKey,
);
await sendAndConfirmRecentTransaction(connection, deactivate, newAuthorized);
// Test that withdraw succeeds after deactivation
withdraw = StakeProgram.withdraw(
newAccountPubkey,
newAuthorized.publicKey,
recipient.publicKey,
minimumAmount + 20,
);
await sendAndConfirmRecentTransaction(connection, withdraw, newAuthorized);
const balance = await connection.getBalance(newAccountPubkey);
expect(balance).toEqual(minimumAmount + 2);
const recipientBalance = await connection.getBalance(recipient.publicKey);
expect(recipientBalance).toEqual(minimumAmount + 20);
});