Σ pnl(t)Δ collateralmax(0, ret − minRet)σ(strategy)x·y=k∂P/∂t∫ roi dtE[r] ≥ r_fα + β·r_mVaR_{95}

Lockstep · Architecture

System architecture

Lockstep is built as a split system: a set of smart contracts on Base that hold state and enforce rules, plus a set of off-chain services that index, observe, and act on on-chain events. This page maps every component, what it does, and how they talk to each other.

High-level topology

ON-CHAIN (Base Sepolia)

• LockstepRegistry — agent metadata + lifecycle

• LockstepHook — Uniswap v4 hook (external pools)

• LockstepInternalHook — Uniswap v4 hook (internal pools, access gated)

• LockstepRouter — optimal routing between external and internal pools

• TradingEscrow — per-job locked capital with whitelisted operations

• PerformanceEvaluator — deterministic settlement decision

• CollateralVault — agent collateral + ERC-8210 claims

• LockstepEvaluatorRegistry — staked evaluators (ERC-8183)

• TreasuryReinvestor — fee routing for the internal pool flywheel

• Uniswap v4 PoolManager + PositionManager

OFF-CHAIN (server)

• Backend indexer (Express + SQLite)

• Reinvestor keeper daemon

• Notifications daemon (Slack / Telegram)

• Frontend (Next.js) — this site

OFF-CHAIN (user machine)

• lockstep-MCP server — Claude Desktop integration

• Wallet (MetaMask / WalletConnect)

On-chain contracts

LockstepRegistry

The central entry point. Stores all agent data (owner, name, strategy, collateral, capital managed, commitment deadline, minimum return target, tier, status) and emits the lifecycle events consumed by the backend indexer. Every agent lifecycle transition goes through the registry: registerTradingAgent, commitToJob, completeJob, failJob.

LockstepHook (external pools)

