blockchainworkbench / workbench-ui

UI of blockchain workbench landing page
0 stars 0 forks source link

proper estimate gas for test contracts #25

Closed severus-sn4pe closed 5 years ago

severus-sn4pe commented 5 years ago

With the existing code, web3 estimates the gas used for the function call always to the same value (53000).

The problem with that approach is that it doesn't consider the data sent as parameters, which is relevant to the calculation of estimateGas() according to https://web3js.readthedocs.io/en/1.0/web3-eth.html#estimategas

Because our test functions always have the same signature with only one parameter, this should be easy to add. I have now added the data payload:

const testFunctionData = web3Abi.encodeFunctionCall(test, [addresses]);
web3.eth.estimateGas({
    from: web3.eth.accounts[0],
    to: test.address,
    data: testFunctionData
}, (err, gas) => { ... })

On one function I've tested I get now an Estimate of 55067, which is slightly higher than the value before, but not even close to the required gas limit of 88898. Some other contract I've tested required a gas limit of 64469. But this functions does only work an some contracts (exercise on 3Functions/Payable). Others return an MetaMask - RPC Error: Internal JSON-RPC error. error and I can't figure out why.

As far as I understand, the gas is depending on what the actual function does, so more complex functions should require more gas. How is it then possible, that estimateGas does only consider the function signature and its parameters?

So the next approach I wanted to try was just run estimateGas without data once for simulating the costs of the nested contract call, and once with data for the outer test contract call and add those costs together.

web3.eth.estimateGas({from: web3.eth.accounts[0]}, (err, gas) => {
    web3.eth.estimateGas({from: web3.eth.accounts[0], data: testFunctionData2}, (err2, gas2) =>
        txParams.gas = gas + (gas2 || gas);
    });
});

This leads to the gas estimation of approx. 106000 - 110000, which seems to be enough for all contracts we currently have.

But I'm not sure if that's the right way to solve that. It fells a bit hacky.

Also, the occasional metamask error is irritating. Currently, if that error happens, I just use estimateGas() without the data parameter and multiply that by 2. It feels like I could just do that always and avoid the nesting of two estimateGas calls.

Any ideas / input on that, @thibmeu ?

thibmeu commented 5 years ago

Using web3 0.20.x, you can get encoded data using https://github.com/ethereum/wiki/wiki/JavaScript-API#contract-methods

Then you just have to set data field to estimate gas cost.

severus-sn4pe commented 5 years ago

I updated the code and use now the getData() method from web3 directly, but I get the same results. The data I used before for estimateGas() was identical.

For now I simply add the constant 53000 to the estimated gas limit to simulate some additional costs for the nested contract call...

const testFunctionData = contract[test.name].getData([addresses]);
web3.eth.estimateGas({from: web3.eth.accounts[0], data: testFunctionData}, (err, gas) => {
    try {
        const newGas = 53000 + (gas || 53000);
        txParams.gas = newGas;
        // ...
    } catch (err) {
        errors.push(err);
        resolve({result: false, errors: errors});
    }
});