Socket
Socket
Sign inDemoInstall

eth-gas-reporter

Package Overview
Dependencies
Maintainers
1
Versions
63
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eth-gas-reporter - npm Package Compare versions

Comparing version 0.0.7 to 0.0.8

mock/contracts/VariableConstructor.sol

122

gasStats.js

@@ -14,2 +14,3 @@ /**

const blockLimit = 6718946;
/**

@@ -34,3 +35,3 @@ * Expresses gas usage as a nation-state currency price

*/
function gasToPercentOfLimit(gasUsed, blockLimit = 6718946){
function gasToPercentOfLimit(gasUsed){
return Math.round(1000 * gasUsed / blockLimit) / 10;

@@ -49,6 +50,6 @@ }

/**
* Prints a gas stats table to stdout. Source: Gnosis / Alan Lu (see issues)
* Prints a gas stats table to stdout. Based on Alan Lu's stats for Gnosis
* @param {Object} methodMap methods and their gas usage (from mapMethodToContracts)
*/
async function generateGasStatsReport (methodMap) {
async function generateGasStatsReport (methodMap, deployMap) {
const {

@@ -61,3 +62,3 @@ currency,

// Compose rows
const rows = [];
const methodRows = [];

@@ -86,3 +87,5 @@ _.forEach(methodMap, (data, methodId) => {

section = [];
stats.numberOfCalls = data.numberOfCalls.toString().grey;
const section = [];
section.push(data.contract.grey);

@@ -93,7 +96,41 @@ section.push(data.method)

section.push({hAlign: 'right', content: stats.average})
section.push({hAlign: 'right', content: stats.numberOfCalls})
section.push({hAlign: 'right', content: stats.cost.toString().green})
rows.push(section);
methodRows.push(section);
})
const deployRows = [];
deployMap.sort((a,b) => a.name.localeCompare(b.name));
deployMap.forEach(contract => {
let stats = {};
if (!contract.gasData.length) return;
const total = contract.gasData.reduce((acc, datum) => acc + datum, 0)
stats.average = Math.round(total / contract.gasData.length)
stats.percent = gasToPercentOfLimit(stats.average);
stats.cost = (ethPrice && gasPrice) ? gasToCost(stats.average, ethPrice, gasPrice) : '-'.grey;
const sortedData = contract.gasData.sort((a,b) => a - b);
stats.min = sortedData[0]
stats.max = sortedData[sortedData.length - 1]
const uniform = (stats.min === stats.max);
stats.min = (uniform) ? '-' : stats.min.toString().yellow;
stats.max = (uniform) ? '-' : stats.max.toString().red;
section = [];
section.push({hAlign: 'left', colSpan: 2, content: contract.name});
section.push({hAlign: 'right', content: stats.min})
section.push({hAlign: 'right', content: stats.max})
section.push({hAlign: 'right', content: stats.average})
section.push({hAlign: 'right', content: `${stats.percent} %`.grey})
section.push({hAlign: 'right', content: stats.cost.toString().green})
deployRows.push(section);
});
// Format table

@@ -107,3 +144,9 @@ const table = new Table({

let title;
// Format and load methods metrics
let title = [
{hAlign: 'center', colSpan: 5, content: 'Gas Usage Metrics'.green.bold},
{hAlign: 'center', colSpan: 2, content: `Block limit: ${blockLimit} gas`.grey }
];
let methodSubtitle;
if (ethPrice && gasPrice){

@@ -113,25 +156,27 @@ const gwei = parseInt(gasPrice) * 1e-9;

title = [
{hAlign: 'center', colSpan: 2, content: 'Gas Usage Analysis'.green.bold},
methodSubtitle = [
{hAlign: 'left', colSpan: 2, content: 'Methods'.green.bold},
{hAlign: 'center', colSpan: 3, content: `${gwei} gwei/gas`.grey},
{hAlign: 'center', colSpan: 2, content: `${rate} ${currency.toLowerCase()}/eth`.red},
{hAlign: 'center', colSpan: 2, content: `${gwei} gwei/gas`.grey},
];
} else {
title = [{hAlign: 'center', colSpan: 6, content: 'Gas Analytics'.green.bold}];
methodSubtitle = [{hAlign: 'left', colSpan: 7, content: 'Methods'.green.bold}];
}
const header = [
'Contract'.bold,
'Method'.bold,
'Min'.green,
'Max'.green,
'Avg'.green,
`${currency.toLowerCase()} (avg)`.bold
]
'Contract'.bold,
'Method'.bold,
'Min'.green,
'Max'.green,
'Avg'.green,
'# calls'.bold,
`${currency.toLowerCase()} (avg)`.bold
]
table.push(title);
table.push(methodSubtitle);
table.push(header);
// Sort rows by contract, then method and push
rows.sort((a,b) => {
methodRows.sort((a,b) => {
const contractName = a[0].localeCompare(b[0]);

@@ -142,4 +187,14 @@ const methodName = a[1].localeCompare(b[1]);

rows.forEach(row => table.push(row));
methodRows.forEach(row => table.push(row));
if (deployRows.length){
const deploymentsSubtitle = [
{hAlign: 'left', colSpan: 2, content: 'Deployments'.green.bold},
{hAlign: 'right', colSpan: 3, content: '' },
{hAlign: 'left', colSpan: 1, content: `% of limit`.bold}
];
table.push(deploymentsSubtitle);
deployRows.forEach(row => table.push(row));
}
// Print

@@ -224,2 +279,3 @@ console.log(table.toString())

const methodMap = {}
const deployMap = []
const abis = []

@@ -235,4 +291,11 @@

// Load all the artifacts
// Create Deploy Map:
const contract = truffleArtifacts.require(name)
const contractInfo = {
name: name.split('.sol')[0],
binary: contract.unlinked_binary,
gasData: []
}
deployMap.push(contractInfo)
abis.push(contract._json.abi)

@@ -244,3 +307,3 @@

// Create Map;
// Create Method Map;
Object.keys(methodIDs).forEach(key => {

@@ -255,3 +318,4 @@ const isConstant = methodIDs[key].constant

method: methodIDs[key].name,
gasData: []
gasData: [],
numberOfCalls: 0
}

@@ -264,3 +328,7 @@ }

abis.forEach(abi => abiDecoder.addABI(abi))
return methodMap
return {
methodMap: methodMap,
deployMap: deployMap
}
}

@@ -275,7 +343,7 @@

module.exports = {
mapMethodsToContracts: mapMethodsToContracts,
getMethodID: getMethodID,
getGasAndPriceRates: getGasAndPriceRates,
gasToPercentOfLimit: gasToPercentOfLimit,
generateGasStatsReport: generateGasStatsReport,
getGasAndPriceRates: getGasAndPriceRates,
getMethodID: getMethodID,
mapMethodsToContracts: mapMethodsToContracts,
pretty: pretty

@@ -282,0 +350,0 @@ }

@@ -17,3 +17,5 @@ const mocha = require('mocha')

let startBlock
let deployStartBlock
let methodMap
let deployMap

@@ -23,3 +25,3 @@ // ------------------------------------ Helpers -------------------------------------------------

const gasAnalytics = (methodMap) => {
const methodAnalytics = (methodMap) => {
let gasUsed = 0

@@ -35,7 +37,11 @@ const endBlock = web3.eth.blockNumber

methodMap && block.transactions.forEach(tx => {
const input = web3.eth.getTransaction(tx).input;
const transaction = web3.eth.getTransaction(tx);
const receipt = web3.eth.getTransactionReceipt(tx);
const id = stats.getMethodID( input );
if (methodMap[id]){
const id = stats.getMethodID( transaction.input );
const threw = receipt.gasUsed === transaction.gas; // Change this @ Byzantium
if (methodMap[id] && !threw){
methodMap[id].gasData.push(receipt.gasUsed);
methodMap[id].numberOfCalls++;
}

@@ -49,5 +55,27 @@ })

const deployAnalytics = (deployMap) => {
const endBlock = web3.eth.blockNumber
while(deployStartBlock <= endBlock){
const block = web3.eth.getBlock(deployStartBlock)
block && block.transactions.forEach(tx => {
const transaction = web3.eth.getTransaction(tx)
const receipt = web3.eth.getTransactionReceipt(tx);
if (receipt.contractAddress){
const match = deployMap.filter(contract => {
return (transaction.input.indexOf(contract.binary) === 0)
})[0];
match && match.gasData.push(receipt.gasUsed);
}
});
deployStartBlock++
}
}
// ------------------------------------ Runners -------------------------------------------------
runner.on('start', () => {
methodMap = stats.mapMethodsToContracts(artifacts)
({ methodMap, deployMap } = stats.mapMethodsToContracts(artifacts));
log()

@@ -73,2 +101,3 @@ })

runner.on('test', () => { deployStartBlock = web3.eth.blockNumber })
runner.on('hook end', () => { startBlock = web3.eth.blockNumber + 1 })

@@ -80,21 +109,16 @@

let gasUsedString
let gasUsed = gasAnalytics(methodMap)
let percent = stats.gasToPercentOfLimit(gasUsed);
deployAnalytics(deployMap);
let gasUsed = methodAnalytics(methodMap)
if (gasUsed){
gasUsedString = color('checkmark', ' (%d gas)');
if (percent >= 100){
limitString = color('fail', ' (%d% of limit) ')
} else {
limitString = ' (%d% of limit) '
}
fmt = indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s') +
gasUsedString +
limitString
gasUsedString;
log(fmt, test.title, gasUsed, percent)
log(fmt, test.title, gasUsed)
} else {
fmt = indent() +

@@ -109,3 +133,2 @@ color('checkmark', ' ' + Base.symbols.ok) +

runner.on('fail', test => {
let gasUsed = gasAnalytics()
let fmt = indent() + color('fail', ' %d) %s')

@@ -117,3 +140,3 @@ log()

runner.on('end', () => {
stats.generateGasStatsReport (methodMap)
stats.generateGasStatsReport (methodMap, deployMap)
self.epilogue()

@@ -120,0 +143,0 @@ })

@@ -5,2 +5,3 @@ var ConvertLib = artifacts.require('./ConvertLib.sol')

var VariableCosts = artifacts.require('./VariableCosts.sol')
var VariableConstructor = artifacts.require('./VariableConstructor');

@@ -13,2 +14,3 @@ module.exports = function (deployer) {

deployer.deploy(VariableCosts);
deployer.deploy(VariableConstructor)
}
{
"name": "eth-gas-reporter",
"version": "0.0.6",
"version": "0.0.7",
"description": "Mocha reporter which shows gas used per unit test.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -40,2 +40,11 @@ const VariableCosts = artifacts.require('./VariableCosts.sol');

})
it('methods that do not throw', async() => {
await instance.methodThatThrows(false);
});
it('methods that throw', async() => {
try {await instance.methodThatThrows(true)}
catch(e){}
});
})
{
"name": "eth-gas-reporter",
"version": "0.0.7",
"version": "0.0.8",
"description": "Mocha reporter which shows gas used per unit test.",

@@ -5,0 +5,0 @@ "main": "index.js",

# eth-gas-reporter
Gas usage per unit test. Average gas usage per method. A mocha reporter.
A mocha reporter for Truffle.
+ Gas usage per unit test.
+ Average gas usage per method.
+ Contract deployment costs.
+ Real currency costs.
![screen shot 2017-10-12 at 8 02 57 pm](https://user-images.githubusercontent.com/7332026/31528529-b0d52178-af88-11e7-9c58-004ba4a7b360.png)
![screen shot 2017-10-22 at 4 03 57 pm](https://user-images.githubusercontent.com/7332026/31867351-c45a5a80-b742-11e7-98dd-49051684e5fd.png)
### Install
```javascript
// Truffle/mocha installed globally
```
// Truffle installed globally
npm install -g eth-gas-reporter
// Truffle/mocha installed locally
// Truffle installed locally (ProTip: This always works.)
npm install --save-dev eth-gas-reporter

@@ -24,3 +28,3 @@ ```

port: 8545,
network_id: "*" // Match any network id
network_id: "*"
}

@@ -33,7 +37,13 @@ },

```
### Thanks
Everything here has been cannibalized from elsewhere and dropped into mocha's reporter facility. Many thanks to:
+ [@maurelian](https://github.com/maurelian) - once posted a slack picture of mocha reporting gas output instead time. Idea from him.
+ [@cag](https://github.com/cag) - wrote a really nice gas stats utility for the Gnosis suites. The table borrows from / is based his work.
+ [Neufund](https://github.com/Neufund/ico-contracts) - expressing gas usage as a ratio of the block limit in their ico suite. That comes from them.
### Usage Notes
+ Euro/Eth rates are loaded at run-time from the `coinmarketcap` api
+ Gas prices are `safe-low` and loaded at run-time from the `blockcypher` api
+ Method calls that throw are filtered from the stats.
+ Contracts that link to libraries are not shown in the deployments table.
### Credits
All the ideas in this utility have been borrowed from elsewhere. Many thanks to:
+ [@maurelian](https://github.com/maurelian) - Mocha reporting gas instead of time is his idea.
+ [@cag](https://github.com/cag) - The table borrows from / is based his gas statistics work for the Gnosis contracts.
+ [Neufund](https://github.com/Neufund/ico-contracts) - Block limit size ratios for contract deployments and euro pricing are borrowed from their `ico-contracts` test suite.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc