import {expect, use} from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { Keypair, LAMPORTS_PER_SOL, VoteAuthorizationLayout, VoteInit, VoteInstruction, VoteProgram, sendAndConfirmTransaction, SystemInstruction, Connection, } from '../src'; import {helpers} from './mocks/rpc-http'; import {url} from './url'; use(chaiAsPromised); describe('VoteProgram', () => { it('createAccount', () => { const fromPubkey = Keypair.generate().publicKey; const newAccountPubkey = Keypair.generate().publicKey; const authorizedPubkey = Keypair.generate().publicKey; const nodePubkey = Keypair.generate().publicKey; const commission = 5; const voteInit = new VoteInit( nodePubkey, authorizedPubkey, authorizedPubkey, commission, ); const lamports = 123; const transaction = VoteProgram.createAccount({ fromPubkey, votePubkey: newAccountPubkey, voteInit, lamports, }); expect(transaction.instructions).to.have.length(2); const [systemInstruction, voteInstruction] = transaction.instructions; const systemParams = { fromPubkey, newAccountPubkey, lamports, space: VoteProgram.space, programId: VoteProgram.programId, }; expect(systemParams).to.eql( SystemInstruction.decodeCreateAccount(systemInstruction), ); const initParams = {votePubkey: newAccountPubkey, nodePubkey, voteInit}; expect(initParams).to.eql( VoteInstruction.decodeInitializeAccount(voteInstruction), ); }); it('initialize', () => { const newAccountPubkey = Keypair.generate().publicKey; const authorizedPubkey = Keypair.generate().publicKey; const nodePubkey = Keypair.generate().publicKey; const voteInit = new VoteInit( nodePubkey, authorizedPubkey, authorizedPubkey, 5, ); const initParams = { votePubkey: newAccountPubkey, nodePubkey, voteInit, }; const initInstruction = VoteProgram.initializeAccount(initParams); expect(initParams).to.eql( VoteInstruction.decodeInitializeAccount(initInstruction), ); }); it('authorize', () => { const votePubkey = Keypair.generate().publicKey; const authorizedPubkey = Keypair.generate().publicKey; const newAuthorizedPubkey = Keypair.generate().publicKey; const voteAuthorizationType = VoteAuthorizationLayout.Voter; const params = { votePubkey, authorizedPubkey, newAuthorizedPubkey, voteAuthorizationType, }; const transaction = VoteProgram.authorize(params); expect(transaction.instructions).to.have.length(1); const [authorizeInstruction] = transaction.instructions; expect(params).to.eql( VoteInstruction.decodeAuthorize(authorizeInstruction), ); }); it('withdraw', () => { const votePubkey = Keypair.generate().publicKey; const authorizedWithdrawerPubkey = Keypair.generate().publicKey; const toPubkey = Keypair.generate().publicKey; const params = { votePubkey, authorizedWithdrawerPubkey, lamports: 123, toPubkey, }; const transaction = VoteProgram.withdraw(params); expect(transaction.instructions).to.have.length(1); const [withdrawInstruction] = transaction.instructions; expect(params).to.eql(VoteInstruction.decodeWithdraw(withdrawInstruction)); }); if (process.env.TEST_LIVE) { it('live vote actions', async () => { const connection = new Connection(url, 'confirmed'); const newVoteAccount = Keypair.generate(); const nodeAccount = Keypair.generate(); const payer = Keypair.generate(); await helpers.airdrop({ connection, address: payer.publicKey, amount: 12 * LAMPORTS_PER_SOL, }); expect(await connection.getBalance(payer.publicKey)).to.eq( 12 * LAMPORTS_PER_SOL, ); const authorized = Keypair.generate(); await helpers.airdrop({ connection, address: authorized.publicKey, amount: 12 * LAMPORTS_PER_SOL, }); expect(await connection.getBalance(authorized.publicKey)).to.eq( 12 * LAMPORTS_PER_SOL, ); const minimumAmount = await connection.getMinimumBalanceForRentExemption( VoteProgram.space, ); // Create initialized Vote account let createAndInitialize = VoteProgram.createAccount({ fromPubkey: payer.publicKey, votePubkey: newVoteAccount.publicKey, voteInit: new VoteInit( nodeAccount.publicKey, authorized.publicKey, authorized.publicKey, 5, ), lamports: minimumAmount + 10 * LAMPORTS_PER_SOL, }); await sendAndConfirmTransaction( connection, createAndInitialize, [payer, newVoteAccount, nodeAccount], {preflightCommitment: 'confirmed'}, ); expect(await connection.getBalance(newVoteAccount.publicKey)).to.eq( minimumAmount + 10 * LAMPORTS_PER_SOL, ); // Withdraw from Vote account let recipient = Keypair.generate(); let withdraw = VoteProgram.withdraw({ votePubkey: newVoteAccount.publicKey, authorizedWithdrawerPubkey: authorized.publicKey, lamports: LAMPORTS_PER_SOL, toPubkey: recipient.publicKey, }); await sendAndConfirmTransaction(connection, withdraw, [authorized], { preflightCommitment: 'confirmed', }); expect(await connection.getBalance(recipient.publicKey)).to.eq( LAMPORTS_PER_SOL, ); const newAuthorizedWithdrawer = Keypair.generate(); await helpers.airdrop({ connection, address: newAuthorizedWithdrawer.publicKey, amount: LAMPORTS_PER_SOL, }); expect( await connection.getBalance(newAuthorizedWithdrawer.publicKey), ).to.eq(LAMPORTS_PER_SOL); // Authorize a new Withdrawer. let authorize = VoteProgram.authorize({ votePubkey: newVoteAccount.publicKey, authorizedPubkey: authorized.publicKey, newAuthorizedPubkey: newAuthorizedWithdrawer.publicKey, voteAuthorizationType: VoteAuthorizationLayout.Withdrawer, }); await sendAndConfirmTransaction(connection, authorize, [authorized], { preflightCommitment: 'confirmed', }); // Test old authorized cannot withdraw anymore. withdraw = VoteProgram.withdraw({ votePubkey: newVoteAccount.publicKey, authorizedWithdrawerPubkey: authorized.publicKey, lamports: minimumAmount, toPubkey: recipient.publicKey, }); await expect( sendAndConfirmTransaction(connection, withdraw, [authorized], { preflightCommitment: 'confirmed', }), ).to.be.rejected; // Test newAuthorizedWithdrawer may withdraw. recipient = Keypair.generate(); withdraw = VoteProgram.withdraw({ votePubkey: newVoteAccount.publicKey, authorizedWithdrawerPubkey: newAuthorizedWithdrawer.publicKey, lamports: LAMPORTS_PER_SOL, toPubkey: recipient.publicKey, }); await sendAndConfirmTransaction( connection, withdraw, [newAuthorizedWithdrawer], { preflightCommitment: 'confirmed', }, ); expect(await connection.getBalance(recipient.publicKey)).to.eq( LAMPORTS_PER_SOL, ); const newAuthorizedVoter = Keypair.generate(); await helpers.airdrop({ connection, address: newAuthorizedVoter.publicKey, amount: LAMPORTS_PER_SOL, }); expect(await connection.getBalance(newAuthorizedVoter.publicKey)).to.eq( LAMPORTS_PER_SOL, ); // The authorized Withdrawer may sign to authorize a new Voter, see // https://github.com/solana-labs/solana/issues/22521 authorize = VoteProgram.authorize({ votePubkey: newVoteAccount.publicKey, authorizedPubkey: newAuthorizedWithdrawer.publicKey, newAuthorizedPubkey: newAuthorizedVoter.publicKey, voteAuthorizationType: VoteAuthorizationLayout.Voter, }); await sendAndConfirmTransaction( connection, authorize, [newAuthorizedWithdrawer], { preflightCommitment: 'confirmed', }, ); }).timeout(10 * 1000); } });