diff --git a/lib/parse.js b/lib/parse.js index f7bfca3..29706d7 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -105,9 +105,15 @@ parse.ForStatement = function(contract, expression) { parse.FunctionDefinition = function(contract, expression) { parse.Modifiers(contract, expression.modifiers); if (expression.body) { - register.functionDeclaration(contract, expression); + // Skip fn & statement instrumentation for `receive` methods to + // minimize gas distortion + (expression.name === null && expression.isReceiveEther) + ? register.trackStatements = false + : register.functionDeclaration(contract, expression); + parse[expression.body.type] && parse[expression.body.type](contract, expression.body); + register.trackStatements = true; } }; diff --git a/lib/registrar.js b/lib/registrar.js index aaa794e..8ba7884 100644 --- a/lib/registrar.js +++ b/lib/registrar.js @@ -5,7 +5,11 @@ */ class Registrar { - constructor(){} + constructor(){ + // Sometimes we don't want to inject statements + // because they're an unnecessary expense. ex: `receive` + this.trackStatements = true; + } /** * Adds injection point to injection points map @@ -27,6 +31,8 @@ class Registrar { * @param {Object} expression AST node */ statement(contract, expression) { + if (!this.trackStatements) return; + const startContract = contract.instrumented.slice(0, expression.range[0]); const startline = ( startContract.match(/\n/g) || [] ).length + 1; const startcol = expression.range[0] - startContract.lastIndexOf('\n') - 1; diff --git a/test/integration/projects/solc-6/contracts/B_Wallet.sol b/test/integration/projects/solc-6/contracts/B_Wallet.sol new file mode 100644 index 0000000..d47470d --- /dev/null +++ b/test/integration/projects/solc-6/contracts/B_Wallet.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.6.0; + +contract B_Wallet { + + event Deposit(address indexed _sender, uint _value, bytes data); + + receive() external payable + { + if (msg.value > 0) + emit Deposit(msg.sender, msg.value, msg.data); + } + + function transferPayment(uint payment, address payable recipient) public { + recipient.transfer(payment); + } + + function sendPayment(uint payment, address payable recipient) public { + require(recipient.send(payment), 'sendPayment failed'); + } + + function getBalance() public view returns(uint){ + return address(this).balance; + } +} diff --git a/test/integration/projects/solc-6/test/b_wallet.js b/test/integration/projects/solc-6/test/b_wallet.js new file mode 100644 index 0000000..da91d33 --- /dev/null +++ b/test/integration/projects/solc-6/test/b_wallet.js @@ -0,0 +1,30 @@ +const Wallet = artifacts.require('B_Wallet'); + +contract('B_Wallet', accounts => { + it('should should allow transfers and sends', async () => { + const walletA = await Wallet.new(); + const walletB = await Wallet.new(); + + await walletA.sendTransaction({ + value: web3.utils.toBN(500), from: accounts[0], + }); + + await walletA.sendPayment(50, walletB.address, { + from: accounts[0], + }); + + await walletA.transferPayment(50, walletB.address, { + from: accounts[0], + }); + + // Also try transferring 0, for branch hit + await walletA.transferPayment(0, walletB.address, { + from: accounts[0], + }); + + // Throws invalid opcode if compiled w/ solc >= 0.5.14 & default EVM version + const balance = await walletB.getBalance(); + assert.equal(balance.toNumber(), 100); + }); +}); + diff --git a/test/units/buidler/standard.js b/test/units/buidler/standard.js index 1eb66fd..be127d6 100644 --- a/test/units/buidler/standard.js +++ b/test/units/buidler/standard.js @@ -289,7 +289,12 @@ describe('Buidler Plugin: standard use cases', function() { { file: mock.pathToContract(buidlerConfig, 'ContractB.sol'), pct: 0, - } + }, + { + file: mock.pathToContract(buidlerConfig, 'B_Wallet.sol'), + pct: 100, + }, + ]; verify.lineCoverage(expected);