Skip to content

Instantly share code, notes, and snippets.

@0xA5DF
Last active August 19, 2022 13:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0xA5DF/cc71e507d45fd51d708b3e8318654ce5 to your computer and use it in GitHub Desktop.
Save 0xA5DF/cc71e507d45fd51d708b3e8318654ce5 to your computer and use it in GitHub Desktop.
Forge calculates gas as if it's all one tx (affecting ops that depend on warm/cold keys/addresses)

Forge calculates gas as if each test is one tx

Sample code

When runing forge test -m testGas --gas-report -vv, it shows the cost of calling x is 261:

╭──────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮
│ contracts/Gas.sol:Store contract ┆                 ┆     ┆        ┆     ┆         │
╞══════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡
│ Deployment Cost                  ┆ Deployment Size ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 47315                            ┆ 251             ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                    ┆ min             ┆ avg ┆ median ┆ max ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ x                                ┆ 261             ┆ 261 ┆ 261    ┆ 261 ┆ 2       │
╰──────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯

After commenting out the x assignment at the constructor, the cost of calling x jumps by 2K

╭──────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮
│ contracts/Gas.sol:Store contract ┆                 ┆      ┆        ┆      ┆         │
╞══════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡
│ Deployment Cost                  ┆ Deployment Size ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 25214                            ┆ 249             ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                    ┆ min             ┆ avg  ┆ median ┆ max  ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ x                                ┆ 2261            ┆ 2261 ┆ 2261   ┆ 2261 ┆ 2       │
╰──────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯

(Constructor after commenting out:)

    constructor(uint256 _x){
        // x = _x;
    }

Explanation

The reason for the difference is that forge considers each test as one tx, therefore when the constructor initializes x - that makes it a warm key/var and the cost for next sload would be only 100 gas units. When x is not initialized its key is cold and therefore the cost is the 'full price' of 2.1K.

More info on sload cost can be seen here

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract Store{
uint256 public x;
constructor(uint256 _x){
x = _x;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../contracts/Gas.sol";
contract ContractTest is Test {
function testGas() public {
Store s = new Store(5);
// changing block number and timestamp wouldn't help
vm.warp(block.timestamp + 30);
vm.roll(block.number + 1);
uint256 myx = s.x();
console2.log("x is", myx);
}
}
@0xA5DF
Copy link
Author

0xA5DF commented Aug 19, 2022

Created an issue for this foundry-rs/foundry#2844

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment