Back to Docs

Architecture Overview

A technical look at how Aegis enforces what an AI agent can do on-chain — at the contract level, not by trusting the agent or the backend.

Current contract generation: v14. Live on Base mainnet, Base Sepolia, Sepolia, and Arc Testnet. ERC-8004 identity and policy, ERC-4337 account abstraction, EntryPoint v0.6.

The problem, and the guarantee

When you give an autonomous agent access to a wallet, the conventional safeguard is to trust that the agent's own code stays within its mandate. That provides no independent guarantee: a bug, a prompt injection, or a compromised key turns “should not” into “just did.”

Aegis moves the boundary into the contract. Every agent acts through an ERC-4337 smart account whose validateUserOp consults an on-chain PermissionEnforcer before anything executes. A transaction that violates policy reverts inside validation — it never reaches the target contract. The agent cannot bypass this, because the EntryPoint always calls validation first and the account is immutable.

The result is a permission, policy, and audit layer for agents — conceptually “IAM + CloudTrail” for autonomous on-chain activity, with the enforcement guarantee rooted in the chain rather than in software.

System at a glance

Off-chain coordination, on-chain enforcement

            Dashboard / API  (Next.js frontend + Go / Chi REST backend)
                  │                │                │
            Policy Engine    Pre-flight API    Audit + Indexer
            (policies,       POST /validate    (off-chain events +
             permissions)    (simulation)       on-chain event poll)
                  │                │                │
                  └────────────────┼────────────────┘
                                   │
                       Base mainnet / Base Sepolia / Sepolia / Arc Testnet
                                   │
   ┌──────────────┬───────────────┼────────────────┬──────────────────┐
   │              │               │                │                  │
IdentityRegistry  PolicyRegistry  PermissionEnforcer  SpendResolverRegistry
 (ERC-8004)       (ERC-8004)      (constraints)       (calldata decoders)
   │              │               │                │
   └──────────────┴───────┬───────┴────────────────┘
                          │
                  AgentSmartAccount  (ERC-4337)  ◄── AgentAccountFactory (CREATE2)
                          │
                     EntryPoint v0.6

The frontend and backend coordinate and observe. They do not — and cannot — enforce. Enforcement lives entirely in the contracts on the right.

Identity & policy (ERC-8004)

IdentityRegistry stores each agent as a bytes32 identity bound to an owner. Deactivated agents fail isAgentActive at enforcement time and can execute nothing.

PolicyRegistry records a policy as the keccak256 hash of its document (the readable terms live off-chain), and a permission as a revocable, time-windowed link between a policy and an agent.

Enforcement engine

PermissionEnforcer holds the constraints that give a permission teeth. Limits are expressed in each asset's raw base units — no price-oracle normalization in the policy path.

  • • Per-asset maxPerTx, maxDaily, maxTxCount
  • • Allowed actions / protocols / chains (empty = unrestricted)
  • • Delegation ceilings — a parent agent caps a child's budget
  • • Daily volume resets at 00:00 UTC

checkAction is the view-only check used during validation; consumeAllowance is the atomic check-and-record run during execution.

The enforcement path (ERC-4337)

Where a violating transaction dies

Agent signs a UserOperation
      │
      ▼
