web3-core-helpers
Version:
Web3 core tools helper for sub packages. This is an internal package.
535 lines (445 loc) • 15.9 kB
JavaScript
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file formatters.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @author Marek Kotewicz <marek@parity.io>
* @date 2017
*/
;
var utils = require('web3-utils');
var Iban = require('web3-eth-iban');
/**
* Will format the given storage key array values to hex strings.
*
* @method inputStorageKeysFormatter
*
* @param {Array<Number|String|BN|BigNumber>} keys
*
* @returns {Array<String>}
*/
var inputStorageKeysFormatter = function (keys) {
return keys.map(utils.numberToHex);
};
/**
* Will format the given proof response from the node.
*
* @method outputProofFormatter
*
* @param {object} proof
*
* @returns {object}
*/
var outputProofFormatter = function (proof) {
proof.address = utils.toChecksumAddress(proof.address);
proof.nonce = utils.hexToNumberString(proof.nonce);
proof.balance = utils.hexToNumberString(proof.balance);
return proof;
};
/**
* Should the format output to a big number
*
* @method outputBigNumberFormatter
*
* @param {String|Number|BigNumber|BN} number
*
* @returns {BN} object
*/
var outputBigNumberFormatter = function (number) {
return utils.toBN(number).toString(10);
};
/**
* Returns true if the given blockNumber is 'latest', 'pending', 'earliest, 'finalized' or 'safe'
*
* @method isPredefinedBlockNumber
*
* @param {String} blockNumber
*
* @returns {Boolean}
*/
var isPredefinedBlockNumber = function (blockNumber) {
return ['latest','pending','earliest','finalized','safe'].includes(blockNumber);
};
/**
* Returns the given block number as hex string or does return the defaultBlock property of the current module
*
* @method inputDefaultBlockNumberFormatter
*
* @param {String|Number|BN|BigNumber} blockNumber
*
* @returns {String}
*/
var inputDefaultBlockNumberFormatter = function (blockNumber) {
if (this && (blockNumber === undefined || blockNumber === null)) {
return inputBlockNumberFormatter(this.defaultBlock);
}
return inputBlockNumberFormatter(blockNumber);
};
/**
* Returns the given block number as hex string or the predefined block number 'latest', 'pending', 'earliest', 'finalized', 'safe', 'genesis'
*
* @param {String|Number|BN|BigNumber} blockNumber
*
* @returns {String}
*/
var inputBlockNumberFormatter = function (blockNumber) {
if (blockNumber === undefined) {
return undefined;
}
if (isPredefinedBlockNumber(blockNumber)) {
return blockNumber;
}
if (blockNumber === 'genesis') {
return '0x0';
}
return (utils.isHexStrict(blockNumber)) ? ((typeof blockNumber === 'string') ? blockNumber.toLowerCase() : blockNumber) : utils.numberToHex(blockNumber);
};
/**
* Formats the input of a transaction and converts all values to HEX
*
* @method _txInputFormatter
* @param {Object} transaction options
* @returns object
*/
var _txInputFormatter = function (options) {
if (options.to) { // it might be contract creation
options.to = inputAddressFormatter(options.to);
}
if (options.data && options.input) {
throw new Error('You can\'t have "data" and "input" as properties of transactions at the same time, please use either "data" or "input" instead.');
}
if (!options.data && options.input) {
options.data = options.input;
delete options.input;
}
if (options.data && !options.data.startsWith('0x')) {
options.data = '0x' + options.data;
}
if (options.data && !utils.isHex(options.data)) {
throw new Error('The data field must be HEX encoded data.');
}
// allow both
if (options.gas || options.gasLimit) {
options.gas = options.gas || options.gasLimit;
}
if (options.maxPriorityFeePerGas || options.maxFeePerGas) {
delete options.gasPrice;
}
['gasPrice', 'gas', 'value', 'maxPriorityFeePerGas', 'maxFeePerGas', 'nonce', 'type'].filter(function (key) {
return options[key] !== undefined;
}).forEach(function (key) {
options[key] = utils.numberToHex(options[key]);
});
return options;
};
/**
* Formats the input of a transaction and converts all values to HEX
*
* @method inputCallFormatter
* @param {Object} transaction options
* @returns object
*/
var inputCallFormatter = function (options) {
options = _txInputFormatter(options);
var from = options.from || (this ? this.defaultAccount : null);
if (from) {
options.from = inputAddressFormatter(from);
}
return options;
};
/**
* Formats the input of a transaction and converts all values to HEX
*
* @method inputTransactionFormatter
* @param {Object} options
* @returns object
*/
var inputTransactionFormatter = function (options) {
options = _txInputFormatter(options);
// check from, only if not number, or object
if (!(typeof options.from === 'number') && !(!!options.from && typeof options.from === 'object')) {
options.from = options.from || (this ? this.defaultAccount : null);
if (!options.from && !(typeof options.from === 'number')) {
throw new Error('The send transactions "from" field must be defined!');
}
options.from = inputAddressFormatter(options.from);
}
return options;
};
/**
* Hex encodes the data passed to eth_sign and personal_sign
*
* @method inputSignFormatter
* @param {String} data
* @returns {String}
*/
var inputSignFormatter = function (data) {
return (utils.isHexStrict(data)) ? data : utils.utf8ToHex(data);
};
/**
* Formats the output of a transaction to its proper values
*
* @method outputTransactionFormatter
* @param {Object} tx
* @returns {Object}
*/
var outputTransactionFormatter = function (tx, hexFormat) {
if (!hexFormat) {
if (tx.blockNumber !== null)
tx.blockNumber = utils.hexToNumber(tx.blockNumber);
if (tx.transactionIndex !== null)
tx.transactionIndex = utils.hexToNumber(tx.transactionIndex);
tx.nonce = utils.hexToNumber(tx.nonce);
tx.gas = utils.hexToNumber(tx.gas);
if (tx.type)
tx.type = utils.hexToNumber(tx.type);
}
if (tx.gasPrice)
tx.gasPrice = outputBigNumberFormatter(tx.gasPrice);
if (tx.maxFeePerGas)
tx.maxFeePerGas = outputBigNumberFormatter(tx.maxFeePerGas);
if (tx.maxPriorityFeePerGas)
tx.maxPriorityFeePerGas = outputBigNumberFormatter(tx.maxPriorityFeePerGas);
tx.value = outputBigNumberFormatter(tx.value);
if (tx.to && utils.isAddress(tx.to)) { // tx.to could be `0x0` or `null` while contract creation
tx.to = utils.toChecksumAddress(tx.to);
} else {
tx.to = null; // set to `null` if invalid address
}
if (tx.from) {
tx.from = utils.toChecksumAddress(tx.from);
}
return tx;
};
/**
* Formats the output of a transaction receipt to its proper values
*
* @method outputTransactionReceiptFormatter
* @param {Object} receipt
* @returns {Object}
*/
var outputTransactionReceiptFormatter = function (receipt) {
if (typeof receipt !== 'object') {
throw new Error('Received receipt is invalid: ' + receipt);
}
if(!this.hexFormat){
if (receipt.blockNumber !== null)
receipt.blockNumber = utils.hexToNumber(receipt.blockNumber);
if (receipt.transactionIndex !== null)
receipt.transactionIndex = utils.hexToNumber(receipt.transactionIndex);
receipt.cumulativeGasUsed = utils.hexToNumber(receipt.cumulativeGasUsed);
receipt.gasUsed = utils.hexToNumber(receipt.gasUsed);
if (receipt.effectiveGasPrice) {
receipt.effectiveGasPrice = utils.hexToNumber(receipt.effectiveGasPrice)
}
}
if (Array.isArray(receipt.logs)) {
receipt.logs = receipt.logs.map(outputLogFormatter);
}
if (receipt.contractAddress) {
receipt.contractAddress = utils.toChecksumAddress(receipt.contractAddress);
}
if (typeof receipt.status !== 'undefined' && receipt.status !== null) {
receipt.status = Boolean(parseInt(receipt.status));
}
return receipt;
};
/**
* Formats the output of a block to its proper values
*
* @method outputBlockFormatter
* @param {Object} block
* @returns {Object}
*/
var outputBlockFormatter = function (block, hexFormat) {
if (!hexFormat) {
// transform to number
block.gasLimit = utils.hexToNumber(block.gasLimit);
block.gasUsed = utils.hexToNumber(block.gasUsed);
block.size = utils.hexToNumber(block.size);
block.timestamp = utils.hexToNumber(block.timestamp);
if (block.number !== null)
block.number = utils.hexToNumber(block.number);
}
if (block.difficulty)
block.difficulty = outputBigNumberFormatter(block.difficulty);
if (block.totalDifficulty)
block.totalDifficulty = outputBigNumberFormatter(block.totalDifficulty);
if (Array.isArray(block.transactions)) {
block.transactions.forEach(function (item) {
if (!(typeof item === 'string'))
return outputTransactionFormatter(item, hexFormat);
});
}
if (block.miner)
block.miner = utils.toChecksumAddress(block.miner);
if (block.baseFeePerGas)
block.baseFeePerGas = utils.hexToNumber(block.baseFeePerGas)
return block;
};
/**
* Formats the input of a log
*
* @method inputLogFormatter
* @param {Object} log object
* @returns {Object} log
*/
var inputLogFormatter = function (options) {
var toTopic = function (value) {
if (value === null || typeof value === 'undefined')
return null;
value = String(value);
if (value.indexOf('0x') === 0)
return value;
else
return utils.fromUtf8(value);
};
if (options === undefined) options = {}
// If options !== undefined, don't blow out existing data
if (options.fromBlock === undefined) options = {...options, fromBlock: 'latest'}
if (options.fromBlock || options.fromBlock === 0)
options.fromBlock = inputBlockNumberFormatter(options.fromBlock);
if (options.toBlock || options.toBlock === 0)
options.toBlock = inputBlockNumberFormatter(options.toBlock);
// make sure topics, get converted to hex
options.topics = options.topics || [];
options.topics = options.topics.map(function (topic) {
return (Array.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
});
toTopic = null;
if (options.address) {
options.address = (Array.isArray(options.address)) ? options.address.map(function (addr) {
return inputAddressFormatter(addr);
}) : inputAddressFormatter(options.address);
}
return options;
};
/**
* Formats the output of a log
*
* @method outputLogFormatter
* @param {Object} log object
* @returns {Object} log
*/
var outputLogFormatter = function (log) {
// generate a custom log id
if (typeof log.blockHash === 'string' &&
typeof log.transactionHash === 'string' &&
typeof log.logIndex === 'string') {
var shaId = utils.sha3(log.blockHash.replace('0x', '') + log.transactionHash.replace('0x', '') + log.logIndex.replace('0x', ''));
log.id = 'log_' + shaId.replace('0x', '').slice(0, 8);
} else if (!log.id) {
log.id = null;
}
if (log.blockNumber !== null)
log.blockNumber = utils.hexToNumber(log.blockNumber);
if (log.transactionIndex !== null)
log.transactionIndex = utils.hexToNumber(log.transactionIndex);
if (log.logIndex !== null)
log.logIndex = utils.hexToNumber(log.logIndex);
if (log.address) {
log.address = utils.toChecksumAddress(log.address);
}
return log;
};
/**
* Formats the input of a whisper post and converts all values to HEX
*
* @method inputPostFormatter
* @param {Object} transaction object
* @returns {Object}
*/
var inputPostFormatter = function (post) {
// post.payload = utils.toHex(post.payload);
if (post.ttl)
post.ttl = utils.numberToHex(post.ttl);
if (post.workToProve)
post.workToProve = utils.numberToHex(post.workToProve);
if (post.priority)
post.priority = utils.numberToHex(post.priority);
// fallback
if (!Array.isArray(post.topics)) {
post.topics = post.topics ? [post.topics] : [];
}
// format the following options
post.topics = post.topics.map(function (topic) {
// convert only if not hex
return (topic.indexOf('0x') === 0) ? topic : utils.fromUtf8(topic);
});
return post;
};
/**
* Formats the output of a received post message
*
* @method outputPostFormatter
* @param {Object}
* @returns {Object}
*/
var outputPostFormatter = function (post) {
post.expiry = utils.hexToNumber(post.expiry);
post.sent = utils.hexToNumber(post.sent);
post.ttl = utils.hexToNumber(post.ttl);
post.workProved = utils.hexToNumber(post.workProved);
// post.payloadRaw = post.payload;
// post.payload = utils.hexToAscii(post.payload);
// if (utils.isJson(post.payload)) {
// post.payload = JSON.parse(post.payload);
// }
// format the following options
if (!post.topics) {
post.topics = [];
}
post.topics = post.topics.map(function (topic) {
return utils.toUtf8(topic);
});
return post;
};
var inputAddressFormatter = function (address) {
var iban = new Iban(address);
if (iban.isValid() && iban.isDirect()) {
return iban.toAddress().toLowerCase();
} else if (utils.isAddress(address)) {
return '0x' + address.toLowerCase().replace('0x', '');
}
throw new Error(`Provided address ${address} is invalid, the capitalization checksum test failed, or it's an indirect IBAN address which can't be converted.`);
};
var outputSyncingFormatter = function (result) {
result.startingBlock = utils.hexToNumber(result.startingBlock);
result.currentBlock = utils.hexToNumber(result.currentBlock);
result.highestBlock = utils.hexToNumber(result.highestBlock);
if (result.knownStates) {
result.knownStates = utils.hexToNumber(result.knownStates);
result.pulledStates = utils.hexToNumber(result.pulledStates);
}
return result;
};
module.exports = {
inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter,
inputBlockNumberFormatter: inputBlockNumberFormatter,
inputCallFormatter: inputCallFormatter,
inputTransactionFormatter: inputTransactionFormatter,
inputAddressFormatter: inputAddressFormatter,
inputPostFormatter: inputPostFormatter,
inputLogFormatter: inputLogFormatter,
inputSignFormatter: inputSignFormatter,
inputStorageKeysFormatter: inputStorageKeysFormatter,
outputProofFormatter: outputProofFormatter,
outputBigNumberFormatter: outputBigNumberFormatter,
outputTransactionFormatter: outputTransactionFormatter,
outputTransactionReceiptFormatter: outputTransactionReceiptFormatter,
outputBlockFormatter: outputBlockFormatter,
outputLogFormatter: outputLogFormatter,
outputPostFormatter: outputPostFormatter,
outputSyncingFormatter: outputSyncingFormatter
};