signet-test-utils 0.16.2

Common utilities for testing Signet components
Documentation
use alloy::{
    primitives::{bytes, Address, Bytes, Keccak256, U256},
    uint,
};
use trevm::revm::{
    state::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot},
    Database, DatabaseCommit,
};

alloy::sol! {
    /// ERC20 token interface for testing.
    #[sol(rpc)]
    #[derive(Debug)]
    interface ERC20 {
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() external view returns (uint8);
        function totalSupply() external view returns (uint256);
        function balanceOf(address account) external view returns (uint256);
        function transfer(address to, uint256 amount) external returns (bool);
        function allowance(address owner, address spender) external view returns (uint256);
        function approve(address spender, uint256 amount) external returns (bool);
        function transferFrom(address from, address to, uint256 amount) external returns (bool);

        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
}

/// Slot at which the balances mapping is stored.
pub const BALANCES_SLOT: U256 = uint!(0_U256);
/// Slot at which the allowances mapping is stored.
pub const ALLOWANCES_SLOT: U256 = uint!(1_U256);
/// Slot at which the total supply is stored.
pub const TOTAL_SUPPLY_SLOT: U256 = uint!(2_U256);
/// Slot at which the token name is stored.
pub const NAME_SLOT: U256 = uint!(3_U256);
/// Slot at which the token symbol is stored.
pub const SYMBOL_SLOT: U256 = uint!(4_U256);
/// Slot at which the token minter is stored.
pub const MINTER_SLOT: U256 = uint!(5_U256);

/// Address of the token minter.
pub const MINTER: U256 =
    uint!(0x00000000000000000000000000000000000000000000746f6b656e61646d696e_U256);
/// WBTC Contract name.
pub const WBTC_NAME: U256 =
    uint!(0x5772617070656420425443000000000000000000000000000000000000000016_U256);
/// WBTC Contract symbol.
pub const WBTC_SYMBOL: U256 =
    uint!(0x5742544300000000000000000000000000000000000000000000000000000008_U256);
/// WETH Contract name.
pub const WETH_NAME: U256 =
    uint!(0x577261707065642045746865720000000000000000000000000000000000001a_U256);
/// WETH Contract symbol.
pub const WETH_SYMBOL: U256 =
    uint!(0x5745544800000000000000000000000000000000000000000000000000000008_U256);

/// Token Contract bytecode, from pecorino genesis.
pub const TOKEN_BYTECODE: Bytes = bytes!("0x608060405234801561000f575f80fd5b50600436106100fb575f3560e01c806370a082311161009357806395d89b411161006357806395d89b4114610246578063a9059cbb1461024e578063dd62ed3e14610261578063f2fde38b14610299575f80fd5b806370a08231146101e8578063715018a61461021057806379cc6790146102185780638da5cb5b1461022b575f80fd5b8063313ce567116100ce578063313ce5671461016557806332424aa31461019957806340c10f19146101c057806342966c68146101d3575f80fd5b806306fdde03146100ff578063095ea7b31461011d57806318160ddd1461014057806323b872dd14610152575b5f80fd5b6101076102ac565b60405161011491906107f0565b60405180910390f35b61013061012b366004610840565b61033c565b6040519015158152602001610114565b6002545b604051908152602001610114565b610130610160366004610868565b610355565b7f00000000000000000000000000000000000000000000000000000000000000085b60405160ff9091168152602001610114565b6101877f000000000000000000000000000000000000000000000000000000000000000881565b6101306101ce366004610840565b610378565b6101e66101e13660046108a2565b610394565b005b6101446101f63660046108b9565b6001600160a01b03165f9081526020819052604090205490565b6101e66103a1565b6101e6610226366004610840565b6103b4565b6005546040516001600160a01b039091168152602001610114565b6101076103cd565b61013061025c366004610840565b6103dc565b61014461026f3660046108d9565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101e66102a73660046108b9565b6103e9565b6060600380546102bb9061090a565b80601f01602080910402602001604051908101604052809291908181526020018280546102e79061090a565b80156103325780601f1061030957610100808354040283529160200191610332565b820191905f5260205f20905b81548152906001019060200180831161031557829003601f168201915b5050505050905090565b5f33610349818585610428565b60019150505b92915050565b5f3361036285828561043a565b61036d8585856104b5565b506001949350505050565b5f610381610512565b61038b838361053f565b50600192915050565b61039e3382610573565b50565b6103a9610512565b6103b25f6105a7565b565b6103bf82338361043a565b6103c98282610573565b5050565b6060600480546102bb9061090a565b5f336103498185856104b5565b6103f1610512565b6001600160a01b03811661041f57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b61039e816105a7565b61043583838360016105f8565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f1981146104af57818110156104a157604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610416565b6104af84848484035f6105f8565b50505050565b6001600160a01b0383166104de57604051634b637e8f60e11b81525f6004820152602401610416565b6001600160a01b0382166105075760405163ec442f0560e01b81525f6004820152602401610416565b6104358383836106ca565b6005546001600160a01b031633146103b25760405163118cdaa760e01b8152336004820152602401610416565b6001600160a01b0382166105685760405163ec442f0560e01b81525f6004820152602401610416565b6103c95f83836106ca565b6001600160a01b03821661059c57604051634b637e8f60e11b81525f6004820152602401610416565b6103c9825f836106ca565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0384166106215760405163e602df0560e01b81525f6004820152602401610416565b6001600160a01b03831661064a57604051634a1406b160e11b81525f6004820152602401610416565b6001600160a01b038085165f90815260016020908152604080832093871683529290522082905580156104af57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516106bc91815260200190565b60405180910390a350505050565b6001600160a01b0383166106f4578060025f8282546106e99190610942565b909155506107649050565b6001600160a01b0383165f90815260208190526040902054818110156107465760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610416565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b0382166107805760028054829003905561079e565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107e391815260200190565b60405180910390a3505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b038116811461083b575f80fd5b919050565b5f8060408385031215610851575f80fd5b61085a83610825565b946020939093013593505050565b5f805f6060848603121561087a575f80fd5b61088384610825565b925061089160208501610825565b929592945050506040919091013590565b5f602082840312156108b2575f80fd5b5035919050565b5f602082840312156108c9575f80fd5b6108d282610825565b9392505050565b5f80604083850312156108ea575f80fd5b6108f383610825565b915061090160208401610825565b90509250929050565b600181811c9082168061091e57607f821691505b60208210810361093c57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561034f57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220a373554eb2e797644d86447291ab79350d58226b5edc56a67e2d820f602062bb64736f6c634300081a0033");

/// Deploys a token contract at the specified address in the given database.
pub fn deploy_token_at<Db: Database + DatabaseCommit>(
    db: &mut Db,
    addr: Address,
    name: U256,
    symbol: U256,
) -> Result<(), Db::Error> {
    let info: AccountInfo =
        db.basic(addr)?.unwrap_or_default().with_code(Bytecode::new_legacy(TOKEN_BYTECODE));

    let acct = Account { info, transaction_id: 0, ..Default::default() }
        .with_storage(
            [
                (NAME_SLOT, EvmStorageSlot::new(name, 0)),
                (SYMBOL_SLOT, EvmStorageSlot::new(symbol, 0)),
                (MINTER_SLOT, EvmStorageSlot::new(MINTER, 0)),
            ]
            .into_iter(),
        )
        .with_touched_mark();

    let changes: EvmState = [(addr, acct)].into_iter().collect();
    db.commit(changes);

    Ok(())
}

/// Deploys a WETH token contract at the specified address in the given database.
pub fn deploy_weth_at<Db: Database + DatabaseCommit>(
    db: &mut Db,
    addr: Address,
) -> Result<(), Db::Error> {
    deploy_token_at(db, addr, WETH_NAME, WETH_SYMBOL)
}

/// Deploys a WBTC token contract at the specified address in the given database.
pub fn deploy_wbtc_at<Db: Database + DatabaseCommit>(
    db: &mut Db,
    addr: Address,
) -> Result<(), Db::Error> {
    deploy_token_at(db, addr, WBTC_NAME, WBTC_SYMBOL)
}

fn mapping_slot(base: U256, key: Address) -> U256 {
    let mut hasher = Keccak256::new();
    hasher.update([0u8; 12]);
    hasher.update(key);
    hasher.update(base.to_be_bytes::<32>());
    U256::from_be_bytes::<32>(hasher.finalize().into())
}

/// Computes the storage slot for the balance of the given owner.
pub fn balance_slot_for(owner: Address) -> U256 {
    mapping_slot(BALANCES_SLOT, owner)
}

/// Computes the storage slot for the allowance mapping for the given owner and spender.
pub fn allowances_slot_for(owner: Address, spender: Address) -> U256 {
    let inner_map_loc = mapping_slot(ALLOWANCES_SLOT, owner);
    mapping_slot(inner_map_loc, spender)
}