import base58 from 'bs58'; import {expect} from 'chai'; import { Keypair, Connection, Transaction, SystemProgram, LAMPORTS_PER_SOL, } from '../src'; import invariant from '../src/utils/assert'; import {MOCK_PORT, url} from './url'; import {helpers, mockRpcResponse, mockServer} from './mocks/rpc-http'; import {stubRpcWebSocket, restoreRpcWebSocket} from './mocks/rpc-websocket'; describe('Transaction Payer', () => { 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('transaction-payer', async () => { const accountPayer = Keypair.generate(); const accountFrom = Keypair.generate(); const accountTo = Keypair.generate(); await helpers.airdrop({ connection, address: accountPayer.publicKey, amount: LAMPORTS_PER_SOL, }); await mockRpcResponse({ method: 'getMinimumBalanceForRentExemption', params: [0], value: 50, }); const minimumAmount = await connection.getMinimumBalanceForRentExemption(0); await helpers.airdrop({ connection, address: accountFrom.publicKey, amount: minimumAmount + 12, }); await helpers.airdrop({ connection, address: accountTo.publicKey, amount: minimumAmount + 21, }); const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: accountFrom.publicKey, toPubkey: accountTo.publicKey, lamports: 10, }), ); await helpers.processTransaction({ connection, transaction, signers: [accountPayer, accountFrom], commitment: 'confirmed', }); invariant(transaction.signature); const signature = base58.encode(transaction.signature); await mockRpcResponse({ method: 'getSignatureStatuses', params: [[signature]], value: [ { slot: 0, confirmations: 11, status: {Ok: null}, err: null, }, ], withContext: true, }); const {value} = await connection.getSignatureStatus(signature); if (value !== null) { expect(typeof value.slot).to.eq('number'); expect(value.err).to.be.null; } else { expect(value).not.to.be.null; } await mockRpcResponse({ method: 'getBalance', params: [accountPayer.publicKey.toBase58(), {commitment: 'confirmed'}], value: LAMPORTS_PER_SOL - 1, withContext: true, }); // accountPayer should be less than LAMPORTS_PER_SOL as it paid for the transaction // (exact amount less depends on the current cluster fees) const balance = await connection.getBalance( accountPayer.publicKey, 'confirmed', ); expect(balance).to.be.greaterThan(0); expect(balance).to.be.at.most(LAMPORTS_PER_SOL); // accountFrom should have exactly 2, since it didn't pay for the transaction await mockRpcResponse({ method: 'getBalance', params: [accountFrom.publicKey.toBase58(), {commitment: 'confirmed'}], value: minimumAmount + 2, withContext: true, }); expect( await connection.getBalance(accountFrom.publicKey, 'confirmed'), ).to.eq(minimumAmount + 2); }).timeout(30 * 1000); });