Skip to content

Gasless Transactions (ERC-2771)

The Agent Registry uses ERC-2771 meta-transactions to allow agents to register, attest compliance, report revenue, and perform all other operations without ever holding cryptocurrency. This page explains the problem, the solution, and the full technical flow.


The Problem

To interact with a smart contract on Ethereum (or any EVM chain), you need to pay a gas fee in the chain's native token (ETH). This means that before an agent can register in the Agent Registry, it would need to:

  1. Generate a wallet
  2. Create an account on a cryptocurrency exchange
  3. Complete KYC on the exchange
  4. Purchase ETH with fiat currency
  5. Bridge ETH to Base L2
  6. Then, finally, submit the registration transaction

Adoption Killer

Steps 2-5 are a massive adoption barrier. Most AI agents do not have bank accounts, exchange accounts, or fiat-to-crypto onramps. Requiring cryptocurrency ownership before registration would limit the registry to a tiny fraction of agents.


The Solution: ERC-2771 Meta-Transactions

ERC-2771 is an Ethereum standard for meta-transactions. It separates the signer (the entity that authorizes an action) from the payer (the entity that pays for gas). A trusted forwarder contract verifies the signer's authorization and forwards the call to the target contract, appending the real signer's address to the calldata.

The result: agents sign messages locally (free), and a relayer service submits and pays for the transaction on their behalf.

What Agents Need

An agent needs only a private key to register. No ETH. No gas tokens. No bridge. No faucet. Just sign and go.


How It Works: Step by Step

The following seven steps describe the complete gasless registration flow.

Step 1: Agent Generates a Private Key

The agent generates an Ethereum-compatible private key. This is a purely local operation that costs nothing and requires no network access.

from eth_account import Account
account = Account.create()
# account.address = "0x..."
# account.key = b'\x...'

Step 2: Agent Signs the Registration Request

The agent constructs the registerAgent() calldata and signs it as EIP-712 typed data. This happens entirely locally -- no gas, no network call, no cost.

# The SDK handles this automatically
registry = AgentRegistry.from_env()
# Internally: encode calldata, build ForwardRequest, sign EIP-712

Step 3: Agent Sends the Signed Request to the Relayer

The signed request is sent as an HTTP POST to the relayer service.

POST /relay
{
  "request": {
    "from": "0xAgentAddress",
    "to": "0xRegistryAddress",
    "value": "0",
    "gas": "800000",
    "nonce": "0",
    "deadline": "0",
    "data": "0x..."
  },
  "signature": "0x..."
}

Step 4: Relayer Validates the Request

The relayer performs several checks before submitting:

  • Signature verification: Recovers the signer from the EIP-712 signature and confirms it matches request.from
  • Rate limiting: Checks that the IP and signer have not exceeded their hourly limits (20/IP, 10/signer)
  • Target validation: Confirms request.to is the Agent Registry contract address (rejects arbitrary targets)
  • Value validation: Confirms request.value is zero (rejects ETH transfers)
  • Balance check: Confirms the relayer wallet has sufficient ETH to pay gas

Step 5: Relayer Submits to MinimalForwarder

The relayer calls MinimalForwarder.execute(request, signature) on-chain, paying approximately $0.005 in gas on Base L2.

Step 6: MinimalForwarder Verifies and Forwards

The MinimalForwarder contract:

  1. Verifies the EIP-712 signature on-chain using ecrecover
  2. Checks the nonce to prevent replay attacks
  3. Checks the deadline (if set) to prevent expired requests
  4. Increments the nonce for the signer
  5. Forwards the call to the Agent Registry, appending the agent's address to the calldata per the ERC-2771 convention

Step 7: Agent Is Registered

The Agent Registry's _msgSender() function detects that the call came from the trusted forwarder and extracts the real sender (the agent) from the last 20 bytes of calldata. The agent is registered with the agent's address as the creator -- not the relayer's address.

The Agent Never Touches ETH

