import bs58 from 'bs58'; import {Buffer} from 'buffer'; import {expect} from 'chai'; import { Connection, SystemProgram, Transaction, PublicKey, Keypair, } from '../src'; import {NONCE_ACCOUNT_LENGTH} from '../src/nonce-account'; import {MOCK_PORT, url} from './url'; import {helpers, mockRpcResponse, mockServer} from './mocks/rpc-http'; import {stubRpcWebSocket, restoreRpcWebSocket} from './mocks/rpc-websockets'; const expectedData = (authorizedPubkey: PublicKey): [string, string] => { const expectedData = Buffer.alloc(NONCE_ACCOUNT_LENGTH); expectedData.writeInt32LE(0, 0); // Version, 4 bytes expectedData.writeInt32LE(1, 4); // State, 4 bytes authorizedPubkey.toBuffer().copy(expectedData, 8); // authorizedPubkey, 32 bytes const mockNonce = Keypair.generate(); mockNonce.publicKey.toBuffer().copy(expectedData, 40); // Hash, 32 bytes expectedData.writeUInt16LE(5000, 72); // feeCalculator, 8 bytes return [expectedData.toString('base64'), 'base64']; }; describe('Nonce', () => { let connection: Connection; beforeEach(() => { connection = new Connection(url); }); if (mockServer) { const server = mockServer; beforeEach(() => { server.start(MOCK_PORT); stubRpcWebSocket(connection); }); afterEach(() => { server.stop(); restoreRpcWebSocket(connection); }); } it('create and query nonce account', async () => { const from = Keypair.generate(); const nonceAccount = Keypair.generate(); await mockRpcResponse({ method: 'getMinimumBalanceForRentExemption', params: [NONCE_ACCOUNT_LENGTH], value: 50, }); const minimumAmount = await connection.getMinimumBalanceForRentExemption( NONCE_ACCOUNT_LENGTH, ); await helpers.airdrop({ connection, address: from.publicKey, amount: minimumAmount * 2, }); const transaction = new Transaction().add( SystemProgram.createNonceAccount({ fromPubkey: from.publicKey, noncePubkey: nonceAccount.publicKey, authorizedPubkey: from.publicKey, lamports: minimumAmount, }), ); await helpers.processTransaction({ connection, transaction, signers: [from, nonceAccount], commitment: 'confirmed', }); await mockRpcResponse({ method: 'getAccountInfo', params: [ nonceAccount.publicKey.toBase58(), {encoding: 'base64', commitment: 'confirmed'}, ], value: { owner: '11111111111111111111111111111111', lamports: minimumAmount, data: expectedData(from.publicKey), executable: false, rentEpoch: 20, }, withContext: true, }); const nonceAccountData = await connection.getNonce( nonceAccount.publicKey, 'confirmed', ); if (nonceAccountData === null) { expect(nonceAccountData).not.to.be.null; return; } expect(nonceAccountData.authorizedPubkey).to.eql(from.publicKey); expect(bs58.decode(nonceAccountData.nonce).length).to.be.greaterThan(30); }); it('create and query nonce account with seed', async () => { const from = Keypair.generate(); const seed = 'seed'; const noncePubkey = await PublicKey.createWithSeed( from.publicKey, seed, SystemProgram.programId, ); await mockRpcResponse({ method: 'getMinimumBalanceForRentExemption', params: [NONCE_ACCOUNT_LENGTH], value: 50, }); const minimumAmount = await connection.getMinimumBalanceForRentExemption( NONCE_ACCOUNT_LENGTH, ); await helpers.airdrop({ connection, address: from.publicKey, amount: minimumAmount * 2, }); const transaction = new Transaction().add( SystemProgram.createNonceAccount({ fromPubkey: from.publicKey, noncePubkey: noncePubkey, basePubkey: from.publicKey, seed, authorizedPubkey: from.publicKey, lamports: minimumAmount, }), ); await helpers.processTransaction({ connection, transaction, signers: [from], commitment: 'confirmed', }); await mockRpcResponse({ method: 'getAccountInfo', params: [ noncePubkey.toBase58(), {encoding: 'base64', commitment: 'confirmed'}, ], value: { owner: '11111111111111111111111111111111', lamports: minimumAmount, data: expectedData(from.publicKey), executable: false, rentEpoch: 20, }, withContext: true, }); const nonceAccountData = await connection.getNonce( noncePubkey, 'confirmed', ); if (nonceAccountData === null) { expect(nonceAccountData).not.to.be.null; return; } expect(nonceAccountData.authorizedPubkey).to.eql(from.publicKey); expect(bs58.decode(nonceAccountData.nonce).length).to.be.greaterThan(30); }); });