# Gas cost of a sha256 hash

I'm confused about the cost of the sha256 function, because my understanding of the cost from the yellow paper ( https://ethereum.github.io/yellowpaper/paper.pdf (Appendix E. Precompiled Contracts) ) doesn't match my experiments of executing the sha256 hash function in Remix.

Here's my understanding of how much sha256 should cost for a 'word' (a 256-bit input):

From the yellow paper:

We define Ξ_{SHA256} as a precompiled contract implementing the SHA2-256 hash function. Its gas usage is dependent on the input data size, a factor rounded up to the nearest number of words.

The gas requirement (g_r) is stated as:

g_r = 60 + ( 12 * ( |I_d| / 32􏰛) )

(where I've edited the notation to look nicer in markdown without latex).

Elsewhere in the paper, it defines I_d as:

I_d, the byte array that is the input data to this execution; if the execution agent is a transaction, this would be the transaction data.

So my interpretation of gas cost for a word of 256-bits (32-bytes) is:

g_r = 60 + ( 12 * 32 / 32 ) = 60 + 12 = 72 gas

However:

I've explored the gas costs of the following simple function in Remix:

function hash() public pure returns (uint256 a) {
a = 1234;
a = uint256(sha256(abi.encodePacked(a)));
}

This has a transaction cost of 22789 gas of which the execution cost is 1517 gas.

Now, some of this will be extraneous storage costs to store a on the stack (and other stuff).

'Commenting out' the hashing line (// a = uint256(sha256(abi.encodePacked(a)));), for a very rough comparison, gives a transaction cost of 21486 gas of which the execution cost is 214 gas.

So a very approx. experimental cost of sha256 appears to be 1517 - 214 = 1303 gas. I'm surprised at how high this cost is (given my understanding that sha256 should be just 72 gas).

Any help would be appreciated in understanding the actual cost of sha256 :)

• Answered here I believe. Sep 21, 2019 at 20:54
• Thanks - this is helpful. I think it's nice to have sha256 addressed separately, as I hadn't thought to search "keccak" for a similar answer (I had "sha256" blinders on). Also, @Ismail has given some extra important information not contained in the other thread. Sep 22, 2019 at 10:50

The Yellow paper only stablishes costs for opcodes of the EVM at a low level. The solidity compiler have to generate extra code to accomodate to the source code written at a high level.

Some of the details the compiler hides from user

• abi.encodePacked converts its parameters to a byte sequence in memory. It has to allocate memory and copy its parameters there.
• sha256 is a precompiled contract. It has to make the call to the precompiled contract, check the result and copy the output. Making a contract call is 700 gas.

Another thing to consider is that the compiler by default generates unoptimized code and it can have many redundancies.

• Thanks! That's interesting; I hadn't appreciated precompiled contracts counted as a proper 'contract call'. So even if there was a low-level assembly implementation to call sha256 (instead of using solidity), there would still be a crude lower bound gas cost of 72 + 700? Sep 22, 2019 at 10:38
• Yes, you will have to pay 700 gas for the call, and some more for copying the input and getting the output if you care about that.
– Ismael
Sep 22, 2019 at 19:33

Here's a cheaper implementation of sha256, using assembly, that I've written:

function assemblyHash() public returns (bytes32[1] memory h) {
bytes32[2] memory inputs;
inputs[0] = "0x1234";
inputs[1] = "0x5678";
bool success;
assembly {
/*
gasLimit: calling with gas equal to not(0), as we have here, will send all available gas to the function being called. This removes the need to guess or upper-bound the amount of gas being sent yourself. As an alternative, we could have guessed the gas needed: with: sub(gas, 2000)
to: the sha256 precompile is at address 0x2: Sending the amount of gas currently available to us, after subtracting 2000;
value: 0 (no ether will be sent to the contract)
inputOffset: I believe this is just the input data
inputSize: hex input size = 0x40 = 2 x 32-bytes
outputOffset: where will the output be stored (in variable h in our case)
outputSize: sha256 outputs 256-bits = 32-bytes = 0x20 in hex
*/
success := call(not(0), 2, 0, inputs, 0x40, h, 0x20)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
}