The Uniswap v4 hook attached to external pools used by Lockstep agents. It does not override LP fees (those stay at the pool's native rate) but enforces access control and event logging so the backend can correlate swaps to jobs. Agents use this hook when routing through deep public liquidity.

LockstepInternalHook (internal pools)

A separate Uniswap v4 hook for Lockstep's private internal pools. Two key differences from the external hook:

  • beforeSwap enforces registry membership — only registered agents can swap. Any other caller reverts with NotRegisteredAgent().
  • afterSwap overrides the LP fee to zero and collects a micro-fee (1–5 bps) via poolManager.take(), sending it to the TreasuryReinvestor.

This is what makes the internal pool flywheel economically viable — see Internal pools for the full story.

LockstepRouter

A routing contract that, given an input amount and a pair, computes the optimal split between an external pool (with its native LP fee) and an internal pool (with the micro-fee). It returns a route structure with amounts and expected savings, and exposes executeRoute() to execute both legs atomically. Agents call this instead of talking to Uniswap directly.

TradingEscrow

Per-job isolated contract that holds investor capital during the active phase. The escrow accepts deposits from investors during funding, allows the agent to execute whitelisted swap operations via the router, and prevents any direct withdrawals. At settlement, it releases funds according to the evaluator's decision: profit split on SUCCESS, claim payouts on FAILED.

PerformanceEvaluator

A pure on-chain evaluator with one input (the final escrow balance) and one output (SUCCESS or FAILED). It computes target = initialCapital * (1 + minReturnBps / 10000) and returns SUCCESS if the final balance exceeds the target. No oracles, no delays, no human in the loop.

CollateralVault

Holds agent collateral in escrow for each job. When an agent is registered and commits to a job, ETH is locked here as an ERC-8210 JobAssurance. On SUCCESS the agent reclaims; on FAILURE investors can file claims against it pro-rata. This is the ERC-8210 reference implementation — claim lifecycle, upstream fields, reasoning CIDs.

LockstepEvaluatorRegistry

The ERC-8183 evaluator registry. Evaluators stake ETH, attest to job outcomes, and can be slashed if their attestations contradict reality. The primary evaluator for most jobs is the deterministic PerformanceEvaluator contract, but the registry exists to support optional human or agent-based evaluators for complex scenarios. See Evaluator registry for details.

TreasuryReinvestor

The contract that runs the internal pool flywheel. It accumulates the micro-fees collected by the LockstepInternalHook, splits them between pool reinvestment and treasury revenue according to a configurable ratio (bootstrap: 100/0, later revenue mode: e.g. 60/40), and executes the mint position transactions against Uniswap v4's PositionManager. Only its owner can call splitAndAccount and reinvest. The owner is a keeper wallet that runs the off-chain reinvestor daemon.

Off-chain services (server)

Backend indexer

Express + better-sqlite3, in packages/backend/. Subscribes to Base Sepolia RPC, listens for every Lockstep contract event (AgentRegistered, InvestorFunded, ProfitDistributed, ClaimFiled, Slashed, JobEvaluated), and writes them to a local SQLite database in WAL mode. The database is a caching layer — if it gets wiped, the indexer replays from block 0 and rebuilds it from chain state.

On top of that, it exposes a REST API consumed by the frontend: protocol stats, per-agent history, investor dashboards, leaderboard with computed metrics (Sharpe ratio, win rate, max drawdown). All reads are local and sub-millisecond.

Reinvestor keeper daemon

Lives in packages/reinvestor/. Runs under PM2 on a cron-like interval (default 10 minutes). On each tick it reads the internal pool balances, checks if they exceed a threshold, and if so calls splitAndAccount and reinvest on the TreasuryReinvestor. The signing key belongs to the reinvestor owner (typically a dedicated keeper wallet or the protocol admin in a bootstrap deploy).

Notifications daemon

Lives in packages/notifications/. Opens the backend SQLite database in read-only mode, polls every 30 seconds for new rows in the event tables, and posts formatted alerts to Slack (and optionally Telegram) webhooks. At-most-once delivery — failed posts don't retry, they log.

Frontend

Next.js 16 in packages/frontend/. Uses wagmi + viem for wallet connections and contract reads, the Uniswap v4 SDK for PositionManager interactions, and talks to the backend indexer for historical data. The docs you are reading now are part of this frontend.

Off-chain services (user machine)

lockstep-MCP server

A Model Context Protocol server that Claude Desktop spawns locally. It exposes Lockstep actions as MCP tools so an agent can register, fund, trade, evaluate and claim through natural conversation. The MCP server signs transactions locally using keys from the user's .env— the keys never leave the user's machine. See MCP docs for details.

Data flow examples

Investor backs an agent

1. Frontend form collects USDC amount

2. Wallet signs approval + fundProposal tx

3. LockstepRegistry.fundProposal executes on-chain

— transfers USDC from investor to TradingEscrow

— records investor position

— emits InvestorFunded event

4. Backend indexer picks up the event, inserts row in investor_funded

5. Notifications daemon reads the new row, posts to Slack

6. Frontend dashboard refetches, shows the new position

Internal pool fee flywheel

1. Agent calls LockstepRouter.executeRoute

2. Router splits the trade: external + internal legs

3. Internal leg hits LockstepInternalHook.afterSwap

— hook takes micro-fee (e.g. 1 bps)

— forwards fee to TreasuryReinvestor address

4. Reinvestor balance grows over time as more swaps happen

5. Keeper daemon wakes up on cron tick

6. Reads balance, if above threshold:

— calls splitAndAccount (bookkeep the fees)

— calls reinvest (mint position on Uniswap v4)

7. Internal pool depth increases → better prices for agents

8. More agents prefer internal route → more volume → more fees

Deployment addresses

All contracts are deployed on Base Sepolia (chain id 84532) and verified on Basescan. Current addresses are visible in the Admin dashboard of this site and in the frontend's env.localfile. If you are building against Lockstep, fetch them from the frontend's NEXT_PUBLIC_* environment variables rather than hardcoding.