RiskClaw

A 0G agent swarm decides, in real time, how a Uniswap v4 pool should behave under risk

RiskClaw

Created At

Open Agents

Project Description

RiskClaw — an autonomous risk guardian for Uniswap v4-style liquidity pools, powered by 0G.

RiskClaw watches pool behavior in real time, runs verifiable AI risk analysis on 0G Compute, persists every decision and piece of evidence on 0G Storage, and updates an onchain policy registry that a
Uniswap v4 hook reads before every swap and liquidity add. Built for the ETHGlobal agentic hackathon, it is fully shipped and live on the 0G Galileo testnet (chain id 16602).

A three-agent swarm coordinates each loop:

  • Observer snapshots pool metrics and writes them to 0G Storage KV.
  • Analyst sends those metrics to 0G Compute, where a TEE-verified 7B-parameter LLM
    (qwen-2.5-7b-instruct) returns a signed risk memo with a score and reasoning. The memo is pinned to 0G Storage and its explanationRoot is committed onchain.
  • Guardian validates the memo against hard guardrails (max fee, max score, max single-update jump), logs the decision, and submits a signed PolicyUpdate tx to RiskPolicyRegistry.

RiskHook.beforeSwap then enforces one of three modes on the next swap: ALLOW (baseline fee), PENALTY_FEE (dynamic fee override, e.g. 10% effective), or BLOCK (revert with PoolBlocked). The same
hook blocks LP deposits into red pools via beforeAddLiquidity. The hook is deployed at a CREATE2-mined address whose low 14 bits encode its declared v4 permissions (BEFORE_SWAP_FLAG |
BEFORE_ADD_LIQUIDITY_FLAG).

Every policy update onchain carries a PolicyProof struct with explanationRoot, computeProofRoot,
metricsRoot, the provider address, and the inference responseId — so anyone can audit which model, on which evidence, justified each policy change. The full chain (observation → inference → decision →
onchain enforcement → real swap settling at the AI-set fee) has been demonstrated end-to-end with verifiable testnet transactions.

The reusable pieces — 0G Storage memory adapters, 0G Compute + TEE verification helpers, policy
schema/guardrails, and v4 hook adapters — are designed to lift into a hook-agent-kit package so any builder can drop a 0G-powered agent loop in front of their own v4 hook.

Repo: https://github.com/Truunik/RiskClaw

How it's Made

Here's the "How it's made" — broken into stack, integration, and the hacky bits.


Stack

Smart contracts (Solidity 0.8.26, Foundry):

  • RiskHook.sol extends Uniswap v4's BaseHook and implements beforeSwap + beforeAddLiquidity. On every
    swap the hook reads the pool's policy from RiskPolicyRegistry and either (a) reverts with PoolBlocked / PolicyStale / SwapTooLarge, or (b) returns policy.dynamicFee | LPFeeLibrary.OVERRIDE_FEE_FLAG so the
    PoolManager applies that fee for the current swap only.
  • RiskPolicyRegistry.sol holds the per-pool policy and a PolicyProof struct: explanationRoot,
    computeProofRoot, metricsRoot, provider, responseIdHash, promptHash, modelHash. The proof anchors the
    offchain inference to the onchain decision so anyone can audit which model on which evidence justified each update.
  • MockPoolScenario.sol is a scripted risk-event source so the loop can drive the pool through ALLOW →
    PENALTY_FEE → BLOCK on the testnet without waiting for organic activity.
  • Deployment is split: Deploy0G.s.sol deploys PoolManager + Registry + Scenario; DeployHook.s.sol runs HookMiner.find(...) to brute-force a CREATE2 salt, then new RiskHook{salt: salt}(...); InitDynamicFeePool.s.sol initializes the pool with DYNAMIC_FEE_FLAG.

Agents (TypeScript on Bun):

  • Four roles: Observer snapshots pool metrics → Analyst calls 0G Compute → Guardian validates and signs the policy tx → Executor submits onchain. They communicate exclusively through 0G Storage roots.
  • viem for hashing/ABI encoding, ethers v6 for wallet + RPC (the 0G SDKs both expect ethers wallets, so a single Wallet instance is shared).
  • Identical interfaces (ZeroGMemory, ZeroGCompute) for the in-memory/heuristic dev impls and the real
    0G adapters, so bun run loop runs deterministically offline and the live mode swaps in via env vars
    without changing call sites.

