feat: add getParsedConfirmedTransaction API

This commit is contained in:
Justin Starry
2020-08-06 19:16:01 +08:00
committed by Justin Starry
parent 5a63c9d535
commit b36e60738e
7 changed files with 414 additions and 133 deletions

View File

@@ -49,7 +49,7 @@ type Context = {
* @property {boolean | undefined} skipPreflight disable transaction verification step
*/
export type SendOptions = {
skipPreflight: ?boolean,
skipPreflight?: boolean,
};
/**
@@ -60,8 +60,8 @@ export type SendOptions = {
* @property {number | undefined} confirmations desired number of cluster confirmations
*/
export type ConfirmOptions = {
skipPreflight: ?boolean,
confirmations: ?number,
skipPreflight?: boolean,
confirmations?: number,
};
/**
@@ -378,6 +378,88 @@ type ConfirmedTransaction = {
meta: ConfirmedTransactionMeta | null,
};
/**
* A partially decoded transaction instruction
*
* @typedef {Object} ParsedMessageAccount
* @property {PublicKey} pubkey Public key of the account
* @property {PublicKey} accounts Indicates if the account signed the transaction
* @property {string} data Raw base-58 instruction data
*/
type PartiallyDecodedInstruction = {|
programId: PublicKey,
accounts: Array<PublicKey>,
data: string,
|};
/**
* A parsed transaction message account
*
* @typedef {Object} ParsedMessageAccount
* @property {PublicKey} pubkey Public key of the account
* @property {boolean} signer Indicates if the account signed the transaction
* @property {boolean} writable Indicates if the account is writable for this transaction
*/
type ParsedMessageAccount = {
pubkey: PublicKey,
signer: boolean,
writable: boolean,
};
/**
* A parsed transaction instruction
*
* @typedef {Object} ParsedInstruction
* @property {string} program Name of the program for this instruction
* @property {PublicKey} programId ID of the program for this instruction
* @property {any} parsed Parsed instruction info
*/
type ParsedInstruction = {|
program: string,
programId: PublicKey,
parsed: any,
|};
/**
* A parsed transaction message
*
* @typedef {Object} ParsedMessage
* @property {Array<ParsedMessageAccount>} accountKeys Accounts used in the instructions
* @property {Array<ParsedInstruction | PartiallyDecodedInstruction>} instructions The atomically executed instructions for the transaction
* @property {string} recentBlockhash Recent blockhash
*/
type ParsedMessage = {
accountKeys: ParsedMessageAccount[],
instructions: (ParsedInstruction | PartiallyDecodedInstruction)[],
recentBlockhash: string,
};
/**
* A parsed transaction
*
* @typedef {Object} ParsedTransaction
* @property {Array<string>} signatures Signatures for the transaction
* @property {ParsedMessage} message Message of the transaction
*/
type ParsedTransaction = {
signatures: Array<string>,
message: ParsedMessage,
};
/**
* A parsed and confirmed transaction on the ledger
*
* @typedef {Object} ParsedConfirmedTransaction
* @property {number} slot The slot during which the transaction was processed
* @property {ParsedTransaction} transaction The details of the transaction
* @property {ConfirmedTransactionMeta|null} meta Metadata produced from the transaction
*/
type ParsedConfirmedTransaction = {
slot: number,
transaction: ParsedTransaction,
meta: ConfirmedTransactionMeta | null,
};
/**
* A ConfirmedBlock on the ledger
*
@@ -807,13 +889,41 @@ const ConfirmedTransactionResult = struct({
numReadonlySignedAccounts: 'number',
numReadonlyUnsignedAccounts: 'number',
}),
instructions: struct.array([
struct({
accounts: struct.array(['number']),
data: 'string',
programIdIndex: 'number',
}),
]),
recentBlockhash: 'string',
}),
});
/**
* @private
*/
const ParsedConfirmedTransactionResult = struct({
signatures: struct.array(['string']),
message: struct({
accountKeys: struct.array([
struct({
pubkey: 'string',
signer: 'boolean',
writable: 'boolean',
}),
]),
instructions: struct.array([
struct.union([
struct.array(['number']),
struct({
accounts: struct.array(['number']),
accounts: struct.array(['string']),
data: 'string',
programIdIndex: 'number',
programId: 'string',
}),
struct({
parsed: 'any',
program: 'string',
programId: 'string',
}),
]),
]),
@@ -877,6 +987,20 @@ const GetConfirmedTransactionRpcResult = jsonRpcResult(
]),
);
/**
* Expected JSON RPC response for the "getConfirmedTransaction" message
*/
const GetParsedConfirmedTransactionRpcResult = jsonRpcResult(
struct.union([
'null',
struct.pick({
slot: 'number',
transaction: ParsedConfirmedTransactionResult,
meta: ConfirmedTransactionMetaResult,
}),
]),
);
/**
* Expected JSON RPC response for the "getRecentBlockhash" message
*/
@@ -1853,6 +1977,56 @@ export class Connection {
};
}
/**
* Fetch parsed transaction details for a confirmed transaction
*/
async getParsedConfirmedTransaction(
signature: TransactionSignature,
): Promise<ParsedConfirmedTransaction | null> {
const unsafeRes = await this._rpcRequest('getConfirmedTransaction', [
signature,
'jsonParsed',
]);
const {result, error} = GetParsedConfirmedTransactionRpcResult(unsafeRes);
if (error) {
throw new Error('failed to get confirmed transaction: ' + error.message);
}
assert(typeof result !== 'undefined');
if (result === null) return result;
const {
accountKeys,
instructions,
recentBlockhash,
} = result.transaction.message;
return {
slot: result.slot,
meta: result.meta,
transaction: {
signatures: result.transaction.signatures,
message: {
accountKeys: accountKeys.map(accountKey => ({
pubkey: new PublicKey(accountKey.pubkey),
signer: accountKey.signer,
writable: accountKey.writable,
})),
instructions: instructions.map(ix => {
let mapped: any = {programId: new PublicKey(ix.programId)};
if ('accounts' in ix) {
mapped.accounts = ix.accounts.map(key => new PublicKey(key));
}
return {
...ix,
...mapped,
};
}),
recentBlockhash,
},
},
};
}
/**
* Fetch a list of all the confirmed signatures for transactions involving an address
* within a specified slot range. Max range allowed is 10,000 slots.