fix: pay transaction fees from system accounts
This commit is contained in:
@ -249,9 +249,13 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
// === src/loader.js ===
|
// === src/loader.js ===
|
||||||
declare export class Loader {
|
declare export class Loader {
|
||||||
constructor(connection: Connection, programId: PublicKey): Loader;
|
static load(
|
||||||
load(program: Account, offset: number, bytes: Array<number>): Promise<void>;
|
connection: Connection,
|
||||||
finalize(program: Account): Promise<void>;
|
payer: Account,
|
||||||
|
program: Account,
|
||||||
|
programId: PublicKey,
|
||||||
|
data: Array<number>,
|
||||||
|
): Promise<PublicKey>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === src/bpf-loader.js ===
|
// === src/bpf-loader.js ===
|
||||||
@ -259,7 +263,7 @@ declare module '@solana/web3.js' {
|
|||||||
static programId: PublicKey;
|
static programId: PublicKey;
|
||||||
static load(
|
static load(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
owner: Account,
|
payer: Account,
|
||||||
elfBytes: Array<number>,
|
elfBytes: Array<number>,
|
||||||
): Promise<PublicKey>;
|
): Promise<PublicKey>;
|
||||||
}
|
}
|
||||||
@ -269,7 +273,7 @@ declare module '@solana/web3.js' {
|
|||||||
static programId: PublicKey;
|
static programId: PublicKey;
|
||||||
static load(
|
static load(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
owner: Account,
|
payer: Account,
|
||||||
programName: string,
|
programName: string,
|
||||||
): Promise<PublicKey>;
|
): Promise<PublicKey>;
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
import {Account} from './account';
|
import {Account} from './account';
|
||||||
import {PublicKey} from './publickey';
|
import {PublicKey} from './publickey';
|
||||||
import {Loader} from './loader';
|
import {Loader} from './loader';
|
||||||
import {SystemProgram} from './system-program';
|
|
||||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
|
||||||
import type {Connection} from './connection';
|
import type {Connection} from './connection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,9 +13,7 @@ export class BpfLoader {
|
|||||||
* Public key that identifies the BpfLoader
|
* Public key that identifies the BpfLoader
|
||||||
*/
|
*/
|
||||||
static get programId(): PublicKey {
|
static get programId(): PublicKey {
|
||||||
return new PublicKey(
|
return new PublicKey('BPFLoader1111111111111111111111111111111111');
|
||||||
'BPFLoader1111111111111111111111111111111111',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,26 +23,12 @@ export class BpfLoader {
|
|||||||
* @param owner User account to load the program into
|
* @param owner User account to load the program into
|
||||||
* @param elfBytes The entire ELF containing the BPF program
|
* @param elfBytes The entire ELF containing the BPF program
|
||||||
*/
|
*/
|
||||||
static async load(
|
static load(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
owner: Account,
|
payer: Account,
|
||||||
elf: Array<number>,
|
elf: Array<number>,
|
||||||
): Promise<PublicKey> {
|
): Promise<PublicKey> {
|
||||||
const programAccount = new Account();
|
const program = new Account();
|
||||||
|
return Loader.load(connection, payer, program, BpfLoader.programId, elf);
|
||||||
const transaction = SystemProgram.createAccount(
|
|
||||||
owner.publicKey,
|
|
||||||
programAccount.publicKey,
|
|
||||||
1 + Math.ceil(elf.length / Loader.chunkSize) + 1,
|
|
||||||
elf.length,
|
|
||||||
BpfLoader.programId,
|
|
||||||
);
|
|
||||||
await sendAndConfirmTransaction(connection, transaction, owner);
|
|
||||||
|
|
||||||
const loader = new Loader(connection, BpfLoader.programId);
|
|
||||||
await loader.load(programAccount, elf);
|
|
||||||
await loader.finalize(programAccount);
|
|
||||||
|
|
||||||
return programAccount.publicKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,9 +143,7 @@ export class BudgetProgram {
|
|||||||
* Public key that identifies the Budget program
|
* Public key that identifies the Budget program
|
||||||
*/
|
*/
|
||||||
static get programId(): PublicKey {
|
static get programId(): PublicKey {
|
||||||
return new PublicKey(
|
return new PublicKey('Budget1111111111111111111111111111111111111');
|
||||||
'Budget1111111111111111111111111111111111111',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -626,7 +626,6 @@ export class Connection {
|
|||||||
subscriptionId,
|
subscriptionId,
|
||||||
programId,
|
programId,
|
||||||
} = this._programAccountChangeSubscriptions[id];
|
} = this._programAccountChangeSubscriptions[id];
|
||||||
console.log('program-id: ' + programId);
|
|
||||||
if (subscriptionId === null) {
|
if (subscriptionId === null) {
|
||||||
try {
|
try {
|
||||||
this._programAccountChangeSubscriptions[
|
this._programAccountChangeSubscriptions[
|
||||||
|
@ -9,43 +9,46 @@ import {Transaction} from './transaction';
|
|||||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
||||||
import {sleep} from './util/sleep';
|
import {sleep} from './util/sleep';
|
||||||
import type {Connection} from './connection';
|
import type {Connection} from './connection';
|
||||||
|
import {SystemProgram} from './system-program';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program loader interface
|
* Program loader interface
|
||||||
*/
|
*/
|
||||||
export class Loader {
|
export class Loader {
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
connection: Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
programId: PublicKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount of program data placed in each load Transaction
|
* Amount of program data placed in each load Transaction
|
||||||
*/
|
*/
|
||||||
static get chunkSize(): number {
|
static get chunkSize(): number {
|
||||||
return 256;
|
return 229; // Keep program chunks under PACKET_DATA_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param connection The connection to use
|
* Loads a generic program
|
||||||
* @param programId Public key that identifies the loader
|
|
||||||
*/
|
|
||||||
constructor(connection: Connection, programId: PublicKey) {
|
|
||||||
Object.assign(this, {connection, programId});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load program data
|
|
||||||
*
|
*
|
||||||
* @param program Account to load the program info
|
* @param connection The connection to use
|
||||||
* @param data Program data
|
* @param payer System account that pays to load the program
|
||||||
|
* @param program Account to load the program into
|
||||||
|
* @param programId Public key that identifies the loader
|
||||||
|
* @param data Program octets
|
||||||
*/
|
*/
|
||||||
async load(program: Account, data: Array<number>) {
|
static async load(
|
||||||
|
connection: Connection,
|
||||||
|
payer: Account,
|
||||||
|
program: Account,
|
||||||
|
programId: PublicKey,
|
||||||
|
data: Array<number>,
|
||||||
|
): Promise<PublicKey> {
|
||||||
|
{
|
||||||
|
const transaction = SystemProgram.createAccount(
|
||||||
|
payer.publicKey,
|
||||||
|
program.publicKey,
|
||||||
|
1,
|
||||||
|
data.length,
|
||||||
|
programId,
|
||||||
|
);
|
||||||
|
await sendAndConfirmTransaction(connection, transaction, payer);
|
||||||
|
}
|
||||||
|
|
||||||
const dataLayout = BufferLayout.struct([
|
const dataLayout = BufferLayout.struct([
|
||||||
BufferLayout.u32('instruction'),
|
BufferLayout.u32('instruction'),
|
||||||
BufferLayout.u32('offset'),
|
BufferLayout.u32('offset'),
|
||||||
@ -76,11 +79,11 @@ export class Loader {
|
|||||||
|
|
||||||
const transaction = new Transaction().add({
|
const transaction = new Transaction().add({
|
||||||
keys: [{pubkey: program.publicKey, isSigner: true}],
|
keys: [{pubkey: program.publicKey, isSigner: true}],
|
||||||
programId: this.programId,
|
programId,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
transactions.push(
|
transactions.push(
|
||||||
sendAndConfirmTransaction(this.connection, transaction, program),
|
sendAndConfirmTransaction(connection, transaction, payer, program),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
|
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
|
||||||
@ -100,14 +103,9 @@ export class Loader {
|
|||||||
array = array.slice(chunkSize);
|
array = array.slice(chunkSize);
|
||||||
}
|
}
|
||||||
await Promise.all(transactions);
|
await Promise.all(transactions);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Finalize the account loaded with program data for execution
|
||||||
* Finalize an account loaded with program data for execution
|
{
|
||||||
*
|
|
||||||
* @param program `load()`ed Account
|
|
||||||
*/
|
|
||||||
async finalize(program: Account) {
|
|
||||||
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
|
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
@ -120,9 +118,11 @@ export class Loader {
|
|||||||
|
|
||||||
const transaction = new Transaction().add({
|
const transaction = new Transaction().add({
|
||||||
keys: [{pubkey: program.publicKey, isSigner: true}],
|
keys: [{pubkey: program.publicKey, isSigner: true}],
|
||||||
programId: this.programId,
|
programId,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
await sendAndConfirmTransaction(this.connection, transaction, program);
|
await sendAndConfirmTransaction(connection, transaction, payer, program);
|
||||||
|
}
|
||||||
|
return program.publicKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
import {Account} from './account';
|
import {Account} from './account';
|
||||||
import {PublicKey} from './publickey';
|
import {PublicKey} from './publickey';
|
||||||
import {Loader} from './loader';
|
import {Loader} from './loader';
|
||||||
import {SystemProgram} from './system-program';
|
|
||||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
|
||||||
import type {Connection} from './connection';
|
import type {Connection} from './connection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,41 +13,29 @@ export class NativeLoader {
|
|||||||
* Public key that identifies the NativeLoader
|
* Public key that identifies the NativeLoader
|
||||||
*/
|
*/
|
||||||
static get programId(): PublicKey {
|
static get programId(): PublicKey {
|
||||||
return new PublicKey(
|
return new PublicKey('NativeLoader1111111111111111111111111111111');
|
||||||
'NativeLoader1111111111111111111111111111111',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a native program
|
* Loads a native program
|
||||||
*
|
*
|
||||||
* @param connection The connection to use
|
* @param connection The connection to use
|
||||||
* @param owner User account to load the program with
|
* @param payer System account that pays to load the program
|
||||||
* @param programName Name of the native program
|
* @param programName Name of the native program
|
||||||
*/
|
*/
|
||||||
static async load(
|
static load(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
owner: Account,
|
payer: Account,
|
||||||
programName: string,
|
programName: string,
|
||||||
): Promise<PublicKey> {
|
): Promise<PublicKey> {
|
||||||
const bytes = [...Buffer.from(programName)];
|
const bytes = [...Buffer.from(programName)];
|
||||||
|
const program = new Account();
|
||||||
const programAccount = new Account();
|
return Loader.load(
|
||||||
|
connection,
|
||||||
// Allocate memory for the program account
|
payer,
|
||||||
const transaction = SystemProgram.createAccount(
|
program,
|
||||||
owner.publicKey,
|
|
||||||
programAccount.publicKey,
|
|
||||||
1 + 1 + 1,
|
|
||||||
bytes.length + 1,
|
|
||||||
NativeLoader.programId,
|
NativeLoader.programId,
|
||||||
|
bytes,
|
||||||
);
|
);
|
||||||
await sendAndConfirmTransaction(connection, transaction, owner);
|
|
||||||
|
|
||||||
const loader = new Loader(connection, NativeLoader.programId);
|
|
||||||
await loader.load(programAccount, bytes);
|
|
||||||
await loader.finalize(programAccount);
|
|
||||||
|
|
||||||
return programAccount.publicKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,12 @@ export class Token {
|
|||||||
programId,
|
programId,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
await sendAndConfirmTransaction(connection, transaction, tokenAccount);
|
await sendAndConfirmTransaction(
|
||||||
|
connection,
|
||||||
|
transaction,
|
||||||
|
owner,
|
||||||
|
tokenAccount,
|
||||||
|
);
|
||||||
|
|
||||||
return [token, initialAccountPublicKey];
|
return [token, initialAccountPublicKey];
|
||||||
}
|
}
|
||||||
@ -299,7 +304,12 @@ export class Token {
|
|||||||
programId: this.programId,
|
programId: this.programId,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
await sendAndConfirmTransaction(this.connection, transaction, tokenAccount);
|
await sendAndConfirmTransaction(
|
||||||
|
this.connection,
|
||||||
|
transaction,
|
||||||
|
owner,
|
||||||
|
tokenAccount,
|
||||||
|
);
|
||||||
|
|
||||||
return tokenAccount.publicKey;
|
return tokenAccount.publicKey;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ export class Transaction {
|
|||||||
signatures: Array<SignaturePubkeyPair> = [];
|
signatures: Array<SignaturePubkeyPair> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first (primary) Transaction signature
|
* The first (payer) Transaction signature
|
||||||
*/
|
*/
|
||||||
get signature(): Buffer | null {
|
get signature(): Buffer | null {
|
||||||
if (this.signatures.length > 0) {
|
if (this.signatures.length > 0) {
|
||||||
@ -172,6 +172,14 @@ export class Transaction {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (numRequiredSignatures > this.signatures.length) {
|
||||||
|
throw new Error(
|
||||||
|
`Insufficent signatures: expected ${numRequiredSignatures} but got ${
|
||||||
|
this.signatures.length
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let keyCount = [];
|
let keyCount = [];
|
||||||
shortvec.encodeLength(keyCount, keys.length);
|
shortvec.encodeLength(keyCount, keys.length);
|
||||||
|
|
||||||
@ -252,7 +260,7 @@ export class Transaction {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const transaction = {
|
const transaction = {
|
||||||
numRequiredSignatures: Buffer.from([numRequiredSignatures]),
|
numRequiredSignatures: Buffer.from([this.signatures.length]),
|
||||||
keyCount: Buffer.from(keyCount),
|
keyCount: Buffer.from(keyCount),
|
||||||
keys: keys.map(key => new PublicKey(key).toBuffer()),
|
keys: keys.map(key => new PublicKey(key).toBuffer()),
|
||||||
recentBlockhash: Buffer.from(bs58.decode(recentBlockhash)),
|
recentBlockhash: Buffer.from(bs58.decode(recentBlockhash)),
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import {
|
import {Account, Connection, BpfLoader, Loader, SystemProgram} from '../src';
|
||||||
Account,
|
|
||||||
Connection,
|
|
||||||
BpfLoader,
|
|
||||||
Loader,
|
|
||||||
SystemProgram,
|
|
||||||
sendAndConfirmTransaction,
|
|
||||||
} from '../src';
|
|
||||||
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../src/timing';
|
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../src/timing';
|
||||||
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
||||||
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
|
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
|
||||||
@ -436,7 +429,11 @@ test('transaction', async () => {
|
|||||||
result: 2,
|
result: 2,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(await connection.getBalance(accountFrom.publicKey)).toBe(2);
|
|
||||||
|
// accountFrom may have less than 2 due to transaction fees
|
||||||
|
const balance = await connection.getBalance(accountFrom.publicKey);
|
||||||
|
expect(balance).toBeGreaterThan(0);
|
||||||
|
expect(balance).toBeLessThanOrEqual(2);
|
||||||
|
|
||||||
mockRpc.push([
|
mockRpc.push([
|
||||||
url,
|
url,
|
||||||
@ -494,7 +491,12 @@ test('multi-instruction transaction', async () => {
|
|||||||
Ok: null,
|
Ok: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(await connection.getBalance(accountFrom.publicKey)).toBe(12);
|
// accountFrom may have less than 12 due to transaction fees
|
||||||
|
expect(await connection.getBalance(accountFrom.publicKey)).toBeGreaterThan(0);
|
||||||
|
expect(
|
||||||
|
await connection.getBalance(accountFrom.publicKey),
|
||||||
|
).toBeLessThanOrEqual(12);
|
||||||
|
|
||||||
expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
|
expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -516,28 +518,21 @@ test('account change notification', async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await connection.requestAirdrop(owner.publicKey, 42);
|
await connection.requestAirdrop(owner.publicKey, 42);
|
||||||
const transaction = SystemProgram.createAccount(
|
await Loader.load(connection, owner, programAccount, BpfLoader.programId, [
|
||||||
owner.publicKey,
|
1,
|
||||||
programAccount.publicKey,
|
2,
|
||||||
42,
|
|
||||||
3,
|
3,
|
||||||
BpfLoader.programId,
|
]);
|
||||||
);
|
|
||||||
await sendAndConfirmTransaction(connection, transaction, owner);
|
|
||||||
|
|
||||||
const loader = new Loader(connection, BpfLoader.programId);
|
|
||||||
await loader.load(programAccount, [1, 2, 3]);
|
|
||||||
|
|
||||||
// Wait for mockCallback to receive a call
|
// Wait for mockCallback to receive a call
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (mockCallback.mock.calls.length === 1) {
|
if (mockCallback.mock.calls.length > 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++i === 5) {
|
if (++i === 30) {
|
||||||
console.log(JSON.stringify(mockCallback.mock.calls));
|
throw new Error('Account change notification not observed');
|
||||||
throw new Error('mockCallback should be called twice');
|
|
||||||
}
|
}
|
||||||
// Sleep for a 1/4 of a slot, notifications only occur after a block is
|
// Sleep for a 1/4 of a slot, notifications only occur after a block is
|
||||||
// processed
|
// processed
|
||||||
@ -546,10 +541,8 @@ test('account change notification', async () => {
|
|||||||
|
|
||||||
await connection.removeAccountChangeListener(subscriptionId);
|
await connection.removeAccountChangeListener(subscriptionId);
|
||||||
|
|
||||||
expect(mockCallback.mock.calls[0][0].lamports).toBe(42);
|
expect(mockCallback.mock.calls[0][0].lamports).toBe(1);
|
||||||
expect(mockCallback.mock.calls[0][0].owner).toEqual(BpfLoader.programId);
|
expect(mockCallback.mock.calls[0][0].owner).toEqual(BpfLoader.programId);
|
||||||
expect(mockCallback.mock.calls[0][0].executable).toBe(false);
|
|
||||||
expect(mockCallback.mock.calls[0][0].data).toEqual(Buffer.from([1, 2, 3]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('program account change notification', async () => {
|
test('program account change notification', async () => {
|
||||||
@ -562,36 +555,35 @@ test('program account change notification', async () => {
|
|||||||
const owner = new Account();
|
const owner = new Account();
|
||||||
const programAccount = new Account();
|
const programAccount = new Account();
|
||||||
|
|
||||||
const mockCallback = jest.fn();
|
// const mockCallback = jest.fn();
|
||||||
|
|
||||||
|
let notified = false;
|
||||||
const subscriptionId = connection.onProgramAccountChange(
|
const subscriptionId = connection.onProgramAccountChange(
|
||||||
BpfLoader.programId,
|
BpfLoader.programId,
|
||||||
mockCallback,
|
keyedAccountInfo => {
|
||||||
|
if (keyedAccountInfo.accountId !== programAccount.publicKey.toString()) {
|
||||||
|
//console.log('Ignoring another account', keyedAccountInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(keyedAccountInfo.accountInfo.lamports).toBe(1);
|
||||||
|
expect(keyedAccountInfo.accountInfo.owner).toEqual(BpfLoader.programId);
|
||||||
|
notified = true;
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await connection.requestAirdrop(owner.publicKey, 42);
|
await connection.requestAirdrop(owner.publicKey, 42);
|
||||||
const transaction = SystemProgram.createAccount(
|
await Loader.load(connection, owner, programAccount, BpfLoader.programId, [
|
||||||
owner.publicKey,
|
1,
|
||||||
programAccount.publicKey,
|
2,
|
||||||
42,
|
|
||||||
3,
|
3,
|
||||||
BpfLoader.programId,
|
]);
|
||||||
);
|
|
||||||
await sendAndConfirmTransaction(connection, transaction, owner);
|
|
||||||
|
|
||||||
const loader = new Loader(connection, BpfLoader.programId);
|
|
||||||
await loader.load(programAccount, [1, 2, 3]);
|
|
||||||
|
|
||||||
// Wait for mockCallback to receive a call
|
// Wait for mockCallback to receive a call
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (;;) {
|
while (!notified) {
|
||||||
if (mockCallback.mock.calls.length === 1) {
|
//for (;;) {
|
||||||
break;
|
if (++i === 30) {
|
||||||
}
|
throw new Error('Program change notification not observed');
|
||||||
|
|
||||||
if (++i === 20) {
|
|
||||||
console.log(JSON.stringify(mockCallback.mock.calls));
|
|
||||||
throw new Error('mockCallback should be called twice');
|
|
||||||
}
|
}
|
||||||
// Sleep for a 1/4 of a slot, notifications only occur after a block is
|
// Sleep for a 1/4 of a slot, notifications only occur after a block is
|
||||||
// processed
|
// processed
|
||||||
@ -599,16 +591,4 @@ test('program account change notification', async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await connection.removeProgramAccountChangeListener(subscriptionId);
|
await connection.removeProgramAccountChangeListener(subscriptionId);
|
||||||
|
|
||||||
expect(mockCallback.mock.calls[0][0].accountId).toEqual(
|
|
||||||
programAccount.publicKey.toString(),
|
|
||||||
);
|
|
||||||
expect(mockCallback.mock.calls[0][0].accountInfo.lamports).toBe(42);
|
|
||||||
expect(mockCallback.mock.calls[0][0].accountInfo.owner).toEqual(
|
|
||||||
BpfLoader.programId,
|
|
||||||
);
|
|
||||||
expect(mockCallback.mock.calls[0][0].accountInfo.executable).toBe(false);
|
|
||||||
expect(mockCallback.mock.calls[0][0].accountInfo.data).toEqual(
|
|
||||||
Buffer.from([1, 2, 3]),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ import {url} from './url';
|
|||||||
|
|
||||||
export async function newAccountWithLamports(
|
export async function newAccountWithLamports(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
lamports: number = 10,
|
lamports: number = 1000000,
|
||||||
): Promise<Account> {
|
): Promise<Account> {
|
||||||
const account = new Account();
|
const account = new Account();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user