feat: make Transaction.populate method public and tweak MessageArgs
This commit is contained in:
committed by
Michael Vines
parent
22a63fe93c
commit
ad0e71d357
3
web3.js/module.d.ts
vendored
3
web3.js/module.d.ts
vendored
@ -423,7 +423,7 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
export type MessageArgs = {
|
export type MessageArgs = {
|
||||||
header: MessageHeader;
|
header: MessageHeader;
|
||||||
accountKeys: PublicKey[];
|
accountKeys: string[];
|
||||||
recentBlockhash: Blockhash;
|
recentBlockhash: Blockhash;
|
||||||
instructions: CompiledInstruction[];
|
instructions: CompiledInstruction[];
|
||||||
};
|
};
|
||||||
@ -487,6 +487,7 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
constructor(opts?: TransactionCtorFields);
|
constructor(opts?: TransactionCtorFields);
|
||||||
static from(buffer: Buffer | Uint8Array | Array<number>): Transaction;
|
static from(buffer: Buffer | Uint8Array | Array<number>): Transaction;
|
||||||
|
static populate(message: Message, signatures: Array<string>): Transaction;
|
||||||
add(
|
add(
|
||||||
...items: Array<
|
...items: Array<
|
||||||
Transaction | TransactionInstruction | TransactionInstructionCtorFields
|
Transaction | TransactionInstruction | TransactionInstructionCtorFields
|
||||||
|
@ -433,7 +433,7 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
declare export type MessageArgs = {
|
declare export type MessageArgs = {
|
||||||
header: MessageHeader,
|
header: MessageHeader,
|
||||||
accountKeys: PublicKey[],
|
accountKeys: string[],
|
||||||
recentBlockhash: Blockhash,
|
recentBlockhash: Blockhash,
|
||||||
instructions: CompiledInstruction[],
|
instructions: CompiledInstruction[],
|
||||||
};
|
};
|
||||||
@ -499,6 +499,7 @@ declare module '@solana/web3.js' {
|
|||||||
|
|
||||||
constructor(opts?: TransactionCtorFields): Transaction;
|
constructor(opts?: TransactionCtorFields): Transaction;
|
||||||
static from(buffer: Buffer | Uint8Array | Array<number>): Transaction;
|
static from(buffer: Buffer | Uint8Array | Array<number>): Transaction;
|
||||||
|
static populate(message: Message, signatures: Array<string>): Transaction;
|
||||||
add(
|
add(
|
||||||
...items: Array<
|
...items: Array<
|
||||||
Transaction | TransactionInstruction | TransactionInstructionCtorFields,
|
Transaction | TransactionInstruction | TransactionInstructionCtorFields,
|
||||||
|
@ -12,6 +12,7 @@ import {NonceAccount} from './nonce-account';
|
|||||||
import {PublicKey} from './publickey';
|
import {PublicKey} from './publickey';
|
||||||
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing';
|
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing';
|
||||||
import {Transaction} from './transaction';
|
import {Transaction} from './transaction';
|
||||||
|
import {Message} from './message';
|
||||||
import {sleep} from './util/sleep';
|
import {sleep} from './util/sleep';
|
||||||
import {toBuffer} from './util/to-buffer';
|
import {toBuffer} from './util/to-buffer';
|
||||||
import type {Blockhash} from './blockhash';
|
import type {Blockhash} from './blockhash';
|
||||||
@ -1580,27 +1581,26 @@ export class Connection {
|
|||||||
*/
|
*/
|
||||||
async getConfirmedBlock(slot: number): Promise<ConfirmedBlock> {
|
async getConfirmedBlock(slot: number): Promise<ConfirmedBlock> {
|
||||||
const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]);
|
const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]);
|
||||||
const result = GetConfirmedBlockRpcResult(unsafeRes);
|
const {result, error} = GetConfirmedBlockRpcResult(unsafeRes);
|
||||||
if (result.error) {
|
if (error) {
|
||||||
throw new Error('failed to get confirmed block: ' + result.error.message);
|
throw new Error('failed to get confirmed block: ' + result.error.message);
|
||||||
}
|
}
|
||||||
assert(typeof result.result !== 'undefined');
|
assert(typeof result !== 'undefined');
|
||||||
if (!result.result) {
|
if (!result) {
|
||||||
throw new Error('Confirmed block ' + slot + ' not found');
|
throw new Error('Confirmed block ' + slot + ' not found');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
blockhash: new PublicKey(result.result.blockhash).toString(),
|
blockhash: new PublicKey(result.blockhash).toString(),
|
||||||
previousBlockhash: new PublicKey(
|
previousBlockhash: new PublicKey(result.previousBlockhash).toString(),
|
||||||
result.result.previousBlockhash,
|
parentSlot: result.parentSlot,
|
||||||
).toString(),
|
transactions: result.transactions.map(result => {
|
||||||
parentSlot: result.result.parentSlot,
|
const {message, signatures} = result.transaction;
|
||||||
transactions: result.result.transactions.map(result => {
|
|
||||||
return {
|
return {
|
||||||
transaction: Transaction.fromRpcResult(result.transaction),
|
transaction: Transaction.populate(new Message(message), signatures),
|
||||||
meta: result.meta,
|
meta: result.meta,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
rewards: result.result.rewards || [],
|
rewards: result.rewards || [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1622,9 +1622,10 @@ export class Connection {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {message, signatures} = result.transaction;
|
||||||
return {
|
return {
|
||||||
slot: result.slot,
|
slot: result.slot,
|
||||||
transaction: Transaction.fromRpcResult(result.transaction),
|
transaction: Transaction.populate(new Message(message), signatures),
|
||||||
meta: result.meta,
|
meta: result.meta,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -42,13 +42,13 @@ export type CompiledInstruction = {
|
|||||||
*
|
*
|
||||||
* @typedef {Object} MessageArgs
|
* @typedef {Object} MessageArgs
|
||||||
* @property {MessageHeader} header The message header, identifying signed and read-only `accountKeys`
|
* @property {MessageHeader} header The message header, identifying signed and read-only `accountKeys`
|
||||||
* @property {PublicKey[]} accounts All the account keys used by this transaction
|
* @property {string[]} accounts All the account keys used by this transaction
|
||||||
* @property {Blockhash} recentBlockhash The hash of a recent ledger block
|
* @property {Blockhash} recentBlockhash The hash of a recent ledger block
|
||||||
* @property {CompiledInstruction[]} instructions Instructions that will be executed in sequence and committed in one atomic transaction if all succeed.
|
* @property {CompiledInstruction[]} instructions Instructions that will be executed in sequence and committed in one atomic transaction if all succeed.
|
||||||
*/
|
*/
|
||||||
type MessageArgs = {
|
type MessageArgs = {
|
||||||
header: MessageHeader,
|
header: MessageHeader,
|
||||||
accountKeys: PublicKey[],
|
accountKeys: string[],
|
||||||
recentBlockhash: Blockhash,
|
recentBlockhash: Blockhash,
|
||||||
instructions: CompiledInstruction[],
|
instructions: CompiledInstruction[],
|
||||||
};
|
};
|
||||||
@ -64,7 +64,7 @@ export class Message {
|
|||||||
|
|
||||||
constructor(args: MessageArgs) {
|
constructor(args: MessageArgs) {
|
||||||
this.header = args.header;
|
this.header = args.header;
|
||||||
this.accountKeys = args.accountKeys;
|
this.accountKeys = args.accountKeys.map(account => new PublicKey(account));
|
||||||
this.recentBlockhash = args.recentBlockhash;
|
this.recentBlockhash = args.recentBlockhash;
|
||||||
this.instructions = args.instructions;
|
this.instructions = args.instructions;
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ export class Transaction {
|
|||||||
let numReadonlySignedAccounts = 0;
|
let numReadonlySignedAccounts = 0;
|
||||||
let numReadonlyUnsignedAccounts = 0;
|
let numReadonlyUnsignedAccounts = 0;
|
||||||
|
|
||||||
const keys = this.signatures.map(({publicKey}) => publicKey.toString());
|
const accountKeys = this.signatures.map(({publicKey}) => publicKey.toString());
|
||||||
const programIds: string[] = [];
|
const programIds: string[] = [];
|
||||||
const accountMetas: AccountMeta[] = [];
|
const accountMetas: AccountMeta[] = [];
|
||||||
this.instructions.forEach(instruction => {
|
this.instructions.forEach(instruction => {
|
||||||
@ -233,8 +233,8 @@ export class Transaction {
|
|||||||
|
|
||||||
accountMetas.forEach(({pubkey, isSigner, isWritable}) => {
|
accountMetas.forEach(({pubkey, isSigner, isWritable}) => {
|
||||||
const keyStr = pubkey.toString();
|
const keyStr = pubkey.toString();
|
||||||
if (!keys.includes(keyStr)) {
|
if (!accountKeys.includes(keyStr)) {
|
||||||
keys.push(keyStr);
|
accountKeys.push(keyStr);
|
||||||
if (isSigner) {
|
if (isSigner) {
|
||||||
this.signatures.push({
|
this.signatures.push({
|
||||||
signature: null,
|
signature: null,
|
||||||
@ -250,8 +250,8 @@ export class Transaction {
|
|||||||
});
|
});
|
||||||
|
|
||||||
programIds.forEach(programId => {
|
programIds.forEach(programId => {
|
||||||
if (!keys.includes(programId)) {
|
if (!accountKeys.includes(programId)) {
|
||||||
keys.push(programId);
|
accountKeys.push(programId);
|
||||||
numReadonlyUnsignedAccounts += 1;
|
numReadonlyUnsignedAccounts += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -260,9 +260,9 @@ export class Transaction {
|
|||||||
instruction => {
|
instruction => {
|
||||||
const {data, programId} = instruction;
|
const {data, programId} = instruction;
|
||||||
return {
|
return {
|
||||||
programIdIndex: keys.indexOf(programId.toString()),
|
programIdIndex: accountKeys.indexOf(programId.toString()),
|
||||||
accounts: instruction.keys.map(keyObj =>
|
accounts: instruction.keys.map(keyObj =>
|
||||||
keys.indexOf(keyObj.pubkey.toString()),
|
accountKeys.indexOf(keyObj.pubkey.toString()),
|
||||||
),
|
),
|
||||||
data: bs58.encode(data),
|
data: bs58.encode(data),
|
||||||
};
|
};
|
||||||
@ -280,7 +280,7 @@ export class Transaction {
|
|||||||
numReadonlySignedAccounts,
|
numReadonlySignedAccounts,
|
||||||
numReadonlyUnsignedAccounts,
|
numReadonlyUnsignedAccounts,
|
||||||
},
|
},
|
||||||
accountKeys: keys.map(k => new PublicKey(k)),
|
accountKeys,
|
||||||
recentBlockhash,
|
recentBlockhash,
|
||||||
instructions,
|
instructions,
|
||||||
});
|
});
|
||||||
@ -477,11 +477,11 @@ export class Transaction {
|
|||||||
const numReadonlyUnsignedAccounts = byteArray.shift();
|
const numReadonlyUnsignedAccounts = byteArray.shift();
|
||||||
|
|
||||||
const accountCount = shortvec.decodeLength(byteArray);
|
const accountCount = shortvec.decodeLength(byteArray);
|
||||||
let accounts = [];
|
let accountKeys = [];
|
||||||
for (let i = 0; i < accountCount; i++) {
|
for (let i = 0; i < accountCount; i++) {
|
||||||
const account = byteArray.slice(0, PUBKEY_LENGTH);
|
const account = byteArray.slice(0, PUBKEY_LENGTH);
|
||||||
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
||||||
accounts.push(bs58.encode(Buffer.from(account)));
|
accountKeys.push(bs58.encode(Buffer.from(account)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const recentBlockhash = byteArray.slice(0, PUBKEY_LENGTH);
|
const recentBlockhash = byteArray.slice(0, PUBKEY_LENGTH);
|
||||||
@ -502,54 +502,24 @@ export class Transaction {
|
|||||||
instructions.push(instruction);
|
instructions.push(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = {
|
const messageArgs = {
|
||||||
header: {
|
header: {
|
||||||
numRequiredSignatures,
|
numRequiredSignatures,
|
||||||
numReadonlySignedAccounts,
|
numReadonlySignedAccounts,
|
||||||
numReadonlyUnsignedAccounts,
|
numReadonlyUnsignedAccounts,
|
||||||
},
|
},
|
||||||
recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)),
|
recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)),
|
||||||
accountKeys: accounts.map(account => new PublicKey(account)),
|
accountKeys,
|
||||||
instructions,
|
instructions,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Transaction._populate(signatures, new Message(message));
|
return Transaction.populate(new Message(messageArgs), signatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an RPC result into a Transaction object.
|
* Populate Transaction object from message and signatures
|
||||||
*/
|
*/
|
||||||
static fromRpcResult(rpcResult: any): Transaction {
|
static populate(message: Message, signatures: Array<string>): Transaction {
|
||||||
const signatures = rpcResult.signatures;
|
|
||||||
const accounts = rpcResult.message.accountKeys;
|
|
||||||
const instructions = rpcResult.message.instructions;
|
|
||||||
const recentBlockhash = rpcResult.message.recentBlockhash;
|
|
||||||
const numRequiredSignatures =
|
|
||||||
rpcResult.message.header.numRequiredSignatures;
|
|
||||||
const numReadonlySignedAccounts =
|
|
||||||
rpcResult.message.header.numReadonlySignedAccounts;
|
|
||||||
const numReadonlyUnsignedAccounts =
|
|
||||||
rpcResult.message.header.numReadonlyUnsignedAccounts;
|
|
||||||
|
|
||||||
const message = {
|
|
||||||
header: {
|
|
||||||
numRequiredSignatures,
|
|
||||||
numReadonlySignedAccounts,
|
|
||||||
numReadonlyUnsignedAccounts,
|
|
||||||
},
|
|
||||||
recentBlockhash,
|
|
||||||
accountKeys: accounts.map(account => new PublicKey(account)),
|
|
||||||
instructions,
|
|
||||||
};
|
|
||||||
|
|
||||||
return Transaction._populate(signatures, new Message(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate Transaction object
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
static _populate(signatures: Array<string>, message: Message): Transaction {
|
|
||||||
const transaction = new Transaction();
|
const transaction = new Transaction();
|
||||||
transaction.recentBlockhash = message.recentBlockhash;
|
transaction.recentBlockhash = message.recentBlockhash;
|
||||||
signatures.forEach((signature, index) => {
|
signatures.forEach((signature, index) => {
|
||||||
|
@ -7,6 +7,7 @@ import {PublicKey} from '../src/publickey';
|
|||||||
import {Transaction} from '../src/transaction';
|
import {Transaction} from '../src/transaction';
|
||||||
import {StakeProgram} from '../src/stake-program';
|
import {StakeProgram} from '../src/stake-program';
|
||||||
import {SystemProgram} from '../src/system-program';
|
import {SystemProgram} from '../src/system-program';
|
||||||
|
import {Message} from '../src/message';
|
||||||
|
|
||||||
test('signPartial', () => {
|
test('signPartial', () => {
|
||||||
const account1 = new Account();
|
const account1 = new Account();
|
||||||
@ -159,10 +160,9 @@ test('parse wire format and serialize', () => {
|
|||||||
expect(wireTransaction).toEqual(expectedTransaction.serialize());
|
expect(wireTransaction).toEqual(expectedTransaction.serialize());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transaction from rpc result', () => {
|
test('populate transaction', () => {
|
||||||
const recentBlockhash = new PublicKey(1).toString();
|
const recentBlockhash = new PublicKey(1).toString();
|
||||||
const rpcResult = {
|
const message = {
|
||||||
message: {
|
|
||||||
accountKeys: [
|
accountKeys: [
|
||||||
new PublicKey(1).toString(),
|
new PublicKey(1).toString(),
|
||||||
new PublicKey(2).toString(),
|
new PublicKey(2).toString(),
|
||||||
@ -171,7 +171,7 @@ test('transaction from rpc result', () => {
|
|||||||
new PublicKey(5).toString(),
|
new PublicKey(5).toString(),
|
||||||
],
|
],
|
||||||
header: {
|
header: {
|
||||||
num_ReadonlySignedAccounts: 0,
|
numReadonlySignedAccounts: 0,
|
||||||
numReadonlyUnsignedAccounts: 3,
|
numReadonlyUnsignedAccounts: 3,
|
||||||
numRequiredSignatures: 2,
|
numRequiredSignatures: 2,
|
||||||
},
|
},
|
||||||
@ -183,14 +183,14 @@ test('transaction from rpc result', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
recentBlockhash,
|
recentBlockhash,
|
||||||
},
|
|
||||||
signatures: [
|
|
||||||
bs58.encode(Buffer.alloc(64).fill(1)),
|
|
||||||
bs58.encode(Buffer.alloc(64).fill(2)),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const transaction = Transaction.fromRpcResult(rpcResult);
|
const signatures = [
|
||||||
|
bs58.encode(Buffer.alloc(64).fill(1)),
|
||||||
|
bs58.encode(Buffer.alloc(64).fill(2)),
|
||||||
|
];
|
||||||
|
|
||||||
|
const transaction = Transaction.populate(new Message(message), signatures);
|
||||||
expect(transaction.instructions.length).toEqual(1);
|
expect(transaction.instructions.length).toEqual(1);
|
||||||
expect(transaction.signatures.length).toEqual(2);
|
expect(transaction.signatures.length).toEqual(2);
|
||||||
expect(transaction.recentBlockhash).toEqual(recentBlockhash);
|
expect(transaction.recentBlockhash).toEqual(recentBlockhash);
|
||||||
|
Reference in New Issue
Block a user