feat(system-program): add createAccountWithSeed

This commit is contained in:
Rob Walker 2019-12-09 19:50:01 -08:00 committed by Michael Vines
parent cc550dfb08
commit 0760853871
4 changed files with 120 additions and 2 deletions

View File

@ -220,6 +220,14 @@ declare module '@solana/web3.js' {
amount: number,
): Transaction;
static assign(from: PublicKey, programId: PublicKey): Transaction;
static createAccountWithSeed(
from: PublicKey,
newAccount: PublicKey,
seed: string,
lamports: number,
space: number,
programId: PublicKey,
): Transaction;
}
// === src/validator-info.js ===

View File

@ -43,5 +43,25 @@ export const rustString = (property: string = 'string') => {
return _encode(data, buffer, offset);
};
rsl.alloc = str => {
return (
BufferLayout.u32().span +
BufferLayout.u32().span +
Buffer.from(str, 'utf8').length
);
};
return rsl;
};
export function getAlloc(type: Object, fields: Object): number {
let alloc = 0;
type.layout.fields.forEach(item => {
if (item.span >= 0) {
alloc += item.span;
} else if (typeof item.alloc === 'function') {
alloc += item.alloc(fields[item.property]);
}
});
return alloc;
}

View File

@ -66,6 +66,7 @@ export class SystemInstruction extends TransactionInstruction {
get fromPublicKey(): PublicKey | null {
if (
this.type == SystemInstructionLayout.Create ||
this.type == SystemInstructionLayout.CreateWithSeed ||
this.type == SystemInstructionLayout.Transfer
) {
return this.keys[0].pubkey;
@ -80,6 +81,7 @@ export class SystemInstruction extends TransactionInstruction {
get toPublicKey(): PublicKey | null {
if (
this.type == SystemInstructionLayout.Create ||
this.type == SystemInstructionLayout.CreateWithSeed ||
this.type == SystemInstructionLayout.Transfer
) {
return this.keys[1].pubkey;
@ -95,7 +97,10 @@ export class SystemInstruction extends TransactionInstruction {
const data = this.type.layout.decode(this.data);
if (this.type == SystemInstructionLayout.Transfer) {
return data.amount;
} else if (this.type == SystemInstructionLayout.Create) {
} else if (
this.type == SystemInstructionLayout.Create ||
this.type == SystemInstructionLayout.CreateWithSeed
) {
return data.lamports;
}
return null;
@ -139,13 +144,25 @@ const SystemInstructionLayout = Object.freeze({
BufferLayout.ns64('amount'),
]),
},
CreateWithSeed: {
index: 3,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.rustString('seed'),
BufferLayout.ns64('lamports'),
BufferLayout.ns64('space'),
Layout.publicKey('programId'),
]),
},
});
/**
* Populate a buffer of instruction data using the SystemInstructionType
*/
function encodeData(type: SystemInstructionType, fields: Object): Buffer {
const data = Buffer.alloc(type.layout.span);
const allocLength =
type.layout.span >= 0 ? type.layout.span : Layout.getAlloc(type, fields);
const data = Buffer.alloc(allocLength);
const layoutFields = Object.assign({instruction: type.index}, fields);
type.layout.encode(layoutFields, data);
return data;
@ -221,4 +238,34 @@ export class SystemProgram {
data,
});
}
/**
* Generate a Transaction that creates a new account at
* an address generated with `from`, a seed, and programId
*/
static createAccountWithSeed(
from: PublicKey,
newAccount: PublicKey,
seed: string,
lamports: number,
space: number,
programId: PublicKey,
): Transaction {
const type = SystemInstructionLayout.CreateWithSeed;
const data = encodeData(type, {
seed,
lamports,
space,
programId: programId.toBuffer(),
});
return new Transaction().add({
keys: [
{pubkey: from, isSigner: true, isWritable: true},
{pubkey: newAccount, isSigner: false, isWritable: true},
],
programId: SystemProgram.programId,
data,
});
}
}

View File

@ -50,6 +50,25 @@ test('assign', () => {
// TODO: Validate transaction contents more
});
test('createAccountWithSeed', () => {
const from = new Account();
const newAccount = new Account();
let transaction;
transaction = SystemProgram.createAccountWithSeed(
from.publicKey,
newAccount.publicKey,
'hi there',
123,
BudgetProgram.space,
BudgetProgram.programId,
);
expect(transaction.keys).toHaveLength(2);
expect(transaction.programId).toEqual(SystemProgram.programId);
// TODO: Validate transaction contents more
});
test('SystemInstruction create', () => {
const from = new Account();
const to = new Account();
@ -104,6 +123,30 @@ test('SystemInstruction assign', () => {
expect(systemInstruction.programId).toEqual(SystemProgram.programId);
});
test('SystemInstruction createWithSeed', () => {
const from = new Account();
const to = new Account();
const program = new Account();
const amount = 42;
const space = 100;
const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash
const create = SystemProgram.createAccountWithSeed(
from.publicKey,
to.publicKey,
'hi there',
amount,
space,
program.publicKey,
);
const transaction = new Transaction({recentBlockhash}).add(create);
const systemInstruction = SystemInstruction.from(transaction.instructions[0]);
expect(systemInstruction.fromPublicKey).toEqual(from.publicKey);
expect(systemInstruction.toPublicKey).toEqual(to.publicKey);
expect(systemInstruction.amount).toEqual(amount);
expect(systemInstruction.programId).toEqual(SystemProgram.programId);
});
test('non-SystemInstruction error', () => {
const from = new Account();
const program = new Account();