SDK Quickstart
Two steps, two examples. Install the EIP-7702 delegation on your EOA once, then batch three actions from the delegated EOA — wrap 0.5 ETH into WETH, transfer 0.1 WETH to bob, send 0.3 ETH to carol. If any sub-call reverts, the whole batch reverts.
Prerequisites
Section titled “Prerequisites”- Rust 1.86+ and Cargo.
- A test-only private key (hex). Do not use production keys.
- An RPC endpoint on a chain that supports EIP-7702 (the Prague hardfork): Ethereum mainnet, Sepolia, Holesky, or a local
anvil --hardfork prague. - The chain must have Arachnid’s canonical CREATE2 proxy at
0x4e59b44847b379578588920cA78FbF26c0B4956C— needed to placeTridentAccountat its canonical address0x319A60309bA63b73624729Cc903CD6FeE78af696. Every major EVM network has it by default. - Native ETH on the EOA for gas, plus whatever value your batch carries.
Cargo.tomldependencies:
[dependencies]trident-evm-aa = "0.3"alloy-network = "1.8"alloy-primitives = "1.5"alloy-provider = { version = "1.8", features = ["reqwest"] }alloy-signer-local = "1.8"alloy-sol-types = "1.5"tokio = { version = "1", features = ["macros", "rt-multi-thread"] }Step 1 — Install the delegation
Section titled “Step 1 — Install the delegation”A one-time transaction per EOA per chain. After this confirms, your EOA is a TridentAccount and every subsequent execute(target, data) call from it works.
use alloy_network::{AnyNetwork, ReceiptResponse};use alloy_provider::ProviderBuilder;use alloy_signer_local::PrivateKeySigner;use trident_evm_aa::eip7702_installer::SelfInstaller;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let rpc_url = std::env::var("RPC_URL")?; let signer: PrivateKeySigner = std::env::var("PRIVATE_KEY")?.parse()?;
let provider = ProviderBuilder::new() .network::<AnyNetwork>() .with_simple_nonce_management() // required: 7702 self-install double-bumps the nonce .wallet(signer.clone()) .connect_http(rpc_url.parse()?);
let tx_hash = SelfInstaller::new() .install(provider, &signer) .await? .get_receipt() .await? .transaction_hash(); println!("install tx: {tx_hash}");
Ok(())}export RPC_URL=https://...export PRIVATE_KEY=0x...cargo run --example installWhy with_simple_nonce_management here
Section titled “Why with_simple_nonce_management here”A 7702 self-install bumps the authority’s nonce twice — once for the outer transaction, once for the applied authorization — but alloy’s default CachedNonceManager only tracks the outer bump. If you reuse this provider for another transaction without SimpleNonceManager, that next tx fails with “nonce too low”. It is only required on providers that send the install tx; the batch provider below does not need it.
What SelfInstaller does
Section titled “What SelfInstaller does”- Defaults to the canonical
TridentAccountaddress. Override with.with_target(...)— see Recipes. - Signs the EIP-7702 authorization with
signer, sends the outer transaction (payer = authority = your EOA), returns aPendingTransactionBuilder.get_receipt()confirms inclusion. - If
TridentAccountisn’t deployed at the canonical address on this chain, auto-deploys it via Arachnid’s CREATE2 proxy in the same flow. Opt out with.without_deploy_if_missing(). - Requires
signer.address() == provider.default_signer_address(). A mismatch fails fast withInstallError::AuthorityMismatch { authority, provider_signer }before any tx is built — EIP-7702 nonce arithmetic depends on both keys being the same.
Step 2 — Batch three actions
Section titled “Step 2 — Batch three actions”Install only happens once. All subsequent work is plain transactions from a normally-configured provider — no SimpleNonceManager, no special setup.
use alloy_network::{AnyNetwork, ReceiptResponse};use alloy_primitives::{Address, U256, address};use alloy_provider::ProviderBuilder;use alloy_signer_local::PrivateKeySigner;use alloy_sol_types::sol;use trident_evm_aa::call_builder::MulticallBuilder;
sol! { interface IWETH9 { function deposit() external payable; function transfer(address to, uint256 amount) external returns (bool); }}
const WETH9: Address = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");const BOB: Address = address!("00000000000000000000000000000000000000bb");const CAROL: Address = address!("00000000000000000000000000000000000000cc");
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let rpc_url = std::env::var("RPC_URL")?; let signer: PrivateKeySigner = std::env::var("PRIVATE_KEY")?.parse()?;
let provider = ProviderBuilder::new() .network::<AnyNetwork>() .wallet(signer) .connect_http(rpc_url.parse()?);
let receipt = MulticallBuilder::new() .call_with_value( WETH9, &IWETH9::depositCall {}, U256::from(500_000_000_000_000_000u128), ) .call( WETH9, &IWETH9::transferCall { to: BOB, amount: U256::from(100_000_000_000_000_000u128), }, ) .send_value(CAROL, U256::from(300_000_000_000_000_000u128)) .send_with_wallet_provider(provider) .await? .get_receipt() .await?; println!("batch tx: {}", receipt.transaction_hash());
Ok(())}cargo run --example batchWhat MulticallBuilder does
Section titled “What MulticallBuilder does”Three ways to append a sub-call:
call_with_value(target, sol_call, value)— contract call that forwardsvalueETH. Thesol!macro gives you the ABI-encoded call struct.call(target, sol_call)— contract call withvalue = 0.send_value(to, value)— bare ETH transfer (empty calldata).
send then builds one transaction:
to= your own EOA (the delegated account).input= ABI-encodedTridentAccount.execute(Multicall3, aggregate3Value(calls)).msg.value= sum of per-call values (here 0.5 + 0 + 0.3 = 0.8 ETH).
Multicall3’s aggregate3Value enforces msg.value == Σ call.value, so MulticallBuilder sets the outer msg.value to match. Sender and receiver are the same address, so the ETH is a net-zero movement — but the EOA must still hold at least that much ETH to pass the EVM’s pre-execution balance check. It’s an accounting artifact of Multicall3’s invariant, not extra cost.
Running install + batch on one provider
Section titled “Running install + batch on one provider”If you wire both steps into one binary that shares a single provider (e.g. a test script or a fresh-EOA onboarding flow), that provider does need with_simple_nonce_management() — the install’s double-bump will otherwise break the very next tx. In any other shape — install today, batch tomorrow; install on provider A, batch on provider B — a plain provider is fine for the batch side.
For overrides — custom target, custom Multicall3 address, manual deploy on a new chain — see Recipes.