devlog
LIVE

Build journal, in public.

Reverse-chronological log of what shipped, what broke, what we learned. Every entry pairs the work with onchain proof — transaction hashes, contract addresses, vault NAVs. Canonical source at contracts/DEVLOG.md.

2026-05-31

Bet-as-collateral lending · built, reviewed, held back on a critical

Took a real run at the original cirBTC-era idea: borrow USDC against a prediction-market position you already hold — the "locked betting capital becomes liquid" primitive. Built two contracts, then an adversarial review caught a critical that we're choosing not to ship around. Logging it honestly.

Shipped: MarketsV3 share-transfer primitive

v2 prediction-market shares were non-transferable (pure msg.sender ledgers), so a position couldn't be used as collateral by any other contract. MarketsV3 inherits Markets v2 unchanged and adds an ERC-20-allowance-style operator model (setShareOperator + transferSharesFrom) so a lending contract can custody a position. Sibling deploy, no migration — v2 markets keep running. 7 tests, clean review. This primitive is reusable and stands on its own.

Built but NOT deployed: CirqueBetLending

Lends USDC against a held YES/NO position, marked at the live AMM price. It solves the famous "cliff-payoff" problem (a share trades at 60¢ but resolves to exactly $1 or $0) with a force-close window: no loan may survive into resolution — anyone can liquidate in the final 2h regardless of health. That mechanism works and is proven by tests.

But the adversarial review found a CRITICAL: marking binary collateral at the instantaneous AMM mid is manipulable on a thin pool. In one transaction an attacker could buy the opposite outcome to inflate their collateral's mark, borrow against the inflated value, and unwind — over-borrowing for only the ~0.7% round-trip fee. Spot-marking binary collateral on a thin CPMM is unsafe at any LTV.

So we're holding it back, not patching around it. A safe version needs a manipulation-resistant mark (TWAP + depth-relative borrow caps), an incentivised force-close keeper, and bad-debt accounting — a v0.6 research track, not a same-day fix. The contract stays in-tree as a clearly-marked research reference (the known issues are written into its header); it is not deployed and not wired to the app. Shipping an exploitable lending contract to look busy would be the wrong call.

147 contract tests pass (incl. the cliff-guard proofs). The honest status: the hard mechanism is solved; the safe collateral mark is the open problem.

2026-05-27

Cirque v0.5 beta live · atomic leverage-and-bet · reviewed + redeployed

The headline cirBTC primitive is live on Arc testnet: lock cirBTC, borrow USDC, and buy YES/NO shares on a Registrai market in a single transaction. The borrowed USDC never touches your wallet — it goes straight into the bet. New CirqueLending at 0x2dd7bc570e876499422b8185dbb04c4b134cd504, source-verified, pool seeded.

Position lifecycle

  • leverageAndBet — open: lock cirBTC + borrow + buy, atomic
  • closePosition — sell shares while trading, settle from proceeds
  • redeemAtExpiry — after resolution: winning shares redeem at 1 USDC each; losing bet, cover the debt to reclaim cirBTC
  • On liquidation, the position forfeits to the treasury (closes the moral-hazard hole where a bettor could let cirBTC liquidate yet keep a winning bet)

Full-power review before shipping

Three parallel adversarial reviews (accounting / security / edge-cases) ran against the new code. Three real findings, all fixed before deploy:

  • Redeem-escrow commingling (critical): with multiple winners on one market, the first redeemer pulled everyone's winnings into the commingled balance, which could misprice supplier shares. Fixed with a per-marketredeemPot escrow excluded from pool value.
  • Never-resolve lockup (high): a market that expired but never resolved could trap a borrower's collateral forever. Fixed with an escape hatch.
  • Treasury-sweep sandwich (medium): restricted sweeps to resolved markets (redeem at $1, zero slippage).

131 contract tests pass. Two findings deferred to external audit with written rationale (first-depositor share inflation — griefing-only, capped; stale-oracle liquidation halt — deliberate). UI to expose the one-click flow lands next.

2026-05-26