Bundler (Alchemy / Pimlico) ──► EntryPoint v0.6
                                      │ calls
                                      ▼
              AgentSmartAccount.validateUserOp()
                ├── verify ECDSA signature (EIP-191, the agent's signer key)
                ├── decode "what moves" via SpendResolverRegistry  (view)
                ├── PermissionEnforcer.checkAction()               (view)
                │      └── constraint violated ──► revert (never executes)
                └── pay EntryPoint prefund from the account's balance
      │ (only if valid)
      ▼
              AgentSmartAccount.execute()
                ├── PermissionEnforcer.consumeAllowance()  (atomic check + record)
                └── call target contract

Inner calldata carries an AEGIS_MAGIC prefix so the account can extract the relevant permissionId (and, in intent mode, the intentHash).

The connected owner retains a privileged direct path (raw calldata, no enforcement) — the documented recovery / withdrawal route for the fully-trusted principal. The agent / EntryPoint path is the one that is policed.

Spend Resolver Registrynew in v14

Adding a DeFi protocol no longer means redeploying every account

To enforce a per-asset limit, the account first has to know which assets a call actually moves — and DeFi calldata is protocol-specific. Earlier generations hard-coded every selector (Uniswap, Aerodrome, …) into the account bytecode, so supporting a new protocol meant redeploying accounts.

v14 externalizes that. The SpendResolverRegistry maps a function selector to a decoder adapter and normalizes any call into one canonical shape:

ResolvedSpend {
  amount0, token0,        // primary asset leaving the account
  amount1, token1,        // second asset (e.g. LP add)
  recognized,             // false ──► account rejects the call
  requireRecipientSelf,   // proceeds (LP / collect) must return to the account
  recipient
}

Decoding is two-tier: built-in handlers cover ERC-20 primitives, Uniswap V3, Aerodrome, and LP operations; the CommonDeFiSpendResolver adapter adds ~40 more selectors (Universal Router, Multicall, Permit2, Curve, Balancer, Aave, Compound v2/v3, Beefy, V2 routers) and recurses back into the registry for nested calls like multicall.

The division of labor is the key point: the resolver answers “what will move?”; the PermissionEnforcer answers “is this agent allowed to move that much, today?” New protocol coverage is a registry update, not an account migration. As an additional guard, a single call may move at most two outgoing assets.

Account modes

Accounts deploy in one of three enforcement modes. The default is MODE_POLICY_ONLY.

  • MODE_POLICY_ONLY — PermissionEnforcer constraints only. The current default for all production accounts.
  • MODE_INTENT_ONLY — an IntentRegistry envelope only (a budget with a cap, a use count, and a validity window).
  • MODE_BOTH — intent reservation first, then policy check and usage recording.

The intent layer is an optional envelope model: a signed intent reserves budget atomically before a call and commits or refunds on the outcome. The full payload is stored off-chain (S3) with only its hash committed on-chain. It is currently in test mode — deployed but not yet enabled for production accounts while it is validated end-to-end; all production accounts run MODE_POLICY_ONLY.

Audit & on-chain indexer

The audit log merges two sources. Off-chain entries are written synchronously when state-changing API calls succeed (agent registration, policy/permission changes, pre-flight validations). On-chain entries come from an indexer that polls every 12 seconds and writes each event with source='onchain', a tx_hash, and a block_number. Indexer position is checkpointed so restarts never replay.

Eight event types are indexed:

EnforcementResult · ConstraintViolation · UsageRecorded · Executed · AccountCreated · IntentRegistered · IntentStateChanged · X402PaymentAuthorized

Dashboard entries link straight to the block explorer, and the full log exports to JSON or CSV — so an enforcement decision traces back to the raw transaction that produced it.

Agent-native integration

Pre-flight checks, MCP, and x402

Pre-flight validation. POST /api/v1/validate (and a batch variant) runs the same constraint logic off-chain so a dashboard or SDK can predict an outcome before submitting. It is a simulation — the binding decision is always the on-chain one.

MCP server. A published package (@project-aegis/mcp-server) gives any MCP-capable agent typed tools to discover deployments and DeFi coverage and to run aegis_preflight_transaction, which calls the live PermissionEnforcer.checkAction and reports ERC-4337 prefund readiness — without submitting anything.

x402 payments. Stablecoin payment authorizations (EIP-3009 transferWithAuthorization, settled via the account's EIP-1271 signature) are routed through authorizeX402Payment so they are policy-checked and counted against the same limits as direct transfers. The backend relays the bot-signed UserOp gas-sponsored; the owner key is never needed at runtime.

Trust boundary & threat model

What each component is — and is not — trusted to do

Enforced on-chain (trustless)Trusted off-chain
Signature validation (EIP-191)Policy document content (committed as a hash)
Per-asset value, daily volume, and tx-count limitsIntent payload content (stored in S3; hash on-chain)
Action / protocol / chain allowlistsCalldata encoding and metadata
Intent budget, use count, validity windowConvenience: pre-flight simulation results
Delegation ceilings; recipient-self constraints

A compromised agent cannot

  • • Exceed per-tx, daily-volume, or tx-count limits for any asset
  • • Touch a protocol, chain, or action outside its allowlist
  • • Approve more than the exact amount the next swap pulls
  • • Overspend an intent envelope or act outside its window
  • • Redirect LP / collect proceeds away from the account

Each of these reverts inside validation or execution — funds never move.

A compromised backend cannot

  • • Forge a signature the account will accept
  • • Bypass the on-chain constraint check with a bad permissionId
  • • Under-report usage — quota is tracked in contract state
  • • Raise a child agent's budget above a parent's ceiling
  • • Move funds at all beyond what policy already permits

Worst case it can refuse to act or mis-encode a call — which simply reverts.

Deployments

The v14 contract generation is deployed across four networks — Base mainnet (the canonical production deployment), Base Sepolia, Sepolia, and Arc Testnet — all on EntryPoint v0.6. The deployed contracts are verified on-chain, so their source is viewable and auditable on the block explorer.

Per-chain addresses for IdentityRegistry, PolicyRegistry, PermissionEnforcer, PriceOracle, the fee manager, SpendResolverRegistry, CommonDeFiSpendResolver, and the account factory are listed under Docs → Smart Contracts.