Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:01:14 +08:00
commit e17e955d35
19 changed files with 6218 additions and 0 deletions

View File

@@ -0,0 +1,567 @@
# forge-std API Reference
Complete reference for the Forge Standard Library.
## Overview
```solidity
import {Test, console} from "forge-std/Test.sol";
import {Script} from "forge-std/Script.sol";
```
`Test` inherits: `StdAssertions`, `StdChains`, `StdCheats`, `StdInvariant`, `StdUtils`
## StdAssertions
### Boolean
```solidity
assertTrue(bool condition);
assertTrue(bool condition, string memory err);
assertFalse(bool condition);
assertFalse(bool condition, string memory err);
```
### Equality
```solidity
// Works with: bool, uint256, int256, address, bytes32, string, bytes
assertEq(T left, T right);
assertEq(T left, T right, string memory err);
assertNotEq(T left, T right);
assertNotEq(T left, T right, string memory err);
// Arrays
assertEq(T[] memory left, T[] memory right);
```
### Comparison
```solidity
// Works with: uint256, int256
assertLt(T left, T right); // <
assertLt(T left, T right, string memory err);
assertGt(T left, T right); // >
assertGt(T left, T right, string memory err);
assertLe(T left, T right); // <=
assertLe(T left, T right, string memory err);
assertGe(T left, T right); // >=
assertGe(T left, T right, string memory err);
```
### Decimal Formatting
```solidity
// Shows values with decimal places in error messages
assertEqDecimal(uint256 left, uint256 right, uint256 decimals);
assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals);
assertLtDecimal(uint256 left, uint256 right, uint256 decimals);
assertGtDecimal(uint256 left, uint256 right, uint256 decimals);
assertLeDecimal(uint256 left, uint256 right, uint256 decimals);
assertGeDecimal(uint256 left, uint256 right, uint256 decimals);
// Example
assertEqDecimal(1e18, 1e18, 18); // Shows "1.0" not "1000000000000000000"
```
### Approximation
```solidity
// Absolute difference
assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta);
assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string memory err);
// Relative difference (maxPercentDelta: 1e18 = 100%)
assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta);
assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string memory err);
// Examples
assertApproxEqAbs(1000, 1005, 10); // Pass: |1000-1005| <= 10
assertApproxEqRel(100, 101, 0.02e18); // Pass: 1% diff <= 2%
```
### Call Comparison
```solidity
assertEqCall(address target, bytes memory callDataA, bytes memory callDataB);
assertEqCall(address targetA, bytes memory callDataA, address targetB, bytes memory callDataB);
```
### Failure
```solidity
fail();
fail(string memory err);
bool hasFailed = failed();
```
## StdCheats
### Address Creation
```solidity
// Create labeled address
address alice = makeAddr("alice");
// Create address with private key
(address bob, uint256 bobKey) = makeAddrAndKey("bob");
// Create account struct
Account memory account = makeAccount("charlie");
// account.addr, account.key
```
### Account Setup
```solidity
// ETH
deal(address to, uint256 amount);
// ERC20
deal(address token, address to, uint256 amount);
deal(address token, address to, uint256 amount, bool adjustTotalSupply);
// ERC721
dealERC721(address token, address to, uint256 tokenId);
// ERC1155
dealERC1155(address token, address to, uint256 id, uint256 amount);
dealERC1155(address token, address to, uint256 id, uint256 amount, bool adjustTotalSupply);
```
### Time Manipulation
```solidity
skip(uint256 seconds); // Move forward
rewind(uint256 seconds); // Move backward
// Examples
skip(1 days);
skip(1 hours);
rewind(30 minutes);
```
### Prank with ETH (hoax)
```solidity
// Single call as sender with ETH
hoax(address sender);
hoax(address sender, uint256 give);
hoax(address sender, address origin);
hoax(address sender, address origin, uint256 give);
// Multiple calls
startHoax(address sender);
startHoax(address sender, uint256 give);
// ... calls ...
vm.stopPrank();
// Example
hoax(alice, 10 ether);
vault.deposit{value: 1 ether}();
```
### Code Deployment
```solidity
// Deploy from artifacts
address deployed = deployCode("ContractName.sol");
address deployed = deployCode("ContractName.sol:ContractName");
address deployed = deployCode("ContractName.sol", constructorArgs);
address deployed = deployCode("ContractName.sol", constructorArgs, value);
// Deploy to specific address
deployCodeTo("ContractName.sol", targetAddress);
deployCodeTo("ContractName.sol", constructorArgs, targetAddress);
```
### Assumptions
```solidity
// Address type checks
assumeNotZeroAddress(address addr);
assumeNotPrecompile(address addr);
assumeNotPrecompile(address addr, uint256 chainId);
assumeNotForgeAddress(address addr);
assumePayable(address addr);
assumeNotPayable(address addr);
// Token blacklists
assumeNotBlacklisted(address token, address addr);
// Combined checks
assumeAddressIsNot(address addr, AddressType t);
assumeAddressIsNot(address addr, AddressType t1, AddressType t2);
// AddressType enum: ZeroAddress, Precompile, ForgeAddress
```
### Fork Detection
```solidity
bool forking = isFork();
// Modifiers
function testOnlyLocal() public skipWhenForking { }
function testOnlyForked() public skipWhenNotForking { }
```
### Gas Metering
```solidity
// Disable gas metering for expensive setup
modifier noGasMetering;
function testExpensiveSetup() public noGasMetering {
// Gas not counted
}
```
### Account Destruction
```solidity
destroyAccount(address target, address beneficiary);
```
## StdStorage
Dynamic storage slot finding and manipulation.
### Setup
```solidity
using stdStorage for StdStorage;
```
### Finding Slots
```solidity
// Simple variable
uint256 slot = stdstore
.target(address(contract))
.sig("variableName()")
.find();
// Mapping
uint256 slot = stdstore
.target(address(contract))
.sig("balances(address)")
.with_key(user)
.find();
// Nested mapping
uint256 slot = stdstore
.target(address(contract))
.sig("allowances(address,address)")
.with_key(owner)
.with_key(spender)
.find();
// Struct field
uint256 slot = stdstore
.target(address(contract))
.sig("structVar()")
.depth(0) // Field index
.find();
```
### Writing Values
```solidity
stdstore
.target(address(contract))
.sig("balances(address)")
.with_key(user)
.checked_write(1000e18);
// For int256
stdstore
.target(address(contract))
.sig("delta()")
.checked_write_int(-100);
```
### Example
```solidity
function testSetBalance() public {
// Set alice's balance to 1000 tokens
stdstore
.target(address(token))
.sig("balanceOf(address)")
.with_key(alice)
.checked_write(1000e18);
assertEq(token.balanceOf(alice), 1000e18);
}
```
## StdUtils
### Bounded Randomness
```solidity
// Constrain fuzz input to range
uint256 bounded = bound(uint256 x, uint256 min, uint256 max);
int256 bounded = bound(int256 x, int256 min, int256 max);
// Constrain to valid private key range
uint256 key = boundPrivateKey(uint256 pk);
// Example
function testFuzz(uint256 amount) public {
amount = bound(amount, 1, 1000);
// amount is now in [1, 1000]
}
```
### Address Computation
```solidity
// CREATE address
address addr = computeCreateAddress(address deployer, uint256 nonce);
// CREATE2 address
address addr = computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer);
address addr = computeCreate2Address(bytes32 salt, bytes32 initCodeHash); // Uses CREATE2_FACTORY
// Init code hash
bytes32 hash = hashInitCode(bytes memory creationCode);
bytes32 hash = hashInitCode(bytes memory creationCode, bytes memory args);
// Example
bytes32 initHash = hashInitCode(type(MyContract).creationCode);
address predicted = computeCreate2Address(salt, initHash, factory);
```
### Token Utilities
```solidity
// Batch balance query (uses Multicall3)
uint256[] memory balances = getTokenBalances(address token, address[] memory addresses);
```
### Byte Conversion
```solidity
uint256 value = bytesToUint(bytes memory b);
```
## StdJson
### Reading
```solidity
using stdJson for string;
string memory json = vm.readFile("data.json");
// Single values
uint256 amount = json.readUint(".amount");
int256 balance = json.readInt(".balance");
address addr = json.readAddress(".recipient");
bytes32 hash = json.readBytes32(".hash");
string memory name = json.readString(".name");
bytes memory data = json.readBytes(".data");
bool flag = json.readBool(".enabled");
// Arrays
uint256[] memory amounts = json.readUintArray(".amounts");
address[] memory addrs = json.readAddressArray(".addresses");
string[] memory names = json.readStringArray(".names");
// With defaults
uint256 amount = json.readUintOr(".amount", 100);
address addr = json.readAddressOr(".recipient", address(0));
// Check existence
bool exists = json.keyExists(".key");
// Raw bytes
bytes memory raw = json.parseRaw(".data");
```
### Writing
```solidity
using stdJson for string;
string memory json = "obj";
json = json.serialize("amount", uint256(100));
json = json.serialize("recipient", address(0x123));
json = json.serialize("enabled", true);
json = json.serialize("amounts", amounts);
json.write("output.json");
json.write("output.json", ".config");
```
## StdToml
Identical API to StdJson:
```solidity
using stdToml for string;
string memory toml = vm.readFile("config.toml");
uint256 runs = toml.readUint(".profile.default.fuzz_runs");
```
## StdChains
Access chain configuration:
```solidity
Chain memory chain = getChain("mainnet");
// chain.name, chain.chainId, chain.rpcUrl
Chain memory chain = getChain(1); // By chain ID
// Set custom RPC
setChain("custom", ChainData({
name: "Custom Chain",
chainId: 12345,
rpcUrl: "https://..."
}));
```
## StdInvariant
For invariant testing targets:
```solidity
// Target contracts for fuzzing
targetContract(address);
targetContracts(); // Returns address[]
// Exclude from fuzzing
excludeContract(address);
excludeContracts(); // Returns address[]
// Target senders
targetSender(address);
targetSenders(); // Returns address[]
// Exclude senders
excludeSender(address);
excludeSenders(); // Returns address[]
// Target specific selectors
targetSelector(FuzzSelector memory);
targetSelectors(); // Returns FuzzSelector[]
// Target artifacts (deploy and fuzz)
targetArtifact(string memory);
targetArtifacts(); // Returns string[]
// Target artifact selectors
targetArtifactSelector(FuzzArtifactSelector memory);
targetArtifactSelectors(); // Returns FuzzArtifactSelector[]
```
## StdError
Common error selectors:
```solidity
import {stdError} from "forge-std/StdError.sol";
vm.expectRevert(stdError.arithmeticError); // Overflow/underflow
vm.expectRevert(stdError.assertionError); // assert() failed
vm.expectRevert(stdError.divisionError); // Division by zero
vm.expectRevert(stdError.encodeStorageError); // Storage encoding
vm.expectRevert(stdError.enumConversionError); // Invalid enum
vm.expectRevert(stdError.indexOOBError); // Array index out of bounds
vm.expectRevert(stdError.memOverflowError); // Memory overflow
vm.expectRevert(stdError.popEmptyArrayError); // Pop empty array
vm.expectRevert(stdError.zeroVarError); // Zero-initialized function pointer
```
## StdMath
```solidity
import {stdMath} from "forge-std/StdMath.sol";
uint256 absolute = stdMath.abs(int256 x);
uint256 delta = stdMath.delta(uint256 a, uint256 b);
uint256 delta = stdMath.delta(int256 a, int256 b);
uint256 percent = stdMath.percentDelta(uint256 a, uint256 b);
```
## Console Logging
```solidity
import {console} from "forge-std/console.sol";
// or
import {console2} from "forge-std/console2.sol"; // Smaller bytecode
console.log("message");
console.log("value:", value);
console.log("a:", a, "b:", b);
// Type-specific
console.log(uint256 x);
console.log(int256 x);
console.log(address x);
console.log(bool x);
console.log(string memory x);
console.log(bytes memory x);
console.log(bytes32 x);
// Formatted
console.logBytes(bytes memory);
console.logBytes1(bytes1);
// ... up to logBytes32
```
## Script.sol
Base for deployment scripts:
```solidity
import {Script, console} from "forge-std/Script.sol";
contract DeployScript is Script {
function setUp() public {}
function run() public {
uint256 deployerKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerKey);
MyContract c = new MyContract();
c.initialize();
vm.stopBroadcast();
console.log("Deployed:", address(c));
}
}
```
### Script vs Test
| Feature | Test | Script |
|---------|------|--------|
| Base | `Test` | `Script` |
| Cheats | Full `StdCheats` | `StdCheatsSafe` |
| Purpose | Testing | Deployment |
| Broadcast | No | Yes |
| State changes | Local | On-chain |
## Constants
```solidity
// From CommonBase
address constant VM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
address constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
address constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
address constant DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38;
address constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
address constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
```