arnaucube
arnaucube/poseidon-solidity-examplePublic Self-hosted repository
poseidon-solidity-example/lib/forge-std/README.md
lib/forge-std/README.mdRaw
  1# Forge Standard Library • [![CI status](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml/badge.svg)](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml)
  2
  3Forge Standard Library is a collection of helpful contracts and libraries for use with [Forge and Foundry](https://github.com/foundry-rs/foundry). It leverages Forge's cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes.
  4
  5**Learn how to use Forge-Std with the [📖 Foundry Book (Forge-Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).**
  6
  7## Install
  8
  9```bash
 10forge install foundry-rs/forge-std
 11```
 12
 13## Contracts
 14### stdError
 15
 16This is a helper contract for errors and reverts. In Forge, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler builtin errors.
 17
 18See the contract itself for all error codes.
 19
 20#### Example usage
 21
 22```solidity
 23
 24import "forge-std/Test.sol";
 25
 26contract TestContract is Test {
 27    ErrorsTest test;
 28
 29    function setUp() public {
 30        test = new ErrorsTest();
 31    }
 32
 33    function testExpectArithmetic() public {
 34        vm.expectRevert(stdError.arithmeticError);
 35        test.arithmeticError(10);
 36    }
 37}
 38
 39contract ErrorsTest {
 40    function arithmeticError(uint256 a) public {
 41        uint256 a = a - 100;
 42    }
 43}
 44```
 45
 46### stdStorage
 47
 48This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`).
 49
 50This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth.
 51
 52I.e.:
 53```solidity
 54struct T {
 55    // depth 0
 56    uint256 a;
 57    // depth 1
 58    uint256 b;
 59}
 60```
 61
 62#### Example usage
 63
 64```solidity
 65import "forge-std/Test.sol";
 66
 67contract TestContract is Test {
 68    using stdStorage for StdStorage;
 69
 70    Storage test;
 71
 72    function setUp() public {
 73        test = new Storage();
 74    }
 75
 76    function testFindExists() public {
 77        // Lets say we want to find the slot for the public
 78        // variable `exists`. We just pass in the function selector
 79        // to the `find` command
 80        uint256 slot = stdstore.target(address(test)).sig("exists()").find();
 81        assertEq(slot, 0);
 82    }
 83
 84    function testWriteExists() public {
 85        // Lets say we want to write to the slot for the public
 86        // variable `exists`. We just pass in the function selector
 87        // to the `checked_write` command
 88        stdstore.target(address(test)).sig("exists()").checked_write(100);
 89        assertEq(test.exists(), 100);
 90    }
 91
 92    // It supports arbitrary storage layouts, like assembly based storage locations
 93    function testFindHidden() public {
 94        // `hidden` is a random hash of a bytes, iteration through slots would
 95        // not find it. Our mechanism does
 96        // Also, you can use the selector instead of a string
 97        uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find();
 98        assertEq(slot, uint256(keccak256("my.random.var")));
 99    }
100
101    // If targeting a mapping, you have to pass in the keys necessary to perform the find
102    // i.e.:
103    function testFindMapping() public {
104        uint256 slot = stdstore
105            .target(address(test))
106            .sig(test.map_addr.selector)
107            .with_key(address(this))
108            .find();
109        // in the `Storage` constructor, we wrote that this address' value was 1 in the map
110        // so when we load the slot, we expect it to be 1
111        assertEq(uint(vm.load(address(test), bytes32(slot))), 1);
112    }
113
114    // If the target is a struct, you can specify the field depth:
115    function testFindStruct() public {
116        // NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc.
117        uint256 slot_for_a_field = stdstore
118            .target(address(test))
119            .sig(test.basicStruct.selector)
120            .depth(0)
121            .find();
122
123        uint256 slot_for_b_field = stdstore
124            .target(address(test))
125            .sig(test.basicStruct.selector)
126            .depth(1)
127            .find();
128
129        assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1);
130        assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2);
131    }
132}
133
134// A complex storage contract
135contract Storage {
136    struct UnpackedStruct {
137        uint256 a;
138        uint256 b;
139    }
140
141    constructor() {
142        map_addr[msg.sender] = 1;
143    }
144
145    uint256 public exists = 1;
146    mapping(address => uint256) public map_addr;
147    // mapping(address => Packed) public map_packed;
148    mapping(address => UnpackedStruct) public map_struct;
149    mapping(address => mapping(address => uint256)) public deep_map;
150    mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct;
151    UnpackedStruct public basicStruct = UnpackedStruct({
152        a: 1,
153        b: 2
154    });
155
156    function hidden() public view returns (bytes32 t) {
157        // an extremely hidden storage slot
158        bytes32 slot = keccak256("my.random.var");
159        assembly {
160            t := sload(slot)
161        }
162    }
163}
164```
165
166### stdCheats
167
168This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for address that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you.
169
170
171#### Example usage:
172```solidity
173
174// SPDX-License-Identifier: MIT
175pragma solidity ^0.8.0;
176
177import "forge-std/Test.sol";
178
179// Inherit the stdCheats
180contract StdCheatsTest is Test {
181    Bar test;
182    function setUp() public {
183        test = new Bar();
184    }
185
186    function testHoax() public {
187        // we call `hoax`, which gives the target address
188        // eth and then calls `prank`
189        hoax(address(1337));
190        test.bar{value: 100}(address(1337));
191
192        // overloaded to allow you to specify how much eth to
193        // initialize the address with
194        hoax(address(1337), 1);
195        test.bar{value: 1}(address(1337));
196    }
197
198    function testStartHoax() public {
199        // we call `startHoax`, which gives the target address
200        // eth and then calls `startPrank`
201        //
202        // it is also overloaded so that you can specify an eth amount
203        startHoax(address(1337));
204        test.bar{value: 100}(address(1337));
205        test.bar{value: 100}(address(1337));
206        vm.stopPrank();
207        test.bar(address(this));
208    }
209}
210
211contract Bar {
212    function bar(address expectedSender) public payable {
213        require(msg.sender == expectedSender, "!prank");
214    }
215}
216```
217
218### Std Assertions
219
220Expand upon the assertion functions from the `DSTest` library.
221
222### `console.log`
223
224Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log).
225It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces.
226
227```solidity
228// import it indirectly via Test.sol
229import "forge-std/Test.sol";
230// or directly import it
231import "forge-std/console2.sol";
232...
233console2.log(someValue);
234```
235
236If you need compatibility with Hardhat, you must use the standard `console.sol` instead.
237Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces.
238
239```solidity
240// import it indirectly via Test.sol
241import "forge-std/Test.sol";
242// or directly import it
243import "forge-std/console.sol";
244...
245console.log(someValue);
246```
247
248## License
249
250Forge Standard Library is offered under either [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) license.