oracle-primitives extracted as a standalone fork-target

Pulled the bonded-agent oracle layer into its own MIT-licensed repository at registrai-multichain/oracle-primitives — six audited Solidity primitives, stripped of every app-layer dependency, ready for any Arc builder to fork.

What got extracted

  • Registry.sol — permissionless feed + bonded agent registration
  • Attestation.sol — value writes + dispute state machine
  • Dispute.sol — counter-bond + slashing
  • AgentIdentity.sol — global per-address profile
  • rules/MedianRule.sol · rules/TrimmedMeanRule.sol — verifiable onchain aggregation
  • Foundry tests (65 passing), single-shot deploy script, two usage examples

What got stripped from the primitives

The deployed v2 stack integrates with RegistraiPoints via optional hooks in Registry.setPoints() and Attestation.setPoints(). Those hooks make the deployed bytecode "app-aware" — useful for the live credit system, noise for a clean fork target. Removed entirely in the primitives repo. Same with PointsValues constants. The primitives are now zero-coupling to any specific application.

Why this matters

Arc OSS Showcase asks specifically for "standalone, infra-focused repos that other builders can pick up." The full registrai-multichain/contracts repo has Markets, Cirque lending, and the credit system bundled — the full production app, not the primitive. Extracting the oracle layer as its own repo answers the showcase's exact criteria and gives forkers a 870-line surface to work with instead of a 3000-line one.

Pre-tag audit pass

Caught one Solidity warning (unused verifiable parameter + dead wasSlashable local — vestigial from the Points strip) and cleaned both before tagging v0.1.0. Repo annotated with 8 topics (arc-network, oracle, defi, solidity, foundry, prediction-markets, circle-arc, usdc) for discoverability. All 65 tests still pass post-cleanup.

2026-05-25

Audit pass · CirqueLending + AttestedBTCOracle redeployed · admin escape hatch removed

Pre-submission audit pass surfaced one critical + one high-priority finding worth fixing before the hackathon submission window closed. Both shipped as a coordinated redeploy.

C1 · adminWithdrawUSDC removed

The previous CirqueLending exposed an owner-only adminWithdrawUSDC(amount) with no balance check, documented as an "alpha-only escape hatch." Real exposure is small (10 USDC TVL) but it's a hard centralization vector: compromise of the owner key would drain the entire pool including supplier funds and locked collateral USDC. Removed entirely. The treasury supplies via the same supplyUSDC() as any LP and withdraws via the same withdrawUSDC(). No special privilege.

H1 · AttestedBTCOracle MAX_LOOKBACK bumped 16 → 100

If 16 consecutive most-recent attestations were all in DisputeStatus.Pending, the adapter reverted — theoretical DoS vector. Each dispute requires a counter-bond (economically expensive), but the lookback ceiling provides cheap extra headroom. 100 attestations is comfortable; gas cost stays bounded.

F4 · methodology textarea locked during submission

The /agents/create form's methodology textarea is now disabled while a registration tx is in flight. Previously, a user could edit the text after the methodologyHash had been computed for the createFeed call but before the tx mined — the worker's signature-gated methodology-save would then reject the edited text with a hash mismatch.

Redeploy addresses

Old contracts (0x1acc24d0… oracle, 0x8384690d… lending) are now orphaned but still on-chain. Deployer's 10 USDC was withdrawn from the old pool and re-seeded into the new one before cutover. Worker keeper secret CIRQUE_LENDING_ADDR updated + worker redeployed; cron continues firing every 30 min.

Findings deferred to v0.5 beta

  • F5 · social-oracle KV consistency race — narrow window (~5s), low real risk on testnet; would require Cloudflare Durable Objects refactor to truly fix
  • F6 · withdraw interest-distribution rounding — analysis showed the math self-corrects on borrower repay, no fund loss; cosmetic accounting drift only
2026-05-23

CirqueLending two-sided · USDC suppliers earn yield · live & verified

v0.5 alpha now fully two-sided. Anyone can supply USDC and earn yield from cirBTC-collateralised borrowers. Interest accrues to suppliers via share-price appreciation — no claim step needed. Re-deployed:

