Skip to content

Commit f1fb8a0

Browse files
authored
Stop instrumenting receive() for statements / fns (to save gas) (sc-forks#517)
1 parent 1ba04ac commit f1fb8a0

File tree

5 files changed

+74
-3
lines changed

5 files changed

+74
-3
lines changed

lib/parse.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,15 @@ parse.ForStatement = function(contract, expression) {
105105
parse.FunctionDefinition = function(contract, expression) {
106106
parse.Modifiers(contract, expression.modifiers);
107107
if (expression.body) {
108-
register.functionDeclaration(contract, expression);
108+
// Skip fn & statement instrumentation for `receive` methods to
109+
// minimize gas distortion
110+
(expression.name === null && expression.isReceiveEther)
111+
? register.trackStatements = false
112+
: register.functionDeclaration(contract, expression);
113+
109114
parse[expression.body.type] &&
110115
parse[expression.body.type](contract, expression.body);
116+
register.trackStatements = true;
111117
}
112118
};
113119

lib/registrar.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
*/
66
class Registrar {
77

8-
constructor(){}
8+
constructor(){
9+
// Sometimes we don't want to inject statements
10+
// because they're an unnecessary expense. ex: `receive`
11+
this.trackStatements = true;
12+
}
913

1014
/**
1115
* Adds injection point to injection points map
@@ -27,6 +31,8 @@ class Registrar {
2731
* @param {Object} expression AST node
2832
*/
2933
statement(contract, expression) {
34+
if (!this.trackStatements) return;
35+
3036
const startContract = contract.instrumented.slice(0, expression.range[0]);
3137
const startline = ( startContract.match(/\n/g) || [] ).length + 1;
3238
const startcol = expression.range[0] - startContract.lastIndexOf('\n') - 1;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pragma solidity ^0.6.0;
2+
3+
contract B_Wallet {
4+
5+
event Deposit(address indexed _sender, uint _value, bytes data);
6+
7+
receive() external payable
8+
{
9+
if (msg.value > 0)
10+
emit Deposit(msg.sender, msg.value, msg.data);
11+
}
12+
13+
function transferPayment(uint payment, address payable recipient) public {
14+
recipient.transfer(payment);
15+
}
16+
17+
function sendPayment(uint payment, address payable recipient) public {
18+
require(recipient.send(payment), 'sendPayment failed');
19+
}
20+
21+
function getBalance() public view returns(uint){
22+
return address(this).balance;
23+
}
24+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const Wallet = artifacts.require('B_Wallet');
2+
3+
contract('B_Wallet', accounts => {
4+
it('should should allow transfers and sends', async () => {
5+
const walletA = await Wallet.new();
6+
const walletB = await Wallet.new();
7+
8+
await walletA.sendTransaction({
9+
value: web3.utils.toBN(500), from: accounts[0],
10+
});
11+
12+
await walletA.sendPayment(50, walletB.address, {
13+
from: accounts[0],
14+
});
15+
16+
await walletA.transferPayment(50, walletB.address, {
17+
from: accounts[0],
18+
});
19+
20+
// Also try transferring 0, for branch hit
21+
await walletA.transferPayment(0, walletB.address, {
22+
from: accounts[0],
23+
});
24+
25+
// Throws invalid opcode if compiled w/ solc >= 0.5.14 & default EVM version
26+
const balance = await walletB.getBalance();
27+
assert.equal(balance.toNumber(), 100);
28+
});
29+
});
30+

test/units/buidler/standard.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,12 @@ describe('Buidler Plugin: standard use cases', function() {
289289
{
290290
file: mock.pathToContract(buidlerConfig, 'ContractB.sol'),
291291
pct: 0,
292-
}
292+
},
293+
{
294+
file: mock.pathToContract(buidlerConfig, 'B_Wallet.sol'),
295+
pct: 100,
296+
},
297+
293298
];
294299

295300
verify.lineCoverage(expected);

0 commit comments

Comments
 (0)