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:
- Generate a wallet
- Create an account on a cryptocurrency exchange
- Complete KYC on the exchange
- Purchase ETH with fiat currency
- Bridge ETH to Base L2
- 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.tois the Agent Registry contract address (rejects arbitrary targets) - Value validation: Confirms
request.valueis 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:
- Verifies the EIP-712 signature on-chain using
ecrecover - Checks the nonce to prevent replay attacks
- Checks the deadline (if set) to prevent expired requests
- Increments the nonce for the signer
- 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