Both sides of the lending mechanism

Supply side: supplyUSDC(amount) pulls USDC into the pool and mints proportional shares.withdrawUSDC(shares) burns shares and returns USDC at the current per-share value (principal + accrued interest). Withdrawals respect utilisation — if the pool's idle USDC is currently lent out, withdraw queues until borrowers repay.

Borrow side: borrow(cirBTC, USDC) locks cirBTC as collateral and draws USDC at 5% APY flat.repay() returns principal + interest in USDC and unlocks the collateral. Interest flows back into the pool as accrued value — every supplier's shares appreciate proportionally.

Live verification

End-to-end supply cycle verified on Arc testnet:

  • Deployer supplied 20 USDC → shares = 20e6, balanceOfUSDC = 20e6
  • Deployer withdrew 10 shares → USDC out = 10e6 (1:1 since no borrows yet) ✓
  • Pool state correctly reflects the partial withdraw: totalShares = 10e6, half remains as supplier position ✓

Borrow side verified in 18 contract unit tests (full repo: 119 pass, 0 fail). Live borrow requires cirBTC from faucet.circle.com — interactive faucet step is up to the user.

Accounting model

Standard Compound/Aave-style share accounting:

  • Pool value = idle_USDC + outstanding_principal + accrued_unrealized_interest
  • Share price = pool_value / total_shares
  • On every supply / withdraw / borrow / repay / liquidate, _rollAccrual() rolls interest forward proportional to elapsed time and outstanding principal — so share value reflects real-time yield without iterating loans.
  • Per-user caps for alpha: 1 cirBTC collateral, 1,000 USDC supply. Lifted in v0.5 beta after audit.

What changed vs the earlier v0.5 alpha

Original v0.5 alpha had only the borrow side (USDC pool seeded by treasury, no public supply). Today's deploy adds the full lender side properly — with share accounting that avoids the double-counting bug surfaced during the previous self-audit. The old seedUSDC admin path is gone; treasury (and everyone else) supplies via supplyUSDC, holds the same shares as any other LP, and earns the same yield. Interest no longer routes to a treasury wallet — it stays in the pool to compound for suppliers.

2026-05-23

CirqueLending v0.5 alpha live on Arc testnet · dogfooded oracle

v0.5 alpha is deployed. The lending contract reads BTC prices from Registrai's own bonded-agent attestation layer — we eat our own dogfood instead of trusting an external keeper key.

Addresses

Why dogfooded

Original plan: owner-set oracle, Cloudflare Worker pushes prices every 5 minutes. Simple, but: hot key in production, single point of failure, no slash-on-bad-data property. Replaced with a bonded oracle agent on Registry v2 — same trust model as every other Registrai feed, just used internally by the lending product. If we attest a bad BTC price, anyone with 25 USDC can dispute and slash us. We hold ourselves to the same standard as our public-facing oracles.

cirBTC integrity probes — defense against Circle-side compromise

Using cirBTC means inheriting Circle's security. We don't blindly trust it — before every attestation the keeper runs four onchain probes:

  • cirBTC.paused() must be false — Circle hasn't halted transfers.
  • cirBTC.owner() must match the expected admin — no covert admin rotation.
  • cirBTC.totalSupply() growth ≤ 50% per 30-min cycle — abnormal mint = potential exploit.
  • cirBTC.isBlacklisted(CirqueLending) must be false — Circle hasn't frozen our pool.

Any failure halts attestation. After 1 hour of no fresh price (MAX_ORACLE_STALENESS), the lending contract refuses new borrows and liquidations until integrity restored. Repays still work — borrowers are never trapped.

What's seeded, what's next

Pool seeded with 100 USDC from the treasury (small for testnet alpha; bounds blast radius if anything goes wrong). cirBTC collateral must come from faucet.circle.com — that step is up to the user. UI surface comes in Phase 3 (supply / borrow / repay / health). External audit gates mainnet (Q4 2026).

