Properly unmarshall TransactionReceiptRPC to TransactionReceipt

This commit is contained in:
Leonid Logvinov 2018-11-20 14:48:32 +01:00
parent 953f8c119b
commit 22cfdd9f0b
No known key found for this signature in database
GPG Key ID: 0DD294BFDE8C95D4
7 changed files with 90 additions and 12 deletions

View File

@ -1,4 +1,17 @@
[ [
{
"version": "3.1.5",
"changes": [
{
"note": "Add unmarshalling of transaction receipts",
"pr": 1291
},
{
"note": "Return `undefined` instead of `null` if transaction receipt not found",
"pr": 1291
}
]
},
{ {
"timestamp": 1542208198, "timestamp": 1542208198,
"version": "3.1.4", "version": "3.1.4",

View File

@ -10,6 +10,7 @@
"scripts": { "scripts": {
"build": "tsc -b", "build": "tsc -b",
"build:ci": "yarn build", "build:ci": "yarn build",
"watch_without_deps": "tsc -w",
"clean": "shx rm -rf lib generated_docs", "clean": "shx rm -rf lib generated_docs",
"lint": "tslint --format stylish --project .", "lint": "tslint --format stylish --project .",
"test": "yarn run_mocha", "test": "yarn run_mocha",

View File

@ -52,6 +52,9 @@ export {
CallDataRPC, CallDataRPC,
BlockWithoutTransactionDataRPC, BlockWithoutTransactionDataRPC,
BlockWithTransactionDataRPC, BlockWithTransactionDataRPC,
TransactionReceiptStatusRPC,
TransactionReceiptRPC,
LogEntryRPC,
TransactionRPC, TransactionRPC,
TxDataRPC, TxDataRPC,
} from './types'; } from './types';

View File

@ -9,6 +9,7 @@ import {
LogEntry, LogEntry,
RawLogEntry, RawLogEntry,
Transaction, Transaction,
TransactionReceipt,
TxData, TxData,
} from 'ethereum-types'; } from 'ethereum-types';
import ethUtil = require('ethereumjs-util'); import ethUtil = require('ethereumjs-util');
@ -21,6 +22,7 @@ import {
BlockWithTransactionDataRPC, BlockWithTransactionDataRPC,
CallDataRPC, CallDataRPC,
CallTxDataBaseRPC, CallTxDataBaseRPC,
TransactionReceiptRPC,
TransactionRPC, TransactionRPC,
TxDataRPC, TxDataRPC,
} from './types'; } from './types';
@ -91,6 +93,22 @@ export const marshaller = {
}; };
return tx; return tx;
}, },
/**
* Unmarshall transaction receipt
* @param txReceiptRpc transaction receipt to unmarshall
* @return unmarshalled transaction receipt
*/
unmarshalTransactionReceipt(txReceiptRpc: TransactionReceiptRPC): TransactionReceipt {
const txReceipt = {
...txReceiptRpc,
blockNumber: utils.convertHexToNumber(txReceiptRpc.blockNumber),
transactionIndex: utils.convertHexToNumber(txReceiptRpc.transactionIndex),
cumulativeGasUsed: utils.convertHexToNumber(txReceiptRpc.cumulativeGasUsed),
gasUsed: utils.convertHexToNumber(txReceiptRpc.gasUsed),
logs: _.map(txReceiptRpc.logs, marshaller.unmarshalLog.bind(marshaller)),
};
return txReceipt;
},
/** /**
* Unmarshall transaction data * Unmarshall transaction data
* @param txDataRpc transaction data to unmarshall * @param txDataRpc transaction data to unmarshall

View File

@ -41,6 +41,33 @@ export interface TransactionRPC {
input: string; input: string;
} }
export interface TransactionReceiptRPC {
blockHash: string;
blockNumber: string;
transactionHash: string;
transactionIndex: string;
from: string;
to: string;
status: TransactionReceiptStatusRPC;
cumulativeGasUsed: string;
gasUsed: string;
contractAddress: string | null;
logs: LogEntryRPC[];
}
export interface LogEntryRPC {
logIndex: string | null;
transactionIndex: string | null;
transactionHash: string;
blockHash: string | null;
blockNumber: string | null;
address: string;
data: string;
topics: string[];
}
export type TransactionReceiptStatusRPC = null | string | 0 | 1;
export interface CallTxDataBaseRPC { export interface CallTxDataBaseRPC {
to?: string; to?: string;
value?: string; value?: string;

View File

@ -27,6 +27,7 @@ import {
BlockWithoutTransactionDataRPC, BlockWithoutTransactionDataRPC,
BlockWithTransactionDataRPC, BlockWithTransactionDataRPC,
NodeType, NodeType,
TransactionReceiptRPC,
TransactionRPC, TransactionRPC,
Web3WrapperErrors, Web3WrapperErrors,
} from './types'; } from './types';
@ -212,20 +213,23 @@ export class Web3Wrapper {
return networkId; return networkId;
} }
/** /**
* Retrieves the transaction receipt for a given transaction hash * Retrieves the transaction receipt for a given transaction hash if found
* @param txHash Transaction hash * @param txHash Transaction hash
* @returns The transaction receipt, including it's status (0: failed, 1: succeeded or undefined: not found) * @returns The transaction receipt, including it's status (0: failed, 1: succeeded). Returns undefined if transaction not found.
*/ */
public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { public async getTransactionReceiptIfExistsAsync(txHash: string): Promise<TransactionReceipt | undefined> {
assert.isHexString('txHash', txHash); assert.isHexString('txHash', txHash);
const transactionReceipt = await this.sendRawPayloadAsync<TransactionReceipt>({ const transactionReceiptRpc = await this.sendRawPayloadAsync<TransactionReceiptRPC>({
method: 'eth_getTransactionReceipt', method: 'eth_getTransactionReceipt',
params: [txHash], params: [txHash],
}); });
if (!_.isNull(transactionReceipt)) { if (!_.isNull(transactionReceiptRpc)) {
transactionReceipt.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceipt.status); transactionReceiptRpc.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceiptRpc.status);
} const transactionReceipt = marshaller.unmarshalTransactionReceipt(transactionReceiptRpc);
return transactionReceipt; return transactionReceipt;
} else {
return undefined;
}
} }
/** /**
* Retrieves the transaction data for a given transaction * Retrieves the transaction data for a given transaction
@ -572,8 +576,8 @@ export class Web3Wrapper {
assert.isNumber('timeoutMs', timeoutMs); assert.isNumber('timeoutMs', timeoutMs);
} }
// Immediately check if the transaction has already been mined. // Immediately check if the transaction has already been mined.
let transactionReceipt = await this.getTransactionReceiptAsync(txHash); let transactionReceipt = await this.getTransactionReceiptIfExistsAsync(txHash);
if (!_.isNull(transactionReceipt) && !_.isNull(transactionReceipt.blockNumber)) { if (!_.isUndefined(transactionReceipt) && !_.isNull(transactionReceipt.blockNumber)) {
const logsWithDecodedArgs = _.map( const logsWithDecodedArgs = _.map(
transactionReceipt.logs, transactionReceipt.logs,
this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder), this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder),
@ -600,8 +604,8 @@ export class Web3Wrapper {
return reject(Web3WrapperErrors.TransactionMiningTimeout); return reject(Web3WrapperErrors.TransactionMiningTimeout);
} }
transactionReceipt = await this.getTransactionReceiptAsync(txHash); transactionReceipt = await this.getTransactionReceiptIfExistsAsync(txHash);
if (!_.isNull(transactionReceipt)) { if (!_.isUndefined(transactionReceipt)) {
intervalUtils.clearAsyncExcludingInterval(intervalId); intervalUtils.clearAsyncExcludingInterval(intervalId);
const logsWithDecodedArgs = _.map( const logsWithDecodedArgs = _.map(
transactionReceipt.logs, transactionReceipt.logs,

View File

@ -1,5 +1,5 @@
import * as chai from 'chai'; import * as chai from 'chai';
import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload, TransactionReceipt } from 'ethereum-types';
import * as Ganache from 'ganache-core'; import * as Ganache from 'ganache-core';
import * as _ from 'lodash'; import * as _ from 'lodash';
import 'mocha'; import 'mocha';
@ -98,6 +98,18 @@ describe('Web3Wrapper tests', () => {
expect(typeof blockNumber).to.be.equal('number'); expect(typeof blockNumber).to.be.equal('number');
}); });
}); });
describe('#getTransactionReceiptAsync/awaitTransactionSuccessAsync', () => {
it('get block number', async () => {
const payload = { from: addresses[0], to: addresses[1], value: 1 };
const txHash = await web3Wrapper.sendTransactionAsync(payload);
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
const receiptIfExists = await web3Wrapper.getTransactionReceiptIfExistsAsync(txHash);
expect(receiptIfExists).to.not.be.undefined();
const receipt = receiptIfExists as TransactionReceipt;
expect(receipt.transactionIndex).to.be.a('number');
expect(receipt.transactionHash).to.be.equal(txHash);
});
});
describe('#getBlockIfExistsAsync', () => { describe('#getBlockIfExistsAsync', () => {
it('gets block when supplied a valid BlockParamLiteral value', async () => { it('gets block when supplied a valid BlockParamLiteral value', async () => {
const blockParamLiteral = BlockParamLiteral.Earliest; const blockParamLiteral = BlockParamLiteral.Earliest;