Partner technology — 0G stack

  • 0G Compute via @0glabs/0g-serving-broker: the broker fetches the TEE provider's service metadata,
    produces signed billing headers committing to the user content, POSTs to the chat-completions endpoint, and broker.inference.processResponse(provider, chatID, usage) verifies the ZG-Res-Key signature on the way back. Live model is qwen/qwen-2.5-7b-instruct on provider 0xa48f01287233509FD694a22Bf840225062E67836. The prompt is fixed (PROMPT_TEMPLATE) and keccak256'd into promptHash; same for the model name into modelHash. The Analyst constrains the LLM to a strict JSON schema (riskScoreBps, recommendedFee, recommendedMaxAbsAmount, reasoning[]) so downstream parsing is deterministic.
  • 0G Storage via @0gfoundation/0g-ts-sdk's Indexer + MemData: every memo, decision log, and pool snapshot is uploaded as a small JSON blob; the Indexer returns a content-addressed root that becomes
    explanationRoot / computeProofRoot / metricsRoot onchain. Verification is a single download(root) call away.
  • 0G Chain (Galileo testnet, chain id 16602): PoolManager, RiskPolicyRegistry, RiskHook, and MockPoolScenario all live here. Same chain as inference and storage = no bridging, single signer key
    for the whole loop.

The benefit of using the full 0G stack is that the audit chain doesn't have any black boxes: the
inference itself is TEE-attested, the evidence and the memo are content-addressed and retrievable, and the onchain policy carries the roots and IDs that bind those pieces together. We could not have
produced this end-to-end provenance with a generic LLM API + IPFS + L2.

Hacky / notable bits

  1. CREATE2 hook-address mining. Uniswap v4 PoolManager rejects hooks whose address low bits don't match their declared permissions. RiskHook declares beforeSwap | beforeAddLiquidity, so its address must end in 0x880. HookMiner.find(CREATE2_DEPLOYER=0x4e59…956C, flags=0x880, creationCode, abi.encode(manager, registry)) brute-forces a salt; the deployment script deploys with new RiskHook{salt: salt}(...) and asserts address(hook) == expected. The deployment is split into two scripts because the miner needs the manager and registry addresses from the prior deploy.
  2. OVERRIDE_FEE_FLAG only works on dynamic-fee pools. The pool itself must be initialized with LPFeeLibrary.DYNAMIC_FEE_FLAG; otherwise the override is silently ignored. We learned this the hard way — InitDynamicFeePool.s.sol was added after a clean swap settled at the static fee instead of the AI-set 10%.
  3. 0G Storage is content-addressed, period. ZeroGStorageMemory.kvGet is intentionally a no-op that returns undefined. Callers track the roots they receive from kvSet / upload themselves and fetch by
    root via download(root). We treat KV as "the latest known root for this key, held in agent memory" rather than fighting the SDK.
  4. The TEE proof anchor is a tuple, not a signature. The broker's processResponse() returns a boolean — the raw TEE signature isn't surfaced. So the onchain proof anchor is (rawResponseHash, ZG-Res-Key
    chatID, verificationResult) plus the provider address, all committed via PolicyProof. A future verifier can re-call processResponse against the provider with the stored chatID to re-attest.
  5. Guardrail layer between the LLM and chain. A hijacked or hallucinating Analyst can produce arbitrarily bad memos. The Guardian rejects any update whose fee or score jump from the previous policy exceeds maxFeeJump (50000 pips) or maxScoreJump (4000 bps), and any memo where verificationResult === false. So no single inference can flip a healthy pool to BLOCK in one tx.
  6. 0G Chain enforces a 2 gwei minimum gas price. The agents hardcode 3 gwei to stay safely above it; the broker uses the same setting for any onchain billing settlement transactions.
  7. One signer, four roles. Observer/Analyst/Guardian/Executor all share the same ethers.Wallet instance for the demo. A production split would give Guardian a hardware-key signer and run the Executor as a
    separate service, but for the hackathon a single key keeps the loop in one process and one transaction history.

The reusable pieces — memory/ (0G Storage adapters), compute/ (0G Compute + TEE verification helpers), policy/ (schema + guardrails), and hooks/ (v4 hook adapters) — are designed to lift cleanly into a hook-agent-kit package so any v4 builder can drop a 0G-powered agent loop in front of their own hook.

Repo: https://github.com/Truunik/RiskClaw

background image mobile

Join the mailing list

Get the latest news and updates