What we're NOT claiming yet

  • This is testnet alpha. Do not deposit real funds.
  • No public marketing tweet until the UI ships AND we have integrity probe data showing the keeper is reliable across a multi-day window.
  • The atomic borrow-and-bet flow (v0.5 beta) is still deferred until per-user share custody is designed correctly.
2026-05-22

CirqueLending v0.5 alpha · self-audited pre-deployment · 3 findings, 3 fixes

Wrote the first version of CirqueLending.sol — the v0.5 lending primitive that lets users borrow USDC against cirBTC collateral and use the proceeds to bet on Registrai markets. Before pushing anywhere near a testnet, ran an internal audit pass against the freshly-written code. Three findings, all fixed before this entry shipped:

C1 · supply() drained borrower collateral (critical)

A _totalCollateral() placeholder returning 0 meant withdraw() treated the entire contract balance as free supply. A cirBTC supplier could withdraw funds that other users had posted as borrow collateral — borrowers later failed to repay() because the contract had no cirBTC to return. Fix: removed the supply / withdraw surface entirely. v0.5 alpha is a pure collateral-locking product. The proper two-sided supply pool (USDC lenders earning yield from borrower interest) ships in v0.6 with the running collateral counter done right.

C2 · supply yield was advertised but had no source (critical)

The website said "supply cirBTC, earn yield from leveraged bettors." The contract had no mechanism connecting cirBTC deposits to any income stream — USDC borrowers paid USDC interest to the treasury, never to cirBTC suppliers. False advertising, even on testnet, is corrosive to trust. Fix: dropped the supply framing from the homepage, vault page, and roadmap. v0.5 leads with the borrower-side value (lever cirBTC into bets); v0.6 introduces two-sided yield with the supplier side actually wired up.

H1 · liquidation seized the entire collateral (high)

At the 65% LTV liquidation trigger, a $22k debt was backed by ~$34k of cirBTC. The first pass handed all $34k to the liquidator for paying $22k — a 54% profit on the transaction, with the borrower losing everything. Fix: liquidator now receives only (debt + interest) × (1 + 5% bonus) worth of cirBTC at the oracle price; the remainder refunds to the borrower. If BTC has crashed below the debt value, the liquidator takes all collateral and the protocol absorbs the shortfall as bad debt — properly accounted, no surprise to the borrower.

H2 · oracle had no staleness check (high)

The v0.5 alpha BTC oracle is owner-set — a centralized keeper calls setPrice on a cadence. If the keeper crashes for 24 hours, the stored price stays stuck while real BTC moves 30%. Underwater loans would look healthy on paper; liquidators can't act. Fix: the oracle interface now returns (price, updatedAt); the lending contract refuses to borrow or liquidate when block.timestamp − updatedAt > 1 hour. Repays deliberately skip the staleness check — borrowers can always close a position regardless of oracle health.

Verification

17 contract tests written against the corrected logic. Full repo: 114 tests pass, 0 fail. The contract is still not deployed — Phase 2 (liquidator bot + deploy script) ships next; Phase 5 (third-party audit) gates mainnet. Disclosing internal findings publicly because the same process scales to v0.6 / v0.7: write, audit own work, document before any external review.

2026-05-22

Methodology · paste-not-link · MethodologyLive on every feed page

The agent-creation form used to ask for an ipfs://… CID or URL — meaning new agent deployers had to host a markdown doc somewhere just to claim a methodology hash. Today the field is a 9-row textarea with a starter scaffold the user overwrites in place. Sources, math, cadence, controls — all four sections required, all four hashed onchain. Validation refuses to submit if the template is unedited.

On successful registration, the text is signature-gated and POSTed to a new Worker endpoint /feed-methodology. The Worker double-verifies: (1) signer recovers to Registry.getFeed(feedId).creatoracross v2 → v1.1 → v1.0, and (2) keccak256(text) === onchain methodologyHash. The KV can never drift from chain — a forged or modified text submission is rejected with a 400 explaining the hash mismatch.

MethodologyLive — three render branches

