uint256 percentage = 30;
function splitAmountToOwnerAndSeller(uint256 amount) internal
view returns (uint256 amountForSender, uint256 amountForOwner)
{ uint256 ownerPercentage = percentage;
amountForSender = (amount * (100 - ownerPercentage)) / 100;
amountForOwner = (amount * ownerPercentage) / 100;
}
(1) Thanks to user CPlusPlusDeveloper from Reddit for pointing out that this is not exactly true. Since the implementation of EIP-2929, the first SLOAD operation costs 2100 gazas, but after the first read, the data is cached and considered "warm", so a second read costs 100 gazas already. Nevertheless, it is still advantageous to load and read such a variable from memory, especially if it is read more than twice.
This is especially important when working with loops that regularly read from state. Always set state variables to memory before entering a loop.
Notes:
This function calculates how much ether should be sent to the owner and seller of the item. The owner receives the percentage given by the percentage variable stored in the storage. If you break down how this works, it is clear that the variable percentage is read from the storage twice. This is not a problem in some programming languages, but if you understand how data is stored on the blockchain, it is clear that reading the amount variable is a memory operation and reading the percentage is a storage operation. These are different instructions at the assembly level.
Let's look at an example:
Solidity is a compiled language in which every operation is converted into a low-level instruction (opcode) that is understood and executed by the Ethereum Virtual Machine (EVM). All operations of your code are executed on every computer on the network, so "gas" is charged for each operation to prevent spam and, more importantly, infinite loops. In Solidity, understanding machine operations and their costs literally saves you money.
So, the code would look like this:
Since you do this twice, you will spend 1600 gas just to read this variable. To avoid this cost, you can first save the value from storage to memory and then read it from there - this is much cheaper (about 3 gaz). That is, you can read from storage once and write to memory (SLOAD + MSTORE) - this is 803 gazas, and then read from memory twice (MLOAD + MLOAD) - another 6 gazas. As a result, the transaction will cost almost half as much - about 809 gaz instead of 1600 gaz.
Every time you read the variable percentage, you get data from the blockchain database (that is, from the network of computers where everyone has to validate that data). This is done with the SLOAD instruction, which, according to the Ethereum Yellow Paper, costs 800 gas to execute.
EVM assembler
If you're familiar with a language like JavaScript, you don't usually think about how variables are stored, except for their scope. But when creating programs for a distributed system like blockchain, you have to think differently.
Blockchain development
uint256 percentage = 30;
function splitAmountToOwnerAndSeller(uint256 amount)
internal
view
returns (uint256 amountForSender, uint256 amountForOwner)
{
amountForSender = (amount * (100 - percentage)) / 100;
amountForOwner = (amount * percentage) / 100;
}