Stop instrumenting receive() for statements / fns (to save gas) (#517)

pull/518/head
cgewecke 5 years ago committed by GitHub
parent 1ba04ac038
commit f1fb8a0f49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      lib/parse.js
  2. 8
      lib/registrar.js
  3. 24
      test/integration/projects/solc-6/contracts/B_Wallet.sol
  4. 30
      test/integration/projects/solc-6/test/b_wallet.js
  5. 7
      test/units/buidler/standard.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;
}
};

@ -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;

@ -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;
}
}

@ -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);
});
});

@ -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);

Loading…
Cancel
Save