A new client component <MethodologyLive /> resolves the methodology in this order, on both the /feed/[feedId] page and the post-registration success panel:

  1. Worker has the text → render the prose in a monospace block with caption creator-supplied · onchain hash · signed by feed creator.
  2. Connected wallet === feed creator AND localStorage has an unsaved methodology → render a publish methodology retry button. Signs the same EIP-191 message, POSTs, refetches, clears localStorage on success.
  3. Per-feed fallback URL (legacy seeded feeds — Warsaw/CPI/ECB each have a methodologyDoc field in live-data.json) → render the GitHub link.

Retry safety net

The methodology is written to localStorage before the Worker POST is attempted. If the user cancels the signature, the Worker is unreachable, or the tab crashes, the text survives. Next visit to /feed/{feedId} surfaces the retry button. Worker writes succeed → localStorage cleared automatically.

Next on the roadmap

v0.3 — long-tail FX feeds (NGN/USDC, TRY/USDC, ARS/USDC) for emerging-market currency markets. v0.5 — borrow USDC against cirBTC collateral, lever into Registrai markets. No BTC sell, no taxable event, no exit from the asset. v0.5 beta adds the atomic borrow-and-bet bundling (one transaction, one signature) — something Aave can't replicate because they don't own the markets contract. v0.6 opens the two-sided pool with USDC suppliers earning yield from borrower interest. Engineering starts now; mainnet launch gated on audit (Q4 2026).

2026-05-21

Social Signal Oracle live · Twitter quests · paste-first UX

The credit system needed a way to mint for off-chain proofs (Twitter ownership, etc.) without breaking the "all credits are minted by bonded oracle agents" story. Today: a new first-party Registrai agent at 0xf26d…6263 — bonded with 10 USDC slashable, registered on Registry v2 against a new feed "Registrai social engagement signals". Same trust model as Warsaw, CPI, ECB.

Quests live

  • Connect Twitter (+50 pts) — user posts a wallet-bound challenge tweet. Worker fetches it via Twitter's public oEmbed endpoint (no API key, no auth, no rate-limit cost), extracts the author handle from author_url, parses the first <p> of the rendered html to stop quote-tweet replays, and binds handle ↔ wallet one-to-one in KV.
  • Tweet about your agent (+150 pts) — same oEmbed verification, plus a chain check via topic-filtered eth_getLogs that the wallet has at least one AgentRegistered event across v1.0 / v1.1 / v2. Returns 503 with retry on RPC failure (never a silent false-negative). Three rotating templates so the same quest doesn't produce identical-looking tweets at scale.

Key separation

The dedicated MINTER wallet 0xaE1A…F0AF is the only authorized minter on RegistraiPoints. The bonded social-oracle wallet had its minter role revoked. Compromise of the mint key = unlimited credit mint, bounded only by the per-call cap; compromise of the bond key = nothing (the mint key is what carries authority). Two keys, two failure modes, both bounded.

Worker-side hardening

  • Per-nonce KV keying (nonce:wallet:nonce) — two tabs each get their own valid pending nonce; the second /start no longer invalidates the first.
  • Sliding-window rate limit: 5 verify attempts per wallet per 10 minutes.
  • AbortController + 30s timeout on every quest fetch, matching Cloudflare's own server cap.
  • Per-wallet-address reset on the frontend — switching MetaMask account wipes flow state so a stale nonce/template can't follow you to a different wallet.

Paste-first UX

Quest panels auto-fetch templates on mount (no "start" button). The paste-URL input is the primary action; templates live in a collapsed "need a tweet? show me a suggestion ↓" disclosure with copy-to-clipboard, intent-URL composer, and a shuffle button to cycle through variants. End-to-end: two interactions per quest — paste, verify.

2026-05-20

v2 stack shipped · onchain soulbound credits · audit fixes

Six new contracts, deployed and source-verified on ArcScan in a single migration. Old v1.0 + v1.1 stay running unchanged; v2 is the write target for every new agent registration, market, and trade.

Onchain credits, live

A soulbound credit layer that mints on every protocol action. Non-transferable, read directly from chain via RegistraiPoints.points(address). No backend, no API, no off-chain ledger.