Throughout this entire flow, the agent never holds, spends, or even needs access to any cryptocurrency. The relayer is the only entity that interacts with the blockchain directly.


EIP-712 Typed Data Structure

The ForwardRequest is the EIP-712 typed data structure that the agent signs.

ForwardRequest(
    address from,       // Real signer (agent/creator)
    address to,         // Target contract (Agentenregister)
    uint256 value,      // ETH to forward (always 0)
    uint256 gas,        // Gas limit for the inner call
    uint256 nonce,      // Replay protection (auto-incremented)
    uint48  deadline,   // Expiry timestamp (0 = no expiry)
    bytes   data        // Encoded function call (e.g., registerAgent(...))
)

EIP-712 Domain:

Field Value
name "MinimalForwarder"
version "1"
chainId Chain ID of the deployment network
verifyingContract Address of the MinimalForwarder contract

The domain separator is computed at deployment time and cached. If the chain ID changes (e.g., due to a fork), it is recomputed automatically.


Security Model

The gasless relayer is designed with multiple layers of protection.

Target Restriction

The relayer only forwards requests where request.to matches the deployed Agent Registry contract address. Arbitrary contract calls are rejected.

Value Restriction

The relayer rejects any request where request.value > 0. The gasless flow is for function calls only, not ETH transfers.

Rate Limiting

Limit Value Scope
IP rate limit 20 requests/hour Per source IP
Signer rate limit 10 requests/hour Per signing address

Daily Gas Budget

The relayer enforces a daily gas budget (default: 0.05 ETH/day). When the budget is exhausted, the relayer stops accepting new requests until the next day.

Balance Backpressure

When the relayer wallet balance drops below 0.001 ETH, the relayer stops accepting requests and returns a 503 Service Unavailable response. This prevents the relayer from running out of gas mid-transaction.

Nonce Management

Each signer has an incrementing nonce tracked by the MinimalForwarder contract. This prevents replay attacks -- a signed request can only be executed once.

Deadline Enforcement

The deadline field in the ForwardRequest allows agents to set an expiry time. If deadline != 0 and block.timestamp > deadline, the forwarder rejects the request. This prevents stale requests from being submitted.


Cost Table

All operations are free for the agent. The relayer pays a small gas fee on Base L2.

Operation Cost to Agent Cost to Relayer (Base L2)
Register agent $0.00 ~$0.005
Attest compliance $0.00 ~$0.001
Report revenue $0.00 ~$0.002
Update capabilities $0.00 ~$0.001
Update constitution $0.00 ~$0.001
Terminate self $0.00 ~$0.001
Query registry $0.00 $0.00 (view function)

Scale Economics

At these costs, the relayer can serve approximately 1,000 agents for ~$5/month on Base L2. This makes it feasible for a research institution or grant-funded project to subsidize registration for the entire early ecosystem.


Who Pays for Gas?

The relayer wallet is funded by different sources depending on the project phase.

Phase Funding Source
Phase 1 (now) COAI Research funds the relayer wallet (~$5/month for 1,000 agents)
Phase 2 Grants from Gitcoin, Optimism RetroPGF, or Base Ecosystem Fund
Phase 3 Infrastructure providers subsidize registration on their platforms

Relayer Endpoints

The relayer exposes four HTTP endpoints.

Method Endpoint Description
POST /relay Submit a signed meta-transaction for on-chain execution
GET /nonce/:address Get the current forwarder nonce for an address (needed for signing)
GET /domain Get the EIP-712 domain parameters (needed for signing)
GET /status Relayer health: wallet address, balance, gas budget, daily usage

Direct Mode (No Relayer)

Both SDKs also support direct mode for agents that already hold ETH. In direct mode, the agent submits transactions directly to the Agent Registry contract without going through the relayer or forwarder.

When to Use Direct Mode

Direct mode is useful for:

  • Testing on a local Hardhat node (where gas is free)
  • Agents that already have ETH and prefer not to depend on the relayer
  • High-throughput scenarios where relayer rate limits are a constraint