Skip to content

Commit 2e37c3e

Browse files
authored
feat: document gas snapshot cheatcodes (#1323)
* add gas snapshots, update state snapshots * embed cheatcodes * link to cheatcodes * update description
1 parent 84b34cb commit 2e37c3e

File tree

7 files changed

+277
-71
lines changed

7 files changed

+277
-71
lines changed

src/SUMMARY.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
- [Deploying and Verifying](./forge/deploying.md)
4040
- [Gas Tracking](./forge/gas-tracking.md)
4141
- [Gas Reports](./forge/gas-reports.md)
42-
- [Gas Snapshots](./forge/gas-snapshots.md)
42+
- [Gas Function Snapshots](./forge/gas-function-snapshots.md)
43+
- [Gas Section Snapshots](./forge/gas-section-snapshots.md)
4344
- [Debugger](./forge/debugger.md)
4445

4546
# Cast Overview
@@ -429,6 +430,8 @@
429430
- [`txGasPrice`](./cheatcodes/tx-gas-price.md)
430431
- [`startStateDiffRecording`](./cheatcodes/start-state-diff-recording.md)
431432
- [`stopAndReturnStateDiff`](./cheatcodes/stop-and-return-state-diff.md)
433+
- [`snapshotState`](./cheatcodes/state-snapshots.md)
434+
- [`snapshotGas`](./cheatcodes/gas-snapshots.md)
432435
- [Assertions](./cheatcodes/assertions.md)
433436
- [`expectRevert`](./cheatcodes/expect-revert.md)
434437
- [`expectEmit`](./cheatcodes/expect-emit.md)
@@ -493,7 +496,6 @@
493496
- [`createWallet`](./cheatcodes/create-wallet.md)
494497
- [`copyStorage`](./cheatcodes/copy-storage.md)
495498
- [`setArbitraryStorage`](./cheatcodes/set-arbitrary-storage.md)
496-
- [Snapshots](./cheatcodes/snapshots.md)
497499
- [RPC](./cheatcodes/rpc.md)
498500
- [Files](./cheatcodes/fs.md)
499501
- [Forge Standard Library Reference](./reference/forge-std/README.md)

src/cheatcodes/gas-snapshots.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
## `snapshotGas` cheatcodes
2+
3+
### Signature
4+
5+
```solidity
6+
/// Start a snapshot capture of the current gas usage by name.
7+
/// The group name is derived from the contract name.
8+
function startSnapshotGas(string calldata name) external;
9+
10+
/// Start a snapshot capture of the current gas usage by name in a group.
11+
function startSnapshotGas(string calldata group, string calldata name) external;
12+
13+
/// Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start.
14+
function stopSnapshotGas() external returns (uint256 gasUsed);
15+
16+
/// Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start.
17+
/// The group name is derived from the contract name.
18+
function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);
19+
20+
/// Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start.
21+
function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);
22+
23+
/// Snapshot capture an arbitrary numerical value by name.
24+
/// The group name is derived from the contract name.
25+
function snapshotValue(string calldata name, uint256 value) external;
26+
27+
/// Snapshot capture an arbitrary numerical value by name in a group.
28+
function snapshotValue(string calldata group, string calldata name, uint256 value) external;
29+
30+
/// Snapshot capture the gas usage of the last call by name from the callee perspective.
31+
function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);
32+
33+
/// Snapshot capture the gas usage of the last call by name in a group from the callee perspective.
34+
function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);
35+
```
36+
37+
### Description
38+
39+
`snapshotGas*` cheatcodes allow you to capture gas usage in your tests. This can be useful to track how much gas your logic is consuming. You can capture the gas usage of the last call by name, capture an arbitrary numerical value by name, or start and stop a snapshot capture of the current gas usage by name.
40+
41+
In order to strictly compare gas usage across test runs, set the `FORGE_SNAPSHOT_CHECK` environment variable to `true` before running your tests. This will compare the gas usage of your tests against the last snapshot and fail if the gas usage has changed. By default the snapshots directory will be newly created and its contents removed before each test run to ensure no stale data is present.
42+
43+
It is intended that the `snapshots` directory created when using the `snapshotGas*` cheatcodes is checked into version control. This allows you to track changes in gas usage over time and compare gas usage during code reviews.
44+
45+
When running `forge clean` the `snapshots` directory will be deleted.
46+
47+
### Examples
48+
49+
Capturing the gas usage of a section of code that calls an external contract:
50+
51+
```solidity
52+
contract SnapshotGasTest is Test {
53+
uint256 public slot0;
54+
55+
Flare public flare;
56+
57+
function setUp() public {
58+
flare = new Flare();
59+
}
60+
61+
function testSnapshotGas() public {
62+
vm.startSnapshotGas("externalA");
63+
flare.run(256);
64+
uint256 gasUsed = vm.stopSnapshotGas();
65+
}
66+
}
67+
```
68+
69+
Capturing the gas usage of multiple sections of code that modify the internal state:
70+
71+
72+
```solidity
73+
contract SnapshotGasTest is Test {
74+
uint256 public slot0;
75+
76+
77+
/// Writes to `snapshots/SnapshotGasTest.json` group with name `internalA`, `internalB`, and `internalC`.
78+
function testSnapshotGas() public {
79+
vm.startSnapshotGas("internalA");
80+
slot0 = 1;
81+
vm.stopSnapshotGas();
82+
83+
vm.startSnapshotGas("internalB");
84+
slot0 = 2;
85+
vm.stopSnapshotGas();
86+
87+
vm.startSnapshotGas("internalC");
88+
slot0 = 0;
89+
vm.stopSnapshotGas();
90+
}
91+
}
92+
```
93+
94+
Capturing the gas usage of a section of code that modifies both the internal state and calls an external contract:
95+
96+
```solidity
97+
contract SnapshotGasTest is Test {
98+
uint256 public slot0;
99+
Flare public flare;
100+
101+
function setUp() public {
102+
flare = new Flare();
103+
}
104+
105+
/// Writes to `snapshots/SnapshotGasTest.json` group with name `combinedA`.
106+
function testSnapshotGas() public {
107+
vm.startSnapshotGas("combinedA");
108+
flare.run(256);
109+
slot0 = 1;
110+
vm.stopSnapshotGas();
111+
}
112+
}
113+
```
114+
115+
```solidity
116+
117+
Capturing an arbitrary numerical value (such as the bytecode size of a contract):
118+
119+
```solidity
120+
contract SnapshotGasTest is Test {
121+
uint256 public slot0;
122+
123+
/// Writes to `snapshots/SnapshotGasTest.json` group with name `valueA`, `valueB`, and `valueC`.
124+
function testSnapshotValue() public {
125+
uint256 a = 123;
126+
uint256 b = 456;
127+
uint256 c = 789;
128+
129+
vm.snapshotValue("valueA", a);
130+
vm.snapshotValue("valueB", b);
131+
vm.snapshotValue("valueC", c);
132+
}
133+
}
134+
```
135+
136+
Capturing the gas usage of the last call from the callee perspective:
137+
138+
```solidity
139+
contract SnapshotGasTest is Test {
140+
Flare public flare;
141+
142+
function setUp() public {
143+
flare = new Flare();
144+
}
145+
146+
/// Writes to `snapshots/SnapshotGasTest.json` group with name `lastCallA`.
147+
function testSnapshotGasLastCall() public {
148+
flare.run(1);
149+
vm.snapshotGasLastCall("lastCallA");
150+
}
151+
}
152+
```
153+
154+
For each of the above examples you can also use the `group` variant of the cheatcodes to group the snapshots together in a custom group.
155+
156+
```solidity
157+
contract SnapshotGasTest is Test {
158+
uint256 public slot0;
159+
160+
/// Writes to `snapshots/CustomGroup.json` group with name `internalA`, `internalB`, and `internalC`.
161+
function testSnapshotGas() public {
162+
vm.startSnapshotGas("CustomGroup", "internalA");
163+
slot0 = 1;
164+
vm.stopSnapshotGas();
165+
166+
vm.startSnapshotGas("CustomGroup", "internalB");
167+
slot0 = 2;
168+
vm.stopSnapshotGas();
169+
170+
vm.startSnapshotGas("CustomGroup", "internalC");
171+
slot0 = 0;
172+
vm.stopSnapshotGas();
173+
}
174+
}
175+
```

src/cheatcodes/snapshots.md

Lines changed: 0 additions & 63 deletions
This file was deleted.

src/cheatcodes/state-snapshots.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
## `snapshotState` cheatcodes
2+
3+
### Signature
4+
5+
```solidity
6+
/// Snapshot the current state of the evm.
7+
/// Returns the ID of the snapshot that was created.
8+
/// To revert a snapshot use `revertToState`.
9+
function snapshotState() external returns (uint256 snapshotId);
10+
11+
/// Revert the state of the EVM to a previous snapshot
12+
/// Takes the snapshot ID to revert to.
13+
/// Returns `true` if the snapshot was successfully reverted.
14+
/// Returns `false` if the snapshot does not exist.
15+
/// **Note:** This does not automatically delete the snapshot. To delete the snapshot use `deleteStateSnapshot`.
16+
function revertToState(uint256 snapshotId) external returns (bool success);
17+
18+
/// Revert the state of the EVM to a previous snapshot and automatically deletes the snapshots
19+
/// Takes the snapshot ID to revert to.
20+
/// Returns `true` if the snapshot was successfully reverted and deleted.
21+
/// Returns `false` if the snapshot does not exist.
22+
function revertToStateAndDelete(uint256 snapshotId) external returns (bool success);
23+
24+
/// Removes the snapshot with the given ID created by `snapshot`.
25+
/// Takes the snapshot ID to delete.
26+
/// Returns `true` if the snapshot was successfully deleted.
27+
/// Returns `false` if the snapshot does not exist.
28+
function deleteStateSnapshot(uint256 snapshotId) external returns (bool success);
29+
30+
/// Removes _all_ snapshots previously created by `snapshot`.
31+
function deleteStateSnapshots() external;
32+
```
33+
34+
### Description
35+
36+
`snapshotState` takes a snapshot of the state of the blockchain and returns the identifier of the created snapshot.
37+
38+
`revertToState` reverts the state of the blockchain to the given state snapshot. This deletes the given snapshot, as well as any snapshots taken after (e.g.: reverting to id 2 will delete snapshots with ids 2, 3, 4, etc.).
39+
40+
### Examples
41+
42+
```solidity
43+
struct Storage {
44+
uint slot0;
45+
uint slot1;
46+
}
47+
48+
contract SnapshotStateTest is Test {
49+
Storage store;
50+
uint256 timestamp;
51+
52+
function setUp() public {
53+
store.slot0 = 10;
54+
store.slot1 = 20;
55+
vm.deal(address(this), 5 ether); // balance = 5 ether
56+
timestamp = block.timestamp;
57+
}
58+
59+
function testSnapshotState() public {
60+
uint256 snapshot = vm.snapshotState(); // saves the state
61+
62+
// let's change the state
63+
store.slot0 = 300;
64+
store.slot1 = 400;
65+
vm.deal(address(this), 500 ether);
66+
vm.warp(12345); // block.timestamp = 12345
67+
68+
assertEq(store.slot0, 300);
69+
assertEq(store.slot1, 400);
70+
assertEq(address(this).balance, 500 ether);
71+
assertEq(block.timestamp, 12345);
72+
73+
vm.revertToState(snapshot); // restores the state
74+
75+
assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful");
76+
assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful");
77+
assertEq(address(this).balance, 5 ether, "snapshot revert for balance unsuccessful");
78+
assertEq(block.timestamp, timestamp, "snapshot revert for timestamp unsuccessful");
79+
}
80+
}
81+
```

src/forge/gas-snapshots.md renamed to src/forge/gas-function-snapshots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Gas Snapshots
1+
## Gas Function Snapshots
22

33
Forge can generate gas snapshots for all your test functions. This can
44
be useful to get a general feel for how much gas your contract will consume,

src/forge/gas-section-snapshots.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Gas Section Snapshots
2+
3+
Forge can capture gas snapshots over arbitrary sections inside of your test functions. This can be useful to get a granular measurement of how much gas your logic is consuming as both external calls and internal gas usage are measured.
4+
5+
Instead of running a command like `forge snapshot` or `forge test --gas-report`, you use the `snapshotGas` [cheatcodes](./cheatcodes.md) in your tests to capture gas usage as follows:
6+
7+
{{#include ../cheatcodes/gas-snapshots.md}}

src/forge/gas-tracking.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
Forge can help you estimate how much gas your contract will consume.
44

5-
Currently, Forge ships with two different tools for this job, but they may be merged in the future:
5+
Currently, Forge ships with three different tools for this job:
66

77
- [**Gas reports**](./gas-reports.md): Gas reports give you an overview of how much Forge thinks the
88
individual functions in your contracts will consume in gas.
9-
- [**Gas snapshots**](./gas-snapshots.md): Gas snapshots give you an overview of how much
10-
each test consumes in gas.
9+
- [**Gas function snapshots**](./gas-function-snapshots.md): Gas function snapshots give you an overview of how much
10+
each test function consumes in gas.
11+
- [**Gas section snapshots**](./gas-section-snapshots.md): Gas section snapshots give you the ability to capture gas usage over arbitrary sections inside of test functions.
12+
This also tracks internal gas usage. You can access this by using the `snapshotGas*` cheatcodes inside your tests.
1113

12-
Gas reports and gas snapshots differ in some ways:
14+
Gas reports, gas function snapshots and gas section snapshots differ in some ways:
1315

1416
- Gas reports use tracing to figure out gas costs for individual contract calls.
1517
This gives more granular insight, at the cost of speed.
16-
- Gas snapshots have more built-in tools, such as diffs and exporting the results to a file.
18+
- Gas function snapshots have more built-in tools, such as diffs and exporting the results to a file.
1719
Snapshots are not as granular as gas reports, but they are faster to generate.
20+
- Gas section snapshots provides the most granular way to capture gas usage. Every captured gas snapshot is written to a file in a `snapshots` directory.
21+
By default these snapshots are grouped by the contract name of the test.

0 commit comments

Comments
 (0)