You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Agents can only trade venues the environment deploys. The market set is fixed at block 0, so "where should liquidity live" is never a decision an agent makes, and market design is not a skill the competition can reward.
The blocker is not deployment. example/agents/runtime/deploy.ts already lets a participant deploy with their own key, and the Uniswap V3 / Balancer / Curve factories are all deployed (deployer/deployments/deployments.json). The two things missing are that nothing tells the other agents a new market exists, and nothing values a position held in one — value parked in an agent-created pool is invisible to core/src/realtime/reconstruct.ts and silently evaporates from the score.
Proposal
Distribute the set of agent-deployed contracts on-chain the same way the fair price is distributed: the coordinator observes off-chain, writes to a contract every block, and every agent reads the same list.
Design decisions (fixed)
MarketRegistry is the second instance of the PriceFeed pattern, not a new concept. Owner-gated writes from the environment's admin wallet, an event per entry, count() / all() / isRegistered() reads, ~50 lines in contracts/ (PriceFeed.sol is 68). It inherits that pattern's one-block distribution lag, which is already documented and accepted ("the write tx lands in the next block, so the information is one block late — it applies equally to everyone"). The creator knows about its own market one block before anyone else; that head start is the incentive to build markets and is intended.
Detection is coordinator-side and log-first. For the known factories (Uniswap V3 PoolCreated, Balancer PoolRegistered, Curve pool deployment) subscribe to factory logs — that yields address, tokens and fee directly, with no classification heuristics. The coordinator already sweeps the caught-up block range in a single getLogs for the GMX keeper (core/src/realtime/coordinator.ts:898); add filters there. For arbitrary contracts, scan the block's transactions for to === null and derive the address with viem's getContractAddress({ from, nonce }) — no receipt fetch, which also sidesteps the broken eth_getBlockReceipts on the Arbitrum fork. That scan misses CREATE/CREATE2 from inside a contract; cover it with a fallback that watches transfers of known tokens and flags unseen recipients whose eth_getCode is non-empty (a pool must receive real tokens to be tradable, so funding — not deployment — is the detectable moment; the same definition ADR 0014 already uses for vuln pools).
Everything is recorded; only some of it is verified. Each entry carries a kind (uniswapV3Pool / balancerWeightedPool / curvePlainPool / curveTwocryptoPool / erc20 / unknown), the creator, the tokens where known, the runtime codehash, and a verified flag. Verified means it came out of a known factory, so its implementation is environment-owned canonical code. Everything else is listed as unknown / unverified — discoverable, but carrying no safety claim.
The registry is a discovery mechanism, not a verification mechanism. It guarantees only that something exists. Dry-running the interaction stays the primary defence (ADR 0014 §4), and the recorded codehash lets an agent detect a swap-out between observation and execution. Losing money to an unverified contract is a legitimate loss and is not made whole.
Expose it through observationFor, not only on-chain. Rule agents can call the registry or read logs directly, but prompt-driven agents only ever see the observation, and the default Quick Start roster ships trading agents in prompt mode (ADR 0015 §2). A discovery feature only agent.ts can use would exclude the default configuration.
The registry is the scoring authority.reconstruct.ts values LP positions in registered pools and nothing else. This closes the evaporation hole by construction: what cannot be enumerated cannot be scored, and what is scored is exactly what the registry lists.
A token with no fair price is marked at realizable exit value, and at zero for its issuer. Agents may deploy and list their own ERC-20. Marking such a token at its pool spot price would let an agent mint a token, trade against itself in a thin pool it also owns, and manufacture unbounded score at no cost — the quote asset round-trips straight back to them. Marking instead at what selling the whole balance into the pool would actually return (a full-size quoteExactInput / get_dy, not balance × spot) bounds the credited value by the real assets sitting in the pool, so fabricated value has a hard ceiling. QuoterV2, BalancerQueries and Curve's get_dy are all already deployed and exposed in constants.local.ts.
The issuer's own holding is excluded outright — the registry records the creator — because the issuer also holds the LP position backing that pool, and crediting both would double-count the same reserves.
Every other holder marks at realizable value. That is also what makes a rug victim's accounting correct: valuing the token at zero for all holders would confiscate the portion they could still recover from the pool, overstating the loss. Rug pulls stay a viable strategy while remaining a zero-sum transfer rather than value creation.
A TWAP does not substitute for this. An asset with no external fair value has no arbitrageur to push a manipulated price back, so holding an inflated mark across the averaging window is cheap. Time-averaging only helps when an outside force corrects the price.
Agent-created markets are excluded from the no-arb guard.core/src/realtime/noArb.ts fails the run at startup and warns on persistent cross-venue spreads. A thin agent-made pool is supposed to sit away from fair; only environment-deployed venues belong in that check.
The environment does not make markets in them. Flow bots and oracle updates stay on environment venues. Price discovery inside an agent-created market is the agents' problem.
core — factory-log filters and CREATE scanning in the coordinator's per-block sweep; a batched register write. Cap registrations per block and carry the overflow into the next block: deployment is already gas-costly for the agent under ADR 0011, but the registry write is paid by the environment, so a deploy-spam agent must not be able to inflate that cost without bound.
sdk (constants) — expose the factories. The LocalDeployment type in sdk/src/constants.local.ts currently carries only poolWethUsdc500 / swapRouter / nonfungiblePositionManager / quoterV2 for Uniswap, vault / queries / pool / poolId for Balancer, and pool + indices for Curve, while deployments.json already holds uniswapV3.factory, balancerV2.weightedPoolFactory, curve.factory and curve.twocryptoFactory. Map them in gen:local-constants.
sdk (actions) — pool creation for all three factories, plus ERC-20 deployment for agents listing their own token.
sdk (observation) — registry entries, with enough per-entry detail to decide whether to interact.
A capability nobody uses is not exercised, and this one needs both sides — a market that gets created and a market that gets found. Ship example/agents/market-launcher/ carrying both agent.ts and prompt.md (every bundled agent provides both ways of running, ADR 0015 §2), plus a roster entry in config/example.yaml.
prompt.md cannot be a stub. The committed config/example.yaml roster ships trading agents in prompt mode, so the Quick Start default drives this through the LLM path. The prompt must describe the registry observation fields and the decisions they support well enough that a model can act on them without reading agent.ts.
market-launcher (new) — create a pool through a factory, seed it, and manage it: pick a pair and fee tier the environment does not already provide, size the initial liquidity, and decide when to pull. Exercises the creation side and, in phase 3, the rug side.
discovery-arb / discovery-arb-verify (extend the existing ADR 0014 agents) — source candidates from the registry rather than only from the vuln factory, so the discovery and verification sides are exercised against agent-created markets too. The verified flag is exactly the signal discovery-arb-verify should gate on.
Arbitrary CREATE recording (kind=unknown, verified=false), agent-deployed ERC-20 listing, and rug pulls as a sanctioned strategy.
Open points
Registration cap and ordering. What the per-block cap should be, and whether overflow is FIFO or prioritised by kind (a pool from a known factory is more useful to publish promptly than an unknown contract).
ERC-20 classification. ERC-165 does not cover ERC-20, so classifying a freshly deployed token means static-calling name() / symbol() / decimals() and accepting the heuristic, or only recording a token once a known factory pairs it into a pool.
Rug-pull scoring semantics. Once phase 3 lands, a run's outcome can be dominated by one successful rug. Whether that is the intended discrimination signal or needs a bound should be decided from live runs, not up front.
check:strategy surface. Deployment is currently unrestricted by the cheatcode static check. If arbitrary deployment becomes a first-class capability, the entry gate should state explicitly what is and is not allowed rather than leaving it implicit.
Roadmap position — steps 4 and 6 of 7
Blocked by #41 (positions outside the registered market set score as zero). Registry-authoritative scoring is a special case of the adapter-driven historical valuation proposed there, and the realizable-exit marking this issue relies on is the same principle #41 needs for #38 and #39.
This issue splits across the sequence: phase 1 is step 4, once #41 has landed and #38 has established the realizable-exit pattern. Phases 2–3 are step 6, last, because arbitrary deployment and sanctioned rug pulls open the largest design surface and will generate follow-on rule decisions.
Independent tracks: #33 / #35 / #36 (own chain — nothing here is chain-specific, so no ordering constraint), #20 → #34 (strategy). #31 / #32 (observability) should not land before #41.
Design details to be worked out at implementation time.
Motivation
Agents can only trade venues the environment deploys. The market set is fixed at block 0, so "where should liquidity live" is never a decision an agent makes, and market design is not a skill the competition can reward.
The blocker is not deployment.
example/agents/runtime/deploy.tsalready lets a participant deploy with their own key, and the Uniswap V3 / Balancer / Curve factories are all deployed (deployer/deployments/deployments.json). The two things missing are that nothing tells the other agents a new market exists, and nothing values a position held in one — value parked in an agent-created pool is invisible tocore/src/realtime/reconstruct.tsand silently evaporates from the score.Proposal
Distribute the set of agent-deployed contracts on-chain the same way the fair price is distributed: the coordinator observes off-chain, writes to a contract every block, and every agent reads the same list.
Design decisions (fixed)
MarketRegistryis the second instance of thePriceFeedpattern, not a new concept. Owner-gated writes from the environment's admin wallet, an event per entry,count()/all()/isRegistered()reads, ~50 lines incontracts/(PriceFeed.solis 68). It inherits that pattern's one-block distribution lag, which is already documented and accepted ("the write tx lands in the next block, so the information is one block late — it applies equally to everyone"). The creator knows about its own market one block before anyone else; that head start is the incentive to build markets and is intended.PoolCreated, BalancerPoolRegistered, Curve pool deployment) subscribe to factory logs — that yields address, tokens and fee directly, with no classification heuristics. The coordinator already sweeps the caught-up block range in a singlegetLogsfor the GMX keeper (core/src/realtime/coordinator.ts:898); add filters there. For arbitrary contracts, scan the block's transactions forto === nulland derive the address with viem'sgetContractAddress({ from, nonce })— no receipt fetch, which also sidesteps the brokeneth_getBlockReceiptson the Arbitrum fork. That scan misses CREATE/CREATE2 from inside a contract; cover it with a fallback that watches transfers of known tokens and flags unseen recipients whoseeth_getCodeis non-empty (a pool must receive real tokens to be tradable, so funding — not deployment — is the detectable moment; the same definition ADR 0014 already uses for vuln pools).verified. Each entry carries akind(uniswapV3Pool / balancerWeightedPool / curvePlainPool / curveTwocryptoPool / erc20 / unknown), the creator, the tokens where known, the runtimecodehash, and averifiedflag. Verified means it came out of a known factory, so its implementation is environment-owned canonical code. Everything else is listed asunknown/ unverified — discoverable, but carrying no safety claim.codehashlets an agent detect a swap-out between observation and execution. Losing money to an unverified contract is a legitimate loss and is not made whole.observationFor, not only on-chain. Rule agents can call the registry or read logs directly, but prompt-driven agents only ever see the observation, and the default Quick Start roster ships trading agents in prompt mode (ADR 0015 §2). A discovery feature onlyagent.tscan use would exclude the default configuration.reconstruct.tsvalues LP positions in registered pools and nothing else. This closes the evaporation hole by construction: what cannot be enumerated cannot be scored, and what is scored is exactly what the registry lists.quoteExactInput/get_dy, notbalance × spot) bounds the credited value by the real assets sitting in the pool, so fabricated value has a hard ceiling.QuoterV2,BalancerQueriesand Curve'sget_dyare all already deployed and exposed inconstants.local.ts.core/src/realtime/noArb.tsfails the run at startup and warns on persistent cross-venue spreads. A thin agent-made pool is supposed to sit away from fair; only environment-deployed venues belong in that check.Scope
MarketRegistry.sol.registerwrite. Cap registrations per block and carry the overflow into the next block: deployment is already gas-costly for the agent under ADR 0011, but the registry write is paid by the environment, so a deploy-spam agent must not be able to inflate that cost without bound.LocalDeploymenttype insdk/src/constants.local.tscurrently carries onlypoolWethUsdc500/swapRouter/nonfungiblePositionManager/quoterV2for Uniswap,vault/queries/pool/poolIdfor Balancer, andpool+ indices for Curve, whiledeployments.jsonalready holdsuniswapV3.factory,balancerV2.weightedPoolFactory,curve.factoryandcurve.twocryptoFactory. Map them ingen:local-constants.reconstruct.tsvalues LP positions in registered pools. This depends on [sim] Scoring black hole: positions outside the registered market set are valued at exactly zero #41: Balancer BPT and Curve LP-token valuation do not exist, and Uniswap V3 positions outside the registered market set are currently valued at exactly zero. With [sim] Scoring black hole: positions outside the registered market set are valued at exactly zero #41 landed, the work remaining here is sourcing the pool set from the registry instead of fromMARKET_LEGS.Reference agents (required)
A capability nobody uses is not exercised, and this one needs both sides — a market that gets created and a market that gets found. Ship
example/agents/market-launcher/carrying bothagent.tsandprompt.md(every bundled agent provides both ways of running, ADR 0015 §2), plus a roster entry inconfig/example.yaml.prompt.mdcannot be a stub. The committedconfig/example.yamlroster ships trading agents in prompt mode, so the Quick Start default drives this through the LLM path. The prompt must describe the registry observation fields and the decisions they support well enough that a model can act on them without readingagent.ts.market-launcher(new) — create a pool through a factory, seed it, and manage it: pick a pair and fee tier the environment does not already provide, size the initial liquidity, and decide when to pull. Exercises the creation side and, in phase 3, the rug side.discovery-arb/discovery-arb-verify(extend the existing ADR 0014 agents) — source candidates from the registry rather than only from the vuln factory, so the discovery and verification sides are exercised against agent-created markets too. Theverifiedflag is exactly the signaldiscovery-arb-verifyshould gate on.Phases
MarketRegistry+ Uniswap V3 factory detection + observation exposure +createPoolaction + registry-authoritative scoring.kind=unknown,verified=false), agent-deployed ERC-20 listing, and rug pulls as a sanctioned strategy.Open points
name()/symbol()/decimals()and accepting the heuristic, or only recording a token once a known factory pairs it into a pool.check:strategysurface. Deployment is currently unrestricted by the cheatcode static check. If arbitrary deployment becomes a first-class capability, the entry gate should state explicitly what is and is not allowed rather than leaving it implicit.Roadmap position — steps 4 and 6 of 7
Blocked by #41 (positions outside the registered market set score as zero). Registry-authoritative scoring is a special case of the adapter-driven historical valuation proposed there, and the realizable-exit marking this issue relies on is the same principle #41 needs for #38 and #39.
This issue splits across the sequence: phase 1 is step 4, once #41 has landed and #38 has established the realizable-exit pattern. Phases 2–3 are step 6, last, because arbitrary deployment and sanctioned rug pulls open the largest design surface and will generate follow-on rule decisions.
Independent tracks: #33 / #35 / #36 (own chain — nothing here is chain-specific, so no ordering constraint), #20 → #34 (strategy). #31 / #32 (observability) should not land before #41.
Design details to be worked out at implementation time.