Objective EBBO enforcement for UniswapX. Bad solver fills get challenged and slashed automatically.
What Reckon Is
Reckon is a cryptoeconomic validation layer for DeFi solvers, built on UniswapX on Base. It makes every solver fill cryptographically challengeable with automatic slashing on objective price violations — no DAO vote, no human intervention, no delay.
The Problem
In solver markets like UniswapX and CoW Protocol, there is no objective, automated way to hold solvers accountable for fill quality. If a solver gives you a bad price, there's no on-chain benchmark to prove it, no mechanism to challenge it, and no automatic penalty. CowSwap's slashing requires a governance vote that takes hours to days. Most bad fills go completely unpunished.
Identity Layer (ENS)
Solvers register as virtual ENS subnames under solvers.reckonprotocol.eth, served via an ENSIP-10 wildcard resolver and CCIP-Read gateway deployed on Ethereum mainnet. Subnames are virtual — they live in MongoDB and are resolved transparently by any standard ENS client (viem, ethers, wagmi) without Reckon-specific integration. All on-chain storage (bonds, fills, reputation) is keyed by ENS namehash, not by wallet address. Reputation scores are published as ENS text records (reckon.reputation, reckon.totalFills, reckon.slashCount), so any third-party app can call viem.getEnsText({ name: 'bunni.solvers.reckonprotocol.eth', key: 'reckon.reputation' }) and get the live value.
How Fills Are Validated
Swappers opt in by setting Reckon's ReckonValidator as their UniswapX order's additionalValidationContract along with an EBBO tolerance in basis points. After every fill, an off-chain relayer records it on-chain in the FillRegistry and in MongoDB. The validator is view-only — it gates fills on solver registration but never blocks valid fills or writes state.
Challenger Agents (0G)
Autonomous challenger agents are minted as ERC-7857 iNFTs on 0G Galileo, each with an encrypted brain blob (AES-256-GCM) stored on 0G Storage containing its Ed25519 keys, challenge thresholds, and KeeperHub credentials. On detecting a fill, agents run suspicion triage via 0G Compute (LLM inference), then check the EBBO benchmark — an equal-weighted geometric mean across 3 canonical Uniswap v3/v4 pools that resists single-pool manipulation. If the solver's output falls below the benchmark minus the swapper's tolerance, the agent initiates a challenge. Two agents are deployed: Sentinel (#0) and Warden (#2).
Multi-Agent Coordination (Gensyn AXL)
Multiple agents coordinate over a Gensyn AXL encrypted P2P mesh using a first-claim-wins protocol. Each agent signs claims with Ed25519, broadcasts via AXL's HTTP API, and applies a deterministic tiebreaker (earliest timestamp, then lowest token ID). 0G Storage KV provides durable backup coordination in case the mesh is partitioned — ensuring no duplicate challenge submissions even under network failure.
Challenge Execution (KeeperHub)
The winning agent submits challenges via KeeperHub webhook workflows, which handle gas estimation, Turnkey-signed transactions, and retry logic. The Challenger contract supports a delegation pattern: KeeperHub's wallet executes the transaction while the USDC bond is pulled from the agent owner's pre-signed Permit2 permit. KeeperHub also handles EBBO benchmark reads and fill event monitoring through additional webhook workflows.
Slashing & Rewards
If a challenge succeeds, slashing is immediate and automatic: 60% to the swapper as restitution, 30% to the current iNFT owner (the person who owns the challenger agent NFT — tradeable, with earnings following ownership atomically), and 10% to the protocol treasury. Solver reputation is decremented and flushed to ENS text records via CCIP-Read. Every fill and slash is batched to 0G Storage Log with Merkle roots anchored on-chain for a permanent audit trail.
What's Deployed
9 smart contracts on Base Sepolia, 1 wildcard resolver on Ethereum mainnet, and 2 contracts (ChallengerNFT + MockVerifier) on 0G Galileo testnet. The system includes a reference demo solver, two live challenger agents, a Next.js dashboard, a CCIP-Read gateway, and an off-chain relayer with 0G Storage archival.
UniswapX Integration — The Foundation
Everything starts with UniswapX's additionalValidationContract hook. When a swapper creates a PriorityOrder, they can set a custom validator that the PriorityOrderReactor calls during fill execution. We built ReckonValidator as a view-only implementation of IValidationCallback.validate() — it checks that the filler has a registered ENS subname in SolverRegistry and that the EBBO tolerance decodes cleanly from additionalValidationData, but it never blocks a valid fill or writes state. This is a hard constraint: UniswapX's validate interface is external view, so it cannot emit events or modify storage. That single constraint shaped our entire architecture — fill recording has to happen separately via an off-chain relayer that subscribes to the reactor's Fill(orderHash, filler, swapper, nonce) events.
We built a reference demo solver (solver/) that speaks the full UniswapX flow: it accepts signed PriorityOrders via HTTP, decodes and validates them (correct reactor address, supported USDC/WETH pair, single output, not expired, ReckonValidator set), then calls PriorityOrderReactor.execute(SignedOrder) with explicit maxFeePerGas >= block.basefee — a requirement specific to PriorityOrderReactor that silently reverts without it. The solver auto-registers its ENS subname and auto-bonds USDC on startup via the relayer.
Smart Contracts — Solidity 0.8.26 + Foundry
We wrote 10 contracts on Base Sepolia, 1 on Ethereum mainnet, and 2 on 0G Galileo. The core challenge pipeline flows through FillRegistry (permissioned fill recording) -> Challenger (challenge submission with on-chain EBBO verification) -> EBBOOracle (reads slot0/StateView.getSlot0 from 3 canonical Uniswap v3/v4 pools, computes equal-weighted geometric mean in 1e18 precision) -> SolverBondVault (namehash-keyed bond slashing) -> RoyaltyDistributor (60/30/10 split). All storage is keyed by ENS namehash (bytes32 node), never by address — so a solver's identity survives wallet rotation.
One notable design: Challenger.sol has a delegation mechanism (agentDelegate mapping + setAgentDelegate()) built specifically so KeeperHub's wallet can call submit() on behalf of the agent owner, while _pullBond() always pulls the Permit2 bond from the owner's pre-signed permit — separating execution authority from signing authority.
ENS — Virtual Subnames via Wildcard Resolver + CCIP-Read
Instead of minting on-chain subnames (expensive, slow), we built virtual subnames. ReckonWildcardResolver.sol is deployed on Ethereum mainnet and set as the resolver for reckonprotocol.eth. For any subname query, it reverts with OffchainLookup per EIP-3668, redirecting ENS clients to our CCIP-Read gateway. The gateway (Node.js + Express) looks up the subname in MongoDB, returns the requested record (address, text record), and signs the response with EIP-712 using a key the resolver trusts. Standard ENS clients handle all of this transparently — viem.getEnsText({ name: 'bunni.solvers.reckonprotocol.eth', key: 'reckon.reputation' }) just works, with no client-side awareness that the subname is virtual.
The benefit: zero gas per subname registration, instant creation, and full ENS compatibility. A third-party DeFi aggregator can build a "trustworthy solvers" leaderboard using standard ENS reads without knowing Reckon exists.
0G — Four Subsystems Woven Together
0G isn't a single integration — it provides four distinct layers. (1) ERC-7857 iNFTs on Galileo: each challenger agent is minted as an intelligent NFT via ChallengerNFT.sol (forked from 0g-agent-nft), giving it on-chain identity and tradeable ownership. (2) 0G Storage for brain blobs: each agent's operational intelligence (Ed25519 keys, EBBO thresholds, KeeperHub credentials, performance history) is AES-256-GCM encrypted and uploaded to 0G Storage via @0gfoundation/0g-ts-sdk. The tokenURI points to the Merkle root hash. At boot, the agent downloads the blob, verifies the Merkle proof, decrypts with the owner's PBKDF2-derived key, and initializes. (3) 0G Storage KV for claim coordination: a single protocol-wide KV stream stores claim state keyed by orderHash, providing durable consensus when AXL gossip is insufficient. (4) 0G Compute Router API for LLM inference: agents run suspicion triage (scoring fills 0.0-1.0) before expensive benchmark computation, using models like Qwen 2.5 7B via the OpenAI-compatible router endpoint. The relayer also batches fills to 0G Storage Log every 50 records or 600 seconds, anchoring Merkle roots on-chain for a permanent audit trail independent of MongoDB.
Gensyn AXL — Encrypted P2P Claim Coordination
When multiple agents detect the same slashable fill, only one should submit. We built a two-layer coordination protocol. Layer 1 (fast, ephemeral): each agent runs alongside its own AXL node with Yggdrasil e2e encryption. On detecting a slashable fill, the agent signs a claim with Ed25519 (key from the iNFT brain blob), sends it to peers via POST /send with X-Destination-Peer-Id, and polls GET /recv during a 30-second backoff window. Deterministic tiebreaker: earlier claimedAt wins, equal timestamps -> lower tokenId wins. Both agents agree without further communication. Layer 2 (slow, durable): after surviving gossip, the winner writes to 0G Storage KV and does a read-after-write verification. If two agents write simultaneously, the read-back reveals the conflict. This dual-layer design means the happy path resolves in ~30 seconds via AXL, while network partitions are caught by KV — no double submissions ever.
KeeperHub — Three Webhook Workflows
KeeperHub handles all on-chain execution for the agents through three webhook workflows. (1) Fill listener: watches for FillRecorded events and reads full fill details via web3/read-contract. (2) EBBO benchmark read: calls EBBOOracle.computeBenchmark(tokenIn, tokenOut) and returns the price. (3) Challenge submission: the agent pre-signs a Permit2 EIP-712 bond permit and POSTs the payload to KeeperHub, which calls Challenger.submit() via its Turnkey-signed wallet (set as agentDelegate on the contract). The agent then polls KeeperHub's execution logs API to retrieve the transaction hash. This pattern — signing authority stays with the agent owner, execution authority delegated to KeeperHub — was a deliberate security choice.
The Hacky Parts
Dual-chain setup for UniswapX: UniswapX's PriorityOrderReactor is only deployed on Base mainnet — there are zero testnet deployments on any chain. To build and demo against real UniswapX without spending real funds, we ran a dual-chain setup: an Anvil fork of Base mainnet for the swap layer (where the real PriorityOrderReactor bytecode, real Uniswap pools with real liquidity, and real Permit2 all live), and Base Sepolia for the Reckon protocol layer (where fill recording, challenges, slashing, and reputation all happen). The relayer bridges the two: it watches Fill events on the Anvil fork and records them into FillRegistry on Base Sepolia. This let us exercise the full UniswapX integration — real reactor, real pool prices, real order signing — while keeping all Reckon state on a proper testnet that persists, has a block explorer, and can be independently verified by judges.
Cross-chain iNFT ownership without a bridge: the relayer listens to Transfer events on ChallengerNFT (0G Galileo) and calls OwnerRegistry.attestOwner(tokenId, newOwner) on Base Sepolia from a permissioned EOA. It's a trusted relay, not a trustless bridge — honest hackathon scoping, but it works and the production path (LayerZero/Wormhole) is documented. The OwnerRegistry has a freshness timestamp so stale attestations don't block slashing — payouts just queue for retry.
The monorepo uses npm workspaces with packages/types as a shared dependency that builds first (npm run build from root handles everything). Each agent (Sentinel, Warden) is a separate workspace with its own env config but shared TypeScript types and constants.

