Skip to main content

Overview

Fishnet uses a permit-based smart contract system to enable secure, gasless on-chain actions for AI agents. The core contract, FishnetWallet, implements EIP-712 typed structured data signing to authorize transactions without requiring the user to hold ETH for gas.

Architecture

The FishnetWallet system consists of three key components:

Components

FishnetWallet Contract

On-chain smart contract that validates permits and executes approved actions

Backend Signer

Rust-based EIP-712 signer that generates cryptographic permits (see crates/server/src/signer.rs)

Relayer

Any entity can submit signed permits on-chain, enabling gasless transactions for users

Permit-Based System

How It Works

  1. User requests an action through Fishnet (e.g., swap tokens on Uniswap)
  2. Fishnet evaluates the request against policy rules (spending limits, allowed protocols, etc.)
  3. Backend signs a permit using EIP-712 structured data signing
  4. Permit is submitted on-chain by a relayer (user or third-party)
  5. Contract validates the permit and executes the approved action

Benefits

Users don’t need ETH in their wallet. Relayers cover gas costs.
All actions are pre-approved by Fishnet’s policy engine before signing
Anyone can relay signed permits—no centralized execution bottleneck
EIP-712 signatures ensure permits cannot be forged or tampered with

EIP-712 Permit Structure

Each permit authorizes a specific action with precise parameters:
struct FishnetPermit {
    address wallet;          // The FishnetWallet contract address
    uint64  chainId;         // Chain ID (prevents replay attacks across chains)
    uint256 nonce;           // Unique nonce (prevents replay attacks)
    uint48  expiry;          // Expiration timestamp (prevents stale permits)
    address target;          // Target contract to call (e.g., Uniswap Router)
    uint256 value;           // ETH value to send
    bytes32 calldataHash;    // Hash of the exact calldata
    bytes32 policyHash;      // Hash of the policy that authorized this action
}

Security Features

  • Nonce-based replay protection: Each permit can only be used once
  • Chain ID binding: Permits are valid only on the intended network
  • Expiration timestamps: Permits automatically expire after a set time
  • Calldata commitment: The exact transaction data is cryptographically bound to the permit
  • Policy tracking: Every action is linked to the policy that approved it

Smart Contract Features

Core Functions

function execute(
    address target,
    uint256 value,
    bytes calldata data,
    FishnetPermit calldata permit,
    bytes calldata signature
) external whenNotPaused;

Access Control

  • Owner: Can update the signer address, pause/unpause the wallet, and withdraw funds
  • Signer: Backend service that signs permits (can be rotated by owner)
  • Anyone: Can submit valid signed permits (relayer model)

Deployment Networks

FishnetWallet is deployed on multiple networks:

Base Sepolia

Testnet for development and testingChain ID: 84532

Base Mainnet

Production deploymentChain ID: 8453
Deployment addresses are stored in contracts/deployments/*.json.

Compatibility Testing

The contract includes comprehensive compatibility tests between the Solidity implementation and Rust backend:
  • Typehash matching: Raw keccak256 of type strings match
  • Domain separator: Field-by-field EIP-712 domain construction is identical
  • Struct hash encoding: abi.encode padding matches Rust’s manual big-endian padding
  • End-to-end flow: Full sign → verify → execute cycle works correctly
  • Signature format: r || s || v (65 bytes) unpacking is compatible
See test/EIP712Compatibility.t.sol for the full test suite.

Next Steps

Contract API

Explore the full FishnetWallet API, state variables, and events

Deploy Your Own

Deploy FishnetWallet to local Anvil, testnets, or mainnet

EIP-712 Permits

Deep dive into permit structure and signing

Testing

Run the full test suite and verify compatibility

Build docs developers (and LLMs) love