trident-evm-aa 0.3.0

Rust glue for the Trident minimal EIP-7702 smart account: deploy, install, and batch transactions.
Documentation
//! Shared test plumbing: anvil setup, mainnet fixtures, small helpers.
//!
//! [`TestSetup::new`] spawns a Prague-hardfork anvil and pre-injects the
//! runtime bytecodes of Multicall3, WETH9, and Arachnid's CREATE2 proxy at
//! their canonical mainnet addresses. Tests consume what they need and
//! ignore the rest — cheap enough to set up on every test.

use alloy_network::AnyNetwork;
use alloy_node_bindings::{Anvil, AnvilInstance};
use alloy_primitives::{Address, Bytes, address, hex};
use alloy_provider::{Provider, ProviderBuilder, WalletProvider};
use alloy_signer_local::PrivateKeySigner;

// -- Canonical addresses --------------------------------------------------

/// Canonical mainnet WETH9 (same address across most EVM chains in practice).
pub(crate) const WETH9_ADDRESS: Address = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");

/// Canonical Multicall3 address. Mirrors [`crate::call_builder::MULTICALL3`].
pub(crate) const MULTICALL3_ADDRESS: Address = address!("cA11bde05977b3631167028862bE2a173976CA11");

/// Canonical Arachnid deterministic deployment proxy.
/// Mirrors [`crate::deployer::CREATE2_FACTORY`].
pub(crate) const ARACHNID_CREATE2: Address = address!("4e59b44847b379578588920cA78FbF26c0B4956C");

// -- Embedded mainnet runtime bytecodes -----------------------------------

const MULTICALL3_RUNTIME_HEX: &str = include_str!("../testdata/multicall3.hex");
const WETH9_RUNTIME_HEX: &str = include_str!("../testdata/weth9.hex");

// 69-byte Arachnid proxy runtime. Small enough to stay inline.
const ARACHNID_PROXY_RUNTIME: [u8; 69] = hex!(
    "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"
);

// -- Shared test-only bindings --------------------------------------------

alloy_sol_types::sol! {
    #[sol(rpc)]
    interface IWETH9 {
        function deposit() external payable;
        function transfer(address to, uint256 amount) external returns (bool);
        function balanceOf(address owner) external view returns (uint256);
    }
}

// -- Test setup ------------------------------------------------------------

/// Pre-baked test environment: running anvil + default signer + mainnet
/// fixtures (Multicall3, WETH9, Arachnid CREATE2) injected at their
/// canonical addresses.
pub(crate) struct TestSetup {
    pub(crate) anvil: AnvilInstance,
    pub(crate) signer: PrivateKeySigner,
}

impl TestSetup {
    /// Spawn anvil and inject the mainnet fixtures. The returned
    /// [`TestSetup`] keeps the anvil process alive for the test's lifetime.
    pub(crate) async fn new() -> Self {
        let anvil = Anvil::new().prague().spawn();
        let signer: PrivateKeySigner = anvil.keys()[0].clone().into();
        let this = Self { anvil, signer };

        let p = this.provider();
        set_code(
            &p,
            MULTICALL3_ADDRESS,
            decode_hex_str(MULTICALL3_RUNTIME_HEX),
        )
        .await;
        set_code(&p, WETH9_ADDRESS, decode_hex_str(WETH9_RUNTIME_HEX)).await;
        set_code(
            &p,
            ARACHNID_CREATE2,
            Bytes::copy_from_slice(&ARACHNID_PROXY_RUNTIME),
        )
        .await;

        this
    }

    /// Build a fresh provider wired to the anvil's default signer. Uses
    /// `SimpleNonceManager` so the nonce cache never drifts (EIP-7702
    /// self-install bumps the authority's nonce once extra; see
    /// [`crate::eip7702_installer`]'s module docs).
    pub(crate) fn provider(
        &self,
    ) -> impl Provider<AnyNetwork> + WalletProvider<AnyNetwork> + Clone + use<> {
        let wallet = self.anvil.wallet().expect("anvil wallet");
        ProviderBuilder::new()
            .network::<AnyNetwork>()
            .with_simple_nonce_management()
            .wallet(wallet)
            .connect_http(self.anvil.endpoint_url())
    }
}

// -- Helpers ---------------------------------------------------------------

/// `anvil_setCode` over raw JSON-RPC. Useful for injecting additional
/// fixtures beyond the three that [`TestSetup::new`] pre-installs.
pub(crate) async fn set_code<P: Provider<AnyNetwork>>(provider: &P, address: Address, code: Bytes) {
    provider
        .client()
        .request::<_, ()>("anvil_setCode", (address, code))
        .await
        .expect("anvil_setCode");
}

fn decode_hex_str(s: &str) -> Bytes {
    hex::decode(s.trim()).expect("decode hex").into()
}