Author: Zen Chen Date: 2026-05-12 Status: Pre-implementation design draft. Not yet stable, not yet shipped. Working title: Crucible. (Alternates: Crucible, Truss, Aegis, Veridian.) Predecessor: Built on top of Cadence (Arc402 protocol v0.1.0) as base payment layer.
Every existing AI-payment protocol — Stripe, Lightning, Coinbase x402, Circle Nanopayments, Cadence v0.1 — treats AI service calls as deterministic transactions: pay X, receive Y, transaction closed.
But AI services are probabilistic: outputs are stochastic, quality is subjective, value resolves over time. The protocols don't model this. Buyers have no recourse if the LLM returned garbage; sellers have no way to differentiate their quality.
Crucible is the missing settlement layer: payment is conditional on a prediction-market-resolved quality outcome, not on delivery alone.
Built right, this becomes the infrastructure layer for verifiable AI commerce — applicable to LLM outputs, code generation, translation, search, prediction services, data labeling, content moderation, and any market where "useful" is subjective but determinable.
The user (Zen Chen, Polymarket strategy researcher) has a unique competitive advantage: prediction-market mechanism design is his domain. This protocol is essentially "Polymarket-style consensus, applied to AI service quality."
| Protocol | Assumption | Why it breaks for AI |
|---|---|---|
| Stripe | Buyer knows what they want, seller delivers | AI outputs are non-deterministic, quality subjective |
| Lightning | Payment = delivery confirmation | No quality dimension at all |
| Coinbase x402 | Service responds 200 = paid | Bad outputs still return 200 |
| Circle Nanopayments | Same as x402, just on Arc | Same blind spot |
| Cadence v0.1 (our previous work) | Same as Nanopayments | Adds rep-tier + refundable design, but core still binary |
| ERC-8183 | Discrete evaluator-arbitrated job | Multi-tx, heavy, doesn't suit per-call streaming |
Common gap: none of them treat the outcome of the AI service as an economic object.
AI service quality is a subjective claim that markets are uniquely suited to resolve.
Polymarket has proved that liquid markets can produce trusted, real-time consensus on contentious facts. Apply this to AI:
- Service commits to a quality claim (off-chain text + on-chain hash)
- A short-lived market opens: "will this output be validated as meeting the claim?"
- Validators stake on YES / NO
- Resolution unlocks payment proportional to outcome
This is not novel as a primitive (UMA, Kleros, Augur all do market-based oracles). What's novel is:
- Per-call granularity: micro-markets opening per API call, settling fast
- AI-specific resolution paths: pluggable resolvers for code (testcase), translation (validator vote), prediction (real-world data via oracle), inference (ZK-ML or TEE)
- Integrated payment layer: market outcome directly drives token flow without an extra arbitration round
- Composability with existing payment layers: works alongside Cadence / Nanopayments / x402 as a settlement enhancement, not a replacement
| Role | Purpose | Bond / Stake | Reward |
|---|---|---|---|
| Agent | Pays for AI service, optionally disputes | USDC in escrow | Refund if service fails quality |
| Service | Provides AI output, commits to quality claim | Quality bond (USDC) | Payment if claim verified |
| Validator | Stakes on YES/NO of service's claim | Validator stake (USDC) | Share of market fee + winning stake redistribution |
| Resolver | Pluggable verification logic (contract) | None (pure code) | None (or small gas reimbursement) |
| Disputer | Challenges automatic resolution within window | Dispute bond (USDC) | Wins if dispute upheld |
Anyone can be any role. No permissioning. No KYC.
Agent Service Crucible Contracts Validators
| | | |
|--(1) HTTP POST----->| | |
| | | |
|<--(2) HTTP 402------| | |
| + requirements | | |
| | | |
|--(3) signed claim-->| | |
| | | |
|<-(4) output+claim---| | |
| + commitmentHash | | |
| | | |
| |--(5) openMarket------>| |
| | (commitmentHash, | |
| | quality criteria, | |
| | resolveDeadline) | |
| | | |
| | |<--(6) stake YES/NO-------|
| | | |
|--(7) optional dispute---------------------->| |
| (bond) | |
| | | |
| | |--(8) resolver.resolve() |
| | | → quality score 0-10000 |
| | | |
| |<--(9) payout---------|----------------------> ↓ |
| | (score-weighted) | (rewards / slashes) |
| | | |
| ↑ refund if score < threshold | |
Each step is one or zero on-chain transactions. The Cadence base payment layer handles steps 1-3. Crucible adds 5-9.
crucible/contracts/src/
├── CrucibleRegistry.sol # services register, declare resolver, deposit bond
├── CommitmentVault.sol # services post output commitment hashes
├── QualityMarket.sol # the per-call prediction market
├── MarketFactory.sol # opens new markets per commitment (deterministic addresses)
├── ResolutionEngine.sol # adapter that calls a resolver contract & writes results
├── ValidatorVault.sol # validators deposit stake + claim rewards
├── DisputeResolver.sol # handles user-raised disputes (UMA-style optimistic)
├── ReputationWriter.sol # writes outcomes back to ERC-8004 ReputationRegistry
└── resolvers/
├── IResolver.sol # interface
├── TestcaseResolver.sol # for code generation (run testcases on chain)
├── OracleResolver.sol # for predictions (Chainlink, Pyth, etc.)
├── ValidatorVoteResolver.sol # for subjective tasks (validator network votes)
├── TEEResolver.sol # for compute integrity (trusts TEE attestation)
└── ZkMlResolver.sol # (future) ZK proof of AI inference
Key invariants:
- No upgrade proxy. Migration via new domain version (same pattern as Cadence V1→V2).
- No admin keys. No pause function.
- Resolver contracts are pluggable but each is immutable once registered.
- Service must pre-commit to which resolver applies to its output.
This is the protocol's flexibility: different AI tasks need different verification.
- Service registers a set of testcase commitments at service-creation time
- For each output, agent provides input; resolver checks if output matches expected
- 100% on-chain, deterministic, fast
- Use cases: code generation, math, deterministic data transformations
- For predictions of real-world events
- Pulls from Chainlink / Pyth / UMA at resolveDeadline
- 100% on-chain, deterministic at resolution time
- Use cases: price predictions, election outcomes, sports, any oracle-resolvable fact
- Validators stake to vote YES/NO on quality
- Schelling-point game: honest validators converge on truth
- 100-1000 ms resolution time after votes collected
- Use cases: translation quality, content moderation, creative writing assessment, subjective code review
- Service runs inference in TEE (Intel SGX, AWS Nitro)
- TEE produces attestation that inference was correct + input/output were as claimed
- Verifies attestation on chain
- Use cases: private inference, "we ran exactly the model we said we ran" claims
- Service produces ZK proof that inference matched stated model + input
- Currently expensive (proof generation > 30s for medium models); will improve
- Use cases: long-term — when ZK ML matures, replaces TEE for trust-minimized scenarios
The protocol does not pick winners. Services choose their resolver; agents see the choice and decide whether to trust it. Market dynamics select for trustworthy combinations.
For ValidatorVoteResolver, validators are the crux. Game theory must work or the whole protocol fails.
- Validator deposits ≥ minStake USDC into ValidatorVault
- Stake locks for cooldown period (e.g., 7 days) on any withdrawal
- Total stake determines vote weight
- Per market: validator votes (YES, score 0-10000) with stake-weighted weight
- Voting window: e.g., 60 seconds after market opens
- Validators can sample only some markets (not required to vote on all)
- Median or weighted-mean of votes = market resolution score
- Quorum required: at least N validators or M% of total stake participating
- Honest (vote within tolerance of resolution): receive share of market fee (collected from service's bond and the spread between agent's escrow and service's earned share)
- Dishonest (vote far from resolution): forfeit fraction of stake proportional to distance from consensus
- Absent: no penalty (validators don't have to vote on every market), but no fee earned
In v0, the validator network is thin. Options:
- Subsidy: protocol-treasury bootstrapped fee pool that pays validators above market-fee rate for first N months
- Validator-as-service: services with high quality bond can act as their own validators on their own markets (with disclosed conflict) — bootstraps participation
- Anchor validators: invite trusted parties (Circle, an audit firm, etc.) to seed the network with reputation
- Self-resolution: for v0, services can choose TestcaseResolver or OracleResolver if validator network too thin
The v0 MVP does not require a thriving validator network. It can ship with only TestcaseResolver and OracleResolver active. ValidatorVoteResolver is unlocked at v0.2 when economics make sense.
- Service deposits quality bond (e.g., 100 USDC or more — operator-defined)
- Bond is locked while service is registered; refundable on de-register after cooldown
- For high-stake claims, service can stake additional per-call bond on top of base bond
- Higher per-call bond = stronger commitment = market opens with prior that quality is high
- If market resolves score < some threshold (e.g., 3000 / 10000), service is slashed proportionally
- Slashed amount: split between agent (compensation) + validators (reward)
- Each resolved market emits a ReputationEvent to ERC-8004
- Service's reputation = function of historical scores
- Buyers see reputation before deciding to call
Agent flow is the same as Cadence v0.1:
- Pre-deposit USDC into PaymentEscrow
- Sign EIP-712 claim per call (now includes commitmentHash field, see §10.1)
- Payment held in escrow until market resolves
After resolution:
- score = resolveScore (0-10000)
- payout = claim.amount × (score / 10000)
- refund = claim.amount - payout
- (Plus: agent gets share of service's slashed bond if score is very low)
Crucible extends Cadence's Claim type:
CrucibleClaim(
address agent,
address service,
uint256 amount,
uint256 nonce,
uint256 expiry,
bytes32 commitmentHash, // hash of service's quality commitment
address resolver, // which resolver applies to this market
uint256 resolveDeadline // when the market resolves
)
EIP-712 domain: name="Crucible", version="1", verifyingContract=QualityMarket.
This is incompatible with Cadence's existing EIP-712 messages — by design (different protocol, different domain).
- Attacker creates many validator addresses with minimum stake
- Mitigation: stake-weighted voting; large stake = large weight; many small stakes = no individual influence
- Also: cooldown periods discourage churn
- Service bribes validator to vote YES on bad output
- Mitigation: economic — colluding validators forfeit stake when other honest validators vote NO; the cheaper the bribe, the smaller the stake at risk → smaller swing in resolution
- For small markets: protocol can require minimum N independent validators (Sybil-resistant) before resolution
- Attacker spams markets to drain validator attention
- Mitigation: market-opening fee paid by service; bots ignore unprofitable markets
- TestcaseResolver: services could pre-compute outputs that pass tests but are useless
- Mitigation: agents see test set before purchasing; can choose to not buy
- ValidatorVoteResolver: bribed validators (see 11.2)
- OracleResolver: oracle compromise — out of scope, inherited risk
- Agent disputes every call to delay payment
- Mitigation: dispute bond; loser of dispute forfeits bond
- Service collects payments, then exit-scams without delivering quality
- Mitigation: quality bond is locked > one-month cooldown; community can monitor and challenge during this window
Aim: ship in 6-8 weeks, full-time. Smaller scope is acceptable.
- CrucibleRegistry, CommitmentVault, MarketFactory, QualityMarket, ResolutionEngine, ReputationWriter
- TestcaseResolver (simplest, deterministic, no validators needed)
- OracleResolver (Chainlink/Pyth/UMA integration)
- TypeScript SDK to integrate from existing Cadence codebase
- Reference service example (a paid code-gen API using TestcaseResolver)
- 30+ tests including 2-3 invariant tests
- ValidatorVoteResolver
- ValidatorVault, DisputeResolver
- Game-theoretic stress tests
- Bootstrap validator program
- TEEResolver
- ZkMlResolver
- Composability with Cadence claims (use Cadence as base payment layer + Crucible on top)
- Cross-chain settlement via CCTP
| Protocol | What it does | How Crucible differs |
|---|---|---|
| UMA Optimistic Oracle | General-purpose subjective fact resolution | Crucible is AI-quality-specialized, pluggable resolvers, integrated payment |
| Kleros | Decentralized arbitration | Crucible is automatic via markets, not arbitration courts (faster, cheaper) |
| Chainlink | Push oracle for objective data | Crucible integrates Chainlink as one resolver type; protocol is the layer on top |
| Augur / Polymarket | Prediction markets | Crucible per-call markets are micro / short-lived / programmatically opened |
| Cadence (Arc402) | Payment escrow with batched settle | Cadence is the BASE PAYMENT LAYER. Crucible is the SETTLEMENT LAYER on top. |
| Circle Nanopayments | x402 on Arc | Crucible is orthogonal — could settle Nanopayments-pattern claims via Crucible markets |
| ERC-8183 | Discrete job escrow with evaluator | Similar evaluator concept, but 8183 is heavy per-job; Crucible is per-call |
| Akash / Bittensor | Compute marketplaces | Crucible is quality-resolution; could be the settlement layer for Akash compute |
Crucible's unique position: it's the FIRST protocol to combine (a) per-call AI service granularity + (b) market-resolved quality + (c) integrated payment layer + (d) pluggable resolution mechanisms.
Decisions still to make before code:
- EIP-712 vs EIP-3009 for CrucibleClaim — interop with Circle Gateway pattern?
- Market resolution math — median vs. weighted mean vs. Schelling-point?
- Quorum requirements — fixed N or % of total stake?
- Validator cooldown period — 7 days enough? 30 days too long?
- Bootstrap subsidy — protocol treasury? Or pure organic?
- Resolver registration — anyone can register a new resolver, or curated?
- Cross-resolver markets — can a single market use multiple resolvers? Weighted?
- Privacy — should agent identities be obscured? ZK proofs?
- Fee economics — what % of payments go to validator pool vs protocol treasury vs reduced for agent?
- MVP go-to-market — code-gen (clearest TestcaseResolver use case)? Or AI prediction services (clearest OracleResolver use case + Polymarket alignment)?
- Cadence is "Nanopayments-compatible OSS reference + 2 small primitives". Useful but not category-defining.
- Crucible is "the missing settlement layer for probabilistic services". This is a 10x bigger swing.
- Spec design — leverages user's prediction-market expertise
- v0 MVP scope (TestcaseResolver + OracleResolver only) avoids the hardest part (validator economics)
- Reuses Cadence's payment escrow as base layer (don't reinvent)
- Cadence remains useful as base payment SDK
- Circle Nanopayments / x402 can interop (Crucible markets settle outputs from any payment source)
- ERC-8004 reputation grows organically (every resolved market writes to it)
- LangChain / MCP agents can use Crucible-protected endpoints with no code change (Crucible-aware SDK handles dispute window transparently)
- AI agent commerce is going to explode 2026-2028. The settlement layer is being defined now. Circle is defining the payment layer; Crucible can define the quality settlement layer.
- Polymarket-style market design is a peak-skill area; Zen has direct expertise. Defensible moat.
- Audit-prep mindset: small scope, careful design, no admin keys, no upgrade proxies.
To move from spec to implementation, the following must be decided:
- Name: Crucible / Crucible / Truss / other?
- Repo strategy: New repo
github.com/Ccheh/crucibleOR new folder in arc402 (/crucible/)? - v0 scope confirmed: TestcaseResolver + OracleResolver only for first ship?
- Initial use case to optimize for: code generation (
TestcaseResolver) or AI predictions (OracleResolver)? - Timeline target: 6 weeks MVP? 8 weeks? Open?
- Honest sanity check: is the user OK with this potentially failing technically (the protocol is hard) or commercially (the market may not adopt)? This is genuine R&D, not safe execution.
This spec is intentionally a first draft. Every section is debatable. Iterate before shipping.
Date: 2026-05-12 Status: Designed and implemented (54/54 tests passing, not yet deployed). Scope: Hardens v0's validator economics. Does NOT redesign the core mechanism — the v0 conceptual layer is unchanged.
Three concrete additions:
-
Validator slashing (
TestcaseResolverV2). Validators whose vote diverges from the stake-weighted consensus by more thanTOLERANCE_BPS = 1500(15 percentage points) lose stake proportional to the excess distance, capped atMAX_SLASH_BPS = 1000(10% of their stake) per market resolution. This is the v0.2 fix to V4 (validator collusion) in the security analysis. -
Validator reward fee pool.
CrucibleMarketV2siphonsRESOLVER_FEE_BPS = 200(2%) of the agent escrow on the disputed-resolution path and forwards it vianotifyFee(marketId)to the resolver. The resolver pools per-market fees and distributes them pro-rata to honest validators (within tolerance) along with the redistributed-slashed stake. Validators withdraw viaclaimRewards(). -
Pending-vote-aware unstake. Validators cannot
completeUnstakeuntil every market they voted on has resolved. This fixes the flash-vote-and-exit attack class (V11 in security analysis).
Resolution flow (3-pass in a single tx):
Pass 1: finalScoreBps = stake-weighted mean of votes
Pass 2: for each voter v with distance d = |vote_v - finalScoreBps|:
if d <= TOLERANCE_BPS: honestStake += stake_v
else: slashAmt = stake_v * min((d - TOLERANCE_BPS)*MAX_SLASH_BPS / 8500, MAX_SLASH_BPS) / 10000
validatorStake[v] -= slashAmt
totalSlashed += slashAmt
Pass 3: rewardPool = feePool + totalSlashed
for each honest voter v: pendingReward[v] += rewardPool * stake_v / honestStake
Settlement math (CrucibleMarketV2):
on dispute path:
resolverFee = agentEscrow * RESOLVER_FEE_BPS / 10000
if resolver accepts notifyFee:
settleEscrow = agentEscrow - resolverFee
else (fallback):
settleEscrow = agentEscrow # fee stays in escrow, split per score
paidToService = settleEscrow * scoreBps / 10000
refundToAgent = settleEscrow - paidToService
bondSlash = bondLocked * (10000 - scoreBps) / 10000
totalToAgent = refundToAgent + bondSlash
on optimistic path (collectAfterWindow):
no resolver fee; service collects full escrow.
CrucibleMarketV2 ships with EIP-712 domain "Crucible" version "2". v0 signatures cannot cross-replay against v0.2, and vice versa — domain separators differ by the version field.
- Median consensus. v0.2 still uses stake-weighted mean; a single large validator can still drag resolution. Median voting requires sorting and is planned for v0.3.
- Sybil dispute spam. Disputes are still free in v0.2 (only gas). A dispute bond is the v0.3 fix.
- Mainnet-ready. Pre-audit. Audit + median voting are the prerequisites.
- Validator equilibrium at low dispute rate. Validators only earn during disputes. A healthy market with rare disputes could starve validators of rewards. Solution: future versions might add a small per-market keep-alive fee, or rely on validators having other reasons to stake (their own service reputation, cross-market voting).
The v0.2 changes were chosen because they:
- Directly address the top three known limitations from
docs/security-considerations.md(V4 collusion, V11 flash-exit, validator-economics-missing). - Add NO new admin surface — all parameters are contract constants. To change them, deploy v0.3.
- Stay backward-compatible at the IResolver interface level. v0 resolvers still work with v0 markets; v0.2 resolvers also work with v0 markets (notifyFee just goes unused).
- Fee is only charged on the disputed path — well-behaved services pay no premium. Bad services pay the cost of arbitration, as designed.
contracts/src/v02/
├── IResolverFeeReceiver.sol ~15 LOC (optional resolver extension)
├── CrucibleMarketV2.sol ~245 LOC (fee routing + EIP-712 v2)
└── TestcaseResolverV2.sol ~280 LOC (slashing + rewards + pendingVotes)
contracts/test/
├── CrucibleMarketV2.t.sol 8 tests passing
└── TestcaseResolverV2.t.sol 16 tests passing
Combined v0 + v0.2: 54/54 tests passing, all forge-verifiable via forge test.
Date: 2026-05-12 Status: Designed and implemented (76/76 tests passing across v0 + v0.2 + v0.3, not yet deployed). Scope: Two changes that close the most-cited attack vectors from the v0.2 audit (median consensus + dispute bond).
-
Stake-weighted median consensus in
TestcaseResolverV3, replacing the stake-weighted mean of v0.2. A minority outlier voting an extreme value can no longer drag the consensus regardless of their stake (as long as their stake is below majority). Closes the V3-style "single large stake drags resolution" attack documented indocs/security-considerations.md. -
Dispute bond in
CrucibleMarketV3. Agents must attach a 5% bond when callingdispute(marketId). Bond is split at settlement proportional to score (score=10000 → bond to service, score=0 → bond refunded to agent). Closes the V3-style "free dispute spam" attack vector.
At resolve(marketId):
1. Insertion-sort (vote, stake) pairs by vote ascending. O(M²) memory ops.
2. threshold = totalVotedStake / 2
3. Walk sorted array; the first index where cumulative stake >= threshold
is the median. Return its vote.
Median runtime: O(M²) sort + O(M) walk. For typical M ≤ 20 voters per market on Arc Testnet, sort cost is <250 ops — gas-bounded. For M > 60 a future version should switch to sorted-on-insert.
On dispute(marketId):
expected = (agentEscrow * DISPUTE_BOND_BPS) / 10000 // 5%
require msg.value == expected
market.disputeBond = expected
market.status = Disputed
On resolveDisputed(marketId, ...):
// ... compute settleEscrow, paidToService, refundEscrow, bondSlash as in v0.2
bondToService = disputeBond * scoreBps / 10000
bondRefund = disputeBond - bondToService
totalToService = paidToService + bondToService
totalToAgent = refundEscrow + bondSlash + bondRefund
Outcomes:
- scoreBps = 0 (service totally wrong): agent gets all of (escrow refund, full bond slash, full bond refund). Zero cost for justified dispute.
- scoreBps = 10000 (service totally right): service gets all of (full escrow, dispute bond). Agent loses 5% of escrow for frivolous dispute.
- scoreBps = 5000 (middle): bond splits evenly; agent partially compensated.
CrucibleMarketV3 ships with EIP-712 domain "Crucible" version "3". v0 / v0.2 / v0.3 signatures cannot cross-replay between contracts (each pair has distinct domain separators).
- MIN_STAKE = 0.1 USDC unchanged for testnet ease. Mainnet config will raise it.
- Validator equilibrium at low dispute rate — validators still only earn during disputes. Open question for the economic model.
- Per-market dispute bond configuration — v0.3 bond is a contract constant. Future versions may let services specify their own bond rate via OpenAuth (which would bump the EIP-712 typehash).
- No on-chain dispute reputation — ERC-8004 events still v0.4+.
- No commit-reveal for votes — validators can see each others' votes. Front-running risk is low because voting window is short and votes are weighted by stake, but still a future hardening.
contracts/src/v03/
├── CrucibleMarketV3.sol ~285 LOC (adds dispute bond + bond split math)
└── TestcaseResolverV3.sol ~265 LOC (replaces mean with insertion-sort + median)
contracts/test/
├── CrucibleMarketV3.t.sol 11 tests passing
└── TestcaseResolverV3.t.sol 11 tests passing
The IResolverFeeReceiver interface (defined in v02/) is reused unchanged — v0.3 resolver implements the same interface so any future market version that accepts IResolverFeeReceiver can plug in v0.3 resolvers and vice versa.
Combined v0 + v0.2 + v0.3: 76/76 tests passing, all forge-verifiable via forge test.
Date: 2026-05-12 Status: Designed and implemented (99/99 tests passing across all versions, not yet deployed). Scope: Three changes that close the three remaining "open" items from the v0.3 audit. Implements the missing validator equilibrium, the stake-cap protection, and the ERC-8004-compatible event surface.
Every settlement (optimistic AND disputed) sends VALIDATOR_SUBSCRIPTION_BPS = 10 (0.10% of agent escrow) to the resolver via the new IResolverSubscriptionReceiver.notifyValidatorSubscription() interface. The resolver pools these subscriptions globally with the standard accumulator pattern:
accSubscriptionPerStake += (subscription * 1e18) / totalStake
validatorEarning[v] = (stake[v] * accSubscriptionPerStake / 1e18) - subscriptionDebt[v]
When a validator stakes/unstakes, _settleValidator snapshots their earned amount into pendingSubscriptionReward[v] and resets their debt. They claim with claimSubscription().
This means validators now earn a baseline yield from normal protocol operation, not just from rare disputes. This is the v0.4 fix for the v0.3 economic equilibrium concern.
At resolve time, each validator's effective stake for the median computation is min(realStake, totalVoterStake * 40% / 100%). No single voter can swing the median by more than 40% of total voter weight.
The protection range:
- < 40% stake: cap is a no-op; mechanism behaves identically to v0.3
- 40–70% stake: cap kicks in and lets the rest of the voters outweigh the dominant one
-
70% stake: even capping to 40% leaves the dominant one as a plurality (insufficient by itself, but combined with sympathetic minority votes still potentially controlling)
The cap does affect slashing — the cap is only applied to weight in the median computation. The slash penalty is computed against REAL stake (so a capped large validator who deviates from consensus still loses real money proportional to their full stake).
Two new events with stable schemas designed for off-chain ERC-8004 reputation indexers:
// Per-validator after each resolved market
event ValidatorReputation(
address indexed validator,
bytes32 indexed marketId,
uint16 vote,
uint256 deviation,
uint256 slashedAmount,
bool honest
);
// Per-service after each settlement
event ServiceReputation(
address indexed service,
bytes32 indexed marketId,
uint16 finalScoreBps,
uint256 bondSlashed
);This aligns with Circle's Agent Stack (Agent Marketplace + ERC-8004) which expects reputation data to be queryable from on-chain events.
subscription = agentEscrow * VALIDATOR_SUBSCRIPTION_BPS / 10000
settleEscrow = agentEscrow - subscription (if resolver accepts)
paidToService = settleEscrow
subscription = agentEscrow * VALIDATOR_SUBSCRIPTION_BPS / 10000
resolverFee = agentEscrow * RESOLVER_FEE_BPS / 10000
settleEscrow = agentEscrow - subscription (if accepted) - resolverFee (if accepted)
paidToService = settleEscrow * scoreBps / 10000
refundEscrow = settleEscrow - paidToService
bondSlash = bondLocked * (10000 - scoreBps) / 10000
bondToService = disputeBond * scoreBps / 10000
bondRefund = disputeBond - bondToService
A $0.01 escrow now incurs:
- 0.10% subscription always
- 2.00% resolver fee on dispute
- 5.00% dispute bond from agent on dispute
For a healthy market with rare disputes (say 1% dispute rate), the effective cost to a service is dominated by the always-on 0.10% subscription. For the validator economics: a network with 10k calls/day at $0.01 each generates $10/day of subscription, split pro-rata by stake.
| Stake | v0.3 weight | v0.4 weight | Vote |
|---|---|---|---|
| v1: 55% | 55% | 40% (capped) | 0 |
| v2: 30% | 30% | 30% | 10000 |
| v3: 15% | 15% | 15% | 10000 |
- Under v0.3: median pivot at cumulative ≥ 50% → first entry (v1's 55% > 50%) → median = 0. v1 dominates.
- Under v0.4: total capped weight = 85; threshold = 42.5; cumulative [40 (v1), 70 (v2+v1), 85 (all)]. First ≥ 42.5 is at v2 → median = 10000. Honest majority recovers.
- MIN_STAKE = 0.1 USDC unchanged for testnet ease. Mainnet config should raise to 1+ USDC.
- Per-market dispute bond configuration — still a contract constant. Adding to OpenAuth would change the EIP-712 typehash (heavy SDK churn).
- Commit-reveal voting — still vulnerable to validators front-running each others' votes. Low risk on a 1-hour voting window with stake-weighted aggregation, but real for high-value markets.
- >70% stake attacker — the 40% cap helps in the 40–70% range but cannot recover correctness when one party holds supermajority. There is no on-chain mechanism that can; this would require a stake distribution policy at validator-network level.
contracts/src/v04/
├── IResolverSubscriptionReceiver.sol ~15 LOC
├── CrucibleMarketV4.sol ~310 LOC
└── TestcaseResolverV4.sol ~330 LOC
contracts/test/
├── CrucibleMarketV4.t.sol 9 tests passing
└── TestcaseResolverV4.t.sol 14 tests passing
Combined v0 + v0.2 + v0.3 + v0.4: 99/99 tests passing.
Date: 2026-05-12 Status: Designed and implemented (131/131 tests passing across all versions, not yet deployed). Scope: Three changes that close the three remaining "open for v0.5+" items from the v0.4 audit. The protocol is now substantially feature-complete for the test/audit cycle.
The single biggest anti-collusion upgrade. Replaces v0.4's vote(marketId, score) with two-phase:
Commit phase (first 30 min):
voteHash = keccak256(abi.encode(score, salt, marketId, voter))
commitVote(marketId, voteHash)
Reveal phase (next 30 min):
revealVote(marketId, score, salt)
→ verifies hash matches → records vote
The hash leaks no information about score (256-bit salt prevents brute force). Validators must commit their own honest estimate; the commit phase ends before anyone learns what anyone else voted. Eliminates the v0.4 risk of validators copying each others' votes at the last second.
A validator who commits but doesn't reveal is not counted in resolution AND is not locked by pendingVotes — clean opt-out semantics.
constructor(uint256 _minStake) {
require(_minStake > 0, "minStake must be > 0");
MIN_STAKE = _minStake;
}Mainnet deployments can pick 1+ USDC for real sybil deterrence. Testnet keeps 0.1 USDC for easy bootstrap. immutable so the contract doesn't grow an admin surface.
struct OpenAuth {
...
uint16 disputeBondBps; // NEW in v0.5
...
}Services pick their own dispute friction at OpenAuth sign time. Bounds: [100 bps (1%), 5000 bps (50%)]. A free testnet API can use 100 bps; a high-value LLM gateway can use 2000 bps. Agents who don't like the chosen friction simply don't open the market — service-side decision.
EIP-712 typehash changes (struct shape changed), so domain version bumps to "5" — sigs cannot cross-replay against v0.4 or earlier.
disputeBond = agentEscrow * market.disputeBondBps / 10000 (v0.5: per-market)
subscription = agentEscrow * VALIDATOR_SUBSCRIPTION_BPS / 10000 (10 bps every settle)
resolverFee = agentEscrow * RESOLVER_FEE_BPS / 10000 (200 bps on dispute)
settleEscrow = agentEscrow - subscription (if accepted) - resolverFee (if dispute and accepted)
paidToService = settleEscrow * scoreBps / 10000
refundEscrow = settleEscrow - paidToService
bondSlash = bondLocked * (10000 - scoreBps) / 10000
bondToService = disputeBond * scoreBps / 10000
bondRefund = disputeBond - bondToService
contracts/src/v05/
├── CrucibleMarketV5.sol ~290 LOC (per-market bondBps + EIP-712 v5)
└── TestcaseResolverV5.sol ~370 LOC (commit-reveal + config MIN_STAKE)
contracts/test/
├── CrucibleMarketV5.t.sol 11 tests passing
└── TestcaseResolverV5.t.sol 21 tests passing
Combined v0 + v0.2 + v0.3 + v0.4 + v0.5: 131/131 tests passing.
The leftover list has shrunk considerably:
- Stuck disputed markets — if everyone commits but no one reveals, the market is stuck in Disputed status with no resolution path. v0.6 needs a force-resolve-default function that the agent or service can call after a grace period. Trivial to add.
- >70% stake attacker — still mechanically impossible on a single market. Validator network stake distribution is the policy-level fix.
- Validator yield smoothing — subscription distribution is event-driven (per fee deposit). Smoother yield would need block-time accrual; not on the critical path.
- Cross-resolver reputation aggregation —
ValidatorReputationevents are per-resolver; an off-chain ERC-8004 indexer can aggregate. On-chain aggregation across resolvers is v0.7+.
None of these block testnet deployment or audit-prep. v0.6 = stuck-market fallback (10-line fix). v0.7 = bigger work.