Газовые соображения

Газ, более подробно описанный в [gas], является невероятно важным фактором при программировании смарт-контрактов. Газ - это ресурс, ограничивающий максимальный объем вычислений, который Ethereum позволит потреблять транзакции. Если во время вычислений лимит газа будет превышен, произойдет следующая серия событий:

  • Возникает исключение "закончился бензин".
  • Восстанавливается (возвращается) состояние контракта до его исполнения.
  • Весь эфир, использованный для оплаты газа, берется в качестве платы за транзакцию; он не возвращается.

Поскольку газ оплачивается пользователем, инициирующим транзакцию, пользователям не рекомендуется вызывать функции, которые имеют высокую стоимость газа. Поэтому в интересах программиста минимизировать стоимость газа для функций контракта. Для этого существуют определенные практики, которые рекомендуется применять при разработке смарт-контрактов, чтобы минимизировать стоимость газа при вызове функции.

Избегайте массивов динамического размера

Любой цикл по динамически изменяемому массиву, в котором функция выполняет операции над каждым элементом или ищет определенный элемент, создает риск использования слишком большого количества газа. Действительно, контракт может исчерпать свой запас газа до того, как будет найден желаемый результат, или до того, как будет произведено действие над каждым элементом, что приведет к потере времени и эфира без получения какого-либо результата.

Избегайте обращений к другим контрактам

Вызов других контрактов, особенно если стоимость газа их функций неизвестна, создает риск того, что газ закончится. Избегайте использования библиотек, которые недостаточно хорошо протестированы и широко используются. Чем меньше библиотека была проверена другими программистами, тем выше риск ее использования.

Оценка стоимости газа

Если вам нужно оценить количество газа, необходимое для выполнения определенного метода контракта с учетом его аргументов, вы можете воспользоваться следующей процедурой:

var contract = web3.eth.contract(abi).at(address);
var gasEstimate = contract.myAweSomeMethod.estimateGas(arg1, arg2,
    {from: account});

gasEstimate сообщит вам количество единиц газа, необходимое для ее выполнения. Это оценка, потому что полнота Тьюринга в EVM - относительно тривиально создать функцию, для выполнения разных вызовов которой потребуется совершенно разное количество газа. Даже производственный код может тонко изменять пути выполнения, что приводит к огромным различиям в затратах газа от одного вызова к другому. Однако большинство функций разумны, и estimateGas в большинстве случаев дает хорошую оценку. Для получения цены на газ из сети вы можете использовать: var gasPrice = web3.eth.getGasPrice(); Отсюда можно оценить стоимость газа: var gasCostInEther = web3.utils.fromWei((gasEstimate * gasPrice), 'ether'); Давайте применим наши функции оценки стоимости газа для оценки стоимости газа в нашем примере Faucet, используя код из репозитория книги. Запустите Truffle в режиме разработки и выполните файл JavaScript gas_estimates.js: Используя функцию estimateGas, gas_estimates.js. Пример 5. gas_estimates.js: Использование функции estimateGas

var FaucetContract = artifacts.require("./Faucet.sol");

FaucetContract.web3.eth.getGasPrice(function(error, result) {
    var gasPrice = Number(result);
    console.log("Gas Price is " + gasPrice + " wei"); // "10000000000000"

    // Get the contract instance
    FaucetContract.deployed().then(function(FaucetContractInstance) {

		// Use the keyword 'estimateGas' after the function name to get the gas
		// estimation for this particular function (aprove)
		FaucetContractInstance.send(web3.utils.toWei(1, "ether"));
        return FaucetContractInstance.withdraw.estimateGas(web3.utils.toWei(0.1, "ether"));

    }).then(function(result) {
        var gas = Number(result);

        console.log("gas estimation = " + gas + " units");
        console.log("gas cost estimation = " + (gas * gasPrice) + " wei");
        console.log("gas cost estimation = " +
                FaucetContract.web3.utils.fromWei((gas * gasPrice), 'ether') + " ether");
    });
});

Вот как это выглядит в консоли разработки Truffle:

$ truffle develop

truffle(develop)> exec gas_estimates.js
Using network 'develop'.

Gas Price is 20000000000 wei
gas estimation = 31397 units
gas cost estimation = 627940000000000 wei
gas cost estimation = 0.00062794 ether

Рекомендуется оценить газовую стоимость функций в рамках рабочего процесса разработки, чтобы избежать неожиданностей при развертывании контрактов в mainnet.