fix: pay transaction fees from system accounts
This commit is contained in:
@@ -3,8 +3,6 @@
|
||||
import {Account} from './account';
|
||||
import {PublicKey} from './publickey';
|
||||
import {Loader} from './loader';
|
||||
import {SystemProgram} from './system-program';
|
||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
||||
import type {Connection} from './connection';
|
||||
|
||||
/**
|
||||
@@ -15,9 +13,7 @@ export class BpfLoader {
|
||||
* Public key that identifies the BpfLoader
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey(
|
||||
'BPFLoader1111111111111111111111111111111111',
|
||||
);
|
||||
return new PublicKey('BPFLoader1111111111111111111111111111111111');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,26 +23,12 @@ export class BpfLoader {
|
||||
* @param owner User account to load the program into
|
||||
* @param elfBytes The entire ELF containing the BPF program
|
||||
*/
|
||||
static async load(
|
||||
static load(
|
||||
connection: Connection,
|
||||
owner: Account,
|
||||
payer: Account,
|
||||
elf: Array<number>,
|
||||
): Promise<PublicKey> {
|
||||
const programAccount = new Account();
|
||||
|
||||
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;
|
||||
const program = new Account();
|
||||
return Loader.load(connection, payer, program, BpfLoader.programId, elf);
|
||||
}
|
||||
}
|
||||
|
@@ -143,9 +143,7 @@ export class BudgetProgram {
|
||||
* Public key that identifies the Budget program
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey(
|
||||
'Budget1111111111111111111111111111111111111',
|
||||
);
|
||||
return new PublicKey('Budget1111111111111111111111111111111111111');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -626,7 +626,6 @@ export class Connection {
|
||||
subscriptionId,
|
||||
programId,
|
||||
} = this._programAccountChangeSubscriptions[id];
|
||||
console.log('program-id: ' + programId);
|
||||
if (subscriptionId === null) {
|
||||
try {
|
||||
this._programAccountChangeSubscriptions[
|
||||
|
@@ -9,43 +9,46 @@ import {Transaction} from './transaction';
|
||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
||||
import {sleep} from './util/sleep';
|
||||
import type {Connection} from './connection';
|
||||
import {SystemProgram} from './system-program';
|
||||
|
||||
/**
|
||||
* Program loader interface
|
||||
*/
|
||||
export class Loader {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
programId: PublicKey;
|
||||
|
||||
/**
|
||||
* Amount of program data placed in each load Transaction
|
||||
*/
|
||||
static get chunkSize(): number {
|
||||
return 256;
|
||||
return 229; // Keep program chunks under PACKET_DATA_SIZE
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connection The connection to use
|
||||
* @param programId Public key that identifies the loader
|
||||
*/
|
||||
constructor(connection: Connection, programId: PublicKey) {
|
||||
Object.assign(this, {connection, programId});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load program data
|
||||
* Loads a generic program
|
||||
*
|
||||
* @param program Account to load the program info
|
||||
* @param data Program data
|
||||
* @param connection The connection to use
|
||||
* @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([
|
||||
BufferLayout.u32('instruction'),
|
||||
BufferLayout.u32('offset'),
|
||||
@@ -76,11 +79,11 @@ export class Loader {
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: program.publicKey, isSigner: true}],
|
||||
programId: this.programId,
|
||||
programId,
|
||||
data,
|
||||
});
|
||||
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
|
||||
@@ -100,29 +103,26 @@ export class Loader {
|
||||
array = array.slice(chunkSize);
|
||||
}
|
||||
await Promise.all(transactions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')]);
|
||||
// Finalize the account loaded with program data for execution
|
||||
{
|
||||
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
|
||||
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Finalize instruction
|
||||
},
|
||||
data,
|
||||
);
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Finalize instruction
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: program.publicKey, isSigner: true}],
|
||||
programId: this.programId,
|
||||
data,
|
||||
});
|
||||
await sendAndConfirmTransaction(this.connection, transaction, program);
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: program.publicKey, isSigner: true}],
|
||||
programId,
|
||||
data,
|
||||
});
|
||||
await sendAndConfirmTransaction(connection, transaction, payer, program);
|
||||
}
|
||||
return program.publicKey;
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,6 @@
|
||||
import {Account} from './account';
|
||||
import {PublicKey} from './publickey';
|
||||
import {Loader} from './loader';
|
||||
import {SystemProgram} from './system-program';
|
||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
||||
import type {Connection} from './connection';
|
||||
|
||||
/**
|
||||
@@ -15,41 +13,29 @@ export class NativeLoader {
|
||||
* Public key that identifies the NativeLoader
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey(
|
||||
'NativeLoader1111111111111111111111111111111',
|
||||
);
|
||||
return new PublicKey('NativeLoader1111111111111111111111111111111');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a native program
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
static async load(
|
||||
static load(
|
||||
connection: Connection,
|
||||
owner: Account,
|
||||
payer: Account,
|
||||
programName: string,
|
||||
): Promise<PublicKey> {
|
||||
const bytes = [...Buffer.from(programName)];
|
||||
|
||||
const programAccount = new Account();
|
||||
|
||||
// Allocate memory for the program account
|
||||
const transaction = SystemProgram.createAccount(
|
||||
owner.publicKey,
|
||||
programAccount.publicKey,
|
||||
1 + 1 + 1,
|
||||
bytes.length + 1,
|
||||
const program = new Account();
|
||||
return Loader.load(
|
||||
connection,
|
||||
payer,
|
||||
program,
|
||||
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,
|
||||
data,
|
||||
});
|
||||
await sendAndConfirmTransaction(connection, transaction, tokenAccount);
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
owner,
|
||||
tokenAccount,
|
||||
);
|
||||
|
||||
return [token, initialAccountPublicKey];
|
||||
}
|
||||
@@ -299,7 +304,12 @@ export class Token {
|
||||
programId: this.programId,
|
||||
data,
|
||||
});
|
||||
await sendAndConfirmTransaction(this.connection, transaction, tokenAccount);
|
||||
await sendAndConfirmTransaction(
|
||||
this.connection,
|
||||
transaction,
|
||||
owner,
|
||||
tokenAccount,
|
||||
);
|
||||
|
||||
return tokenAccount.publicKey;
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ export class Transaction {
|
||||
signatures: Array<SignaturePubkeyPair> = [];
|
||||
|
||||
/**
|
||||
* The first (primary) Transaction signature
|
||||
* The first (payer) Transaction signature
|
||||
*/
|
||||
get signature(): Buffer | null {
|
||||
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 = [];
|
||||
shortvec.encodeLength(keyCount, keys.length);
|
||||
|
||||
@@ -252,7 +260,7 @@ export class Transaction {
|
||||
]);
|
||||
|
||||
const transaction = {
|
||||
numRequiredSignatures: Buffer.from([numRequiredSignatures]),
|
||||
numRequiredSignatures: Buffer.from([this.signatures.length]),
|
||||
keyCount: Buffer.from(keyCount),
|
||||
keys: keys.map(key => new PublicKey(key).toBuffer()),
|
||||
recentBlockhash: Buffer.from(bs58.decode(recentBlockhash)),
|
||||
|
Reference in New Issue
Block a user