Skip to main content

Using Payable

In Solidity, leveraging the payable keyword can be a subtle yet effective way to optimize gas usage. In this article, we explore two distinct scenarios where using payable can lead to gas savings: in constructors and admin functions.

Payable Constructor

The way you write constructors can influence the deployment cost of your contracts, particularly with respect to gas usage. The Ethereum Virtual Machine (EVM) requires gas for all operations, including contract deployment.

Demo Code

Below, we have two simple contracts, BasicConstructor and AdvancedConstructor. Both are minimal, but they differ in whether the constructor is marked as payable.

  • BasicConstructor has a non-payable constructor.
  • AdvancedConstructor has a payable constructor, allowing it to receive Ether during deployment.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract BasicConstructor {
constructor() {} // Gas: 67161
}

contract AdvancedConstructor {
constructor() payable {} // Gas: 67102
}

When deployed, the AdvancedConstructor uses less gas (67,102 gas) compared to the BasicConstructor (67,161 gas). Although the difference is minor, marking a constructor as payable does not necessarily increase deployment costs and in this case, it marginally reduces them.

The difference in gas cost can be attributed to how the EVM handles the deployment bytecode. The payable modifier might influence the constructor's bytecode slightly differently than a non-payable one, potentially due to optimizations in how storage access and function accessibility are handled during deployment.

Payable Admin Functions

Admin functions can be made payable to save gas. Making admin-specific functions payable reduces gas costs because the compiler won't check the callvalue of the function. This approach also makes the contract smaller and cheaper to deploy as there will be fewer opcodes in the creation and runtime code.

Demo Code

Below is an example demonstrating how to implement payable admin functions:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Admin {
address public admin;

constructor() {
admin = msg.sender;
}

modifier onlyAdmin() {
require(msg.sender == admin, "Only admin can call this function");
_;
}

// gas: 23348
function adminFunction() external onlyAdmin {
// Admin specific logic here
}
}

contract AdminPayable {
address public admin;

constructor() {
admin = msg.sender;
}

modifier onlyAdmin() {
require(msg.sender == admin, "Only admin can call this function");
_;
}

// gas: 23324
function adminFunction() external payable onlyAdmin {
// Admin specific logic here
}
}

In this example, the adminFunction is payable, which can help save gas costs.

Recommendations for Gas Optimization

🌟 Using a payable modifier in a constructor will slightly reduce gas costs during contract deployment.

🌟 Consider making admin functions payable to reduce gas costs associated with value checks by the compiler.