test
iterations
result
register an oracle agent
+1,000 pts
one-time per feed
create a prediction market
+200 pts
per market
attest a data point
+50 pts
per attestation, dispute-free
trade
+10 pts / USDC
capped 500 pts / day / wallet
resolve a market
+25 pts → agent
rewards the oracle, not the keeper
slashed attestation
−300 pts
deducted on ResolvedInvalid

Audit fixes baked into v2

  • ReentrancyGuard on every fund-moving function in Markets (buy, sell, resolve, redeem, claimLP, createMarket, addLiquidity) — the points sub-call is an external call to an arbitrary minter contract; reentrancy guard closes the obvious vector.
  • Strict state machine on Attestation.setStatus — only Pending → ResolvedValid|ResolvedInvalid permitted; slash idempotent (only debits when the prior state was Pending).
  • Feed-creator-must-be-agent rule baked into Registry._register — couples spec authorship and data quality to one accountable bonded wallet. No more Case-B where someone else attests against your feed.
  • awardFlat per-call cap (10_000 pts) so a compromised minter can't mint unlimited credits in a single tx.
  • Buy uses effectiveIn (post-fee), sell uses grossOut (pre-fee) for points — symmetric accounting so two-way volume is measured consistently.
  • maxUint256 approval on every USDC approve the UI sends — second feed / second market / second trade skips the approve popup.

Profile updates

New Quests tab on /profile (the default — fresh wallets land here, not on empty trader/creator/deployer). CreditsBanner reads the live balance from chain. DeployerTab now discovers v2 agents via topic-filtered eth_getLogs across all three Registry deployments — a wallet that registers on v2 sees its agent in "deployer" immediately after the tx confirms.

2026-05-17

Markets v1.1 · verifiable markets live · Circle-product roadmap

Yesterday's v1.1 trio hosted the verifiable Warsaw feed but had no markets layer — Markets v1.0 is immutable-coupled to Attestation v1.0, so trades against the verifiable feed weren't possible. Today's Markets v1.1 closes that.

  • Markets_v1_1: 0xec70ce17aa4b0da6898ced47621655c4c31b1136
  • Same bytecode as v1.0, pointing at Attestation v1.1.
  • Three demo markets seeded against the verifiable Warsaw feed: >17,000, >17,500, and <18,000 PLN/sqm by expiry. Each visible on the /markets grid with a verifiable badge that clicks through to the rule bytecode on ArcScan.

The verifiable demo loop is now complete end-to-end: agent fetches Otodom listings → submits raw int256s via attestWithRule → MedianRule computes 17,371 onchain → markets resolve against that value at expiry → traders redeem at $1 per winning share. Every step is bytecode anyone can verify.

Roadmap commitments for the Circle Developer Grant

Re-prioritised against the May 14, 2026 grant relaunch — Circle named prediction markets, stablecoin FX, and agentic economic activity as priority verticals.

test
iterations
result
v0.3 · Long-tail FX feeds
verifiable NGN/USDC, BRL/USDC, TRY/USDC, KES/USDC… rate feeds
Stablecoin FX (priority vertical)
v0.4 · Programmable Wallets
external agents onboard via Circle hosted wallets
Circle Programmable Wallets
v0.5 · CCTP
bring USDC from Ethereum/Base to attest on Arc
Circle CCTP
v0.6 · BoundedScalarRule
range guards + max-step-bps for slow feeds
v0.7 · Phala TEE attestation
TEE-attested data-fetch closes the trust loop

Complete moat story with this stack: aggregation as bytecode, data-fetch as TEE-attested execution, identity as Circle Programmable Wallets, capital as CCTP-bridged USDC. Long-tail FX feeds make the protocol immediately useful to existing Arc-native FX products without forcing them to build their own oracle plumbing.

2026-05-16

Verifiable agents end-to-end live — first rule-bound attestation onchain

Shipped end to end on the same day as the design. The off-chain agent process used to be trusted to (a) fetch honest data and (b) compute the right value from it. The bond + slash mechanism deterred lying, but the math itself was opaque. As of this commit, the math is verifiable bytecode anyone can read.

What landed today

  • IAgentRule.sol + MedianRule + TrimmedMeanRule(1000) deployed: 0x415fb74629d8eab51b7991679cec6cb71f3fb997 and 0x772a40fee7b51542cf09c8c26c9e7b786d162a70
  • Registry.registerAgentWithRule(...) parallel to registerAgent — backward compatible; existing agents unchanged
  • Attestation.attestWithRule(feedId, int256[] rawInputs) reads the agent's bound rule, computes the value via rule.submit(rawInputs), stores the result, and emits an Attested event with inputHash = keccak256(abi.encode(rawInputs)) — anyone watching the chain can re-derive the input vector and re-call the rule to confirm
  • 25 new contract tests (including a 256-run fuzz over MedianRule). Full suite 86/86 green
  • @registrai/[email protected] adds the rule-bound path. defineAgent({ rule: '0x…' }) switches run()'s return shape from { value, inputHash } to { rawInputs }; SDK calls attestWithRule under the hood and never computes the final value off-chain
  • /agents/create gained a rule picker — none / Median / Trimmed Mean 10% / Custom address. Success panel hands back a pre-filled SDK snippet for the chosen rule

The invariant the protocol now guarantees for any rule-bound agent: pull inputHash from the Attested event → reconstruct rawInputs from the attest tx calldata → re-call rule.submit(rawInputs) yourself → confirm the stored value matches. Aggregation math is no longer trust-by-markdown.

Then deployed v1.1 + migrated Warsaw

Registry/Attestation/Dispute v1.1 deployed alongside v1.0 to host rule-bound agents — Registry v1.1 at 0x4e074806ce7b8bcee27c14fd446d924179aa919e, Attestation v1.1 at 0xf0caf69125bd17717c4804edce61bbdacd52ac60. v1.0 keeps running for the existing agents/markets/vault; Markets v1.0 can't resolve against v1.1 feeds (immutable coupling), so Markets v1.1 is the next milestone.

A new feed WARSAW_RESI_MEDIAN_VERIFIABLE went live on v1.1 with MedianRule bound. The Warsaw agent worker now runs both a plain attestation (v1.0, government-anchored) and a verifiable one (v1.1, raw market median onchain). First live attestWithRule call:

  • tx 0xce87ee21b487b4
  • 148 Otodom listings fetched → 134 retained after 5% trim → 128 inputs to MedianRule
  • onchain median: 17,371 PLN/sqm
  • inputHash committed — anyone can re-derive rawInputs from the attest calldata and re-call MedianRule.submit to reproduce 17,371 byte-for-byte

The verifiable feed intentionally drops the NBP-anchor calibration the v1.0 feed used — v1.0 anchored to a Polish- government figure, which moved the trust off-chain. v1.1 trusts the market median itself, computed deterministically onchain from raw listings. Different methodology, both live.

Next on this milestone

  • Markets v1.1 — so markets can resolve against verifiable feeds
  • BoundedScalarRule (range guards + max-step-bps)
  • Phala TEE attestation for the data-fetch half — closes the trust loop end to end
2026-05-15

Market-maker vault + end-to-end resolve test, live

Shipped MarketMakerVault.sol — pooled USDC for operator-driven liquidity bootstrapping. Anyone deposits and burns shares against current NAV; an authorized operator routes funds into buys, sells, and add-liquidity calls on Markets; resolved winnings flow back into the vault and depositors claim pro-rata. v1 NAV is conservative (USDC balance only) — immune to AMM-state sandwich attacks. 1e6 virtual offset neuters the classic first-depositor share-inflation attack. 9 new contract tests, full suite 61/61 green.

Deployed at 0x79F09d46dA4cA607f8805930778fBfFDAad0E9D8, seeded with 50 USDC from the treasury. New /vault page on the site has the live NAV, share price, your position, and a deposit/withdraw form. MM bot now signs through the vault — every trade originates from the vault address onchain.

Operator/agent separation — the MM operator is wallet 0x9FB5…6959, distinct from the oracle agent wallet 0x84C7…2E5e. Oracle agents do not trade markets keyed to their own attestations.

Onchain stress + invariant tests

test
iterations
result
MM stress (price-target convergence)
20 / 11 trades
0 invariant failures, AMM k strictly grew per buy, model converged within 5pp spread tolerance
Sell-side (vault.executeSell)
5 / 5 sells
0 failures, NAV+ and AMM k+ on every iter
End-to-end resolve
1 full cycle, ~1h wall-clock
PnL +0.4482 USDC on 0.5 USDC YES bet — matched theory exactly

End-to-end resolve test breakdown

~1 hour wall-clock, all real onchain transactions on Arc testnet:

  1. createFeed — minimum allowed dispute window (1h)
  2. approve + registerAgent — 10 USDC bond locked
  3. attest(17500) — finalizes at +1h
  4. createMarket(threshold=17000, GreaterThan, 5 USDC seed) — market 0x7c06d272b32dcb
  5. vault.executeBuy(YES, 0.5 USDC) → 0.9482 YES shares acquired at price ~0.527
  6. wait for finalization + market expiry
  7. resolve — YES won (17500 > 17000)
  8. vault.redeem — winnings flow back to vault

Vault NAV moved 45.1688 → 45.6170 USDC. Profit 0.9482 × $1 − 0.5 = $0.4482, matching exactly.

2026-05-14

SDK published to npm + agent repo cutover

Published @registrai/agent-sdk v0.1.0 to npm — 17 kB tarball, 33 files, public scope claimed. Runtime-agnostic (Node, Cloudflare Workers, Phala TEE). Surface: defineAgent, Agent, preflight, submitAttestation, median, trimByPercentile, hashRecords, fetchText, fetchJson, log, plus minimal viem-compatible ABIs.

Cut the agent repo over from its duplicated src/sdk/ copy to consume the npm package. Net delta: −477 LOC, +27 LOC. Worker bundles cleanly, tsc clean, 13/13 tests pass.

Audited every page on the site so beta/soon status labels match reality. Added the missing per-page badge on feed detail pages.

2026-05-13

Macro agents + Markets v5 with LP shares & fees

Shipped two new first-party oracle agents, both Cloudflare-Worker cron-driven on the daily 14:00 UTC tick:

  • Polish CPI Y/Y in basis points — sourced from GUS's official monthly inflation reports
  • ECB main refinancing operations rate in basis points — sourced from ECB's Statistical Data Warehouse

Each agent ships with its own methodology document hashed and pinned for verification.

Markets v5 — fundamentally upgraded contract:

  • 70 bps trading fee per trade, split 40 / 20 / 10 to creator / agent / treasury. Oracle layer remains free; revenue only on Markets.
  • LP sharesaddLiquidity mints proportional shares against current reserves (Polymarket-style: preserves pool odds rather than drifting toward 50/50).
  • claimLP — after resolution, LPs withdraw their share of the snapshotted winning-side reserve.
  • redeem() decoupled from reserves — user outcome balances and pool reserves are separate buckets. Burning user shares no longer drains the LP pot.

7 markets seeded across the 3 feeds, all live.

Notable engineering: fixed stack-too-deep in Markets.sell by enabling via_ir + optimizer = true. The viaIR pass surfaced a latent test bug folding two block.timestamp reads into one local; fixed with explicit vm.warp(literal). First attempt at proportional addLiquidity drifted price toward 50/50 when reserves were imbalanced — fixed with Polymarket-style residual share allocation.

2026-05-11 to 2026-05-12

Bootstrap on Arc testnet

Initial contracts deployed to Arc testnet (chain id 5042002):

First feed: Warsaw residential PLN/sqm. First-party agent registered, bonded, attesting daily at 14:00 UTC. Frontend at registrai.cc — Next.js 14 static export on Cloudflare Pages, viem for chain reads.

Subscribed? Watch the contracts repo on GitHub for new entries, or check back here.