Chio/Docs
LOGIN · JOIN

EconomyTools

Bidding in the Open Market

A buyer submits a bid, a seller returns an ask, and both parties sign an accepted trade.

Three related crates

These three crates enforce different parts of the market flow.

CrateRoleWhat it does
chio-listingDiscoverySigned registry publication and pricing hints. Answers “what exists, and roughly what does it cost.”
chio-open-marketBid/ask venue (this page)Buyer submits a bid, seller quotes an ask, both sides accept into a receipted trade. Answers “what price clears, right now.”
chio-marketLiability and insuranceCoverage quotes, binding, and claims for economic risk. It does not authorize capability access.

See Capability Discovery for the registry chio-open-market reads listings from, and Liability Coverage for the coverage market it is sometimes confused with by name alone.


The bid, ask, and accept flow

A trade starts when a buyer submits a BidRequest naming the listing it wants, a ceiling price, and a time window. The venue resolves the request against the listing and its pricing hint and returns a signed AskResponse (SignedAskResponse): a quoted price plus a token offer, a capability grant minted and bound to the buyer's subject key. The grant itself is assembled inside a BidMintContext, which pairs the resolved listing with the issuer's signing key and the buyer's subject before anything is minted.

Accepting the ask is a separate, signed step. The buyer signs an AcceptedBid (SignedAcceptedBid) against the ask and a reservation: a funds hold verified before acceptance is allowed to proceed. The accepted record carries a bid_receipt_id tying the trade back to that reservation, so a settlement layer downstream can verify the bid, ask, and acceptance as one signed record.

The three records, each of them a SignedExportEnvelope around the body below, with camelCase keys on the wire:

rust
// chio.marketplace.bid-request.v1
pub struct BidRequest {
    pub schema: String,
    pub agent_id: String,
    /// Buyer-signed settlement address. Generic bids may omit it, while
    /// cognition-market purchase coordinators require a valid EVM address.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub payout_destination: Option<String>,
    pub listing_id: String,
    pub max_price_per_call: MonetaryAmount,
    pub window_seconds: u64,
    pub requested_scope: RequestedScope,
    pub issued_at: u64,
}

// chio.marketplace.ask-response.v1
pub struct AskResponse {
    pub schema: String,
    pub listing_id: String,
    pub agent_id: String,
    /// Canonicalized SHA-256 digest of the originating `BidRequest`.
    pub bid_digest: String,
    pub quoted_price: MonetaryAmount,
    /// Minted capability token bound to the agent subject with the
    /// provider's issuer key.
    pub token_offer: CapabilityToken,
    pub issued_at: u64,
    pub expires_at: u64,
}

// chio.marketplace.accepted-bid.v1
pub struct AcceptedBid {
    pub schema: String,
    pub listing_id: String,
    pub agent_id: String,
    pub bid_digest: String,
    /// Digest of the signed `AskResponse` being accepted.
    pub ask_digest: String,
    /// The receipt identifier issued by the kernel when the agent's funds
    /// were reserved for this ask.
    pub bid_receipt_id: String,
    pub quoted_price: MonetaryAmount,
    pub accepted_at: u64,
    pub token_id: String,
    pub token_subject: PublicKey,
    pub token_expires_at: u64,
}

Each record digests the one before it. The ask names the bid it answers, and the acceptance names both, so verifying a trade means recomputing two digests rather than trusting three separate signatures to be about the same thing. The token the ask carries is the whole minted CapabilityToken, not a reference to one, which is why the acceptance repeats its id, subject and expiry rather than pointing at a registry.


Trust and abuse controls

The fee schedule, defined in fee_schedule.rs, sets what publishing, disputing, and participating in the market cost, and what bond an operator posts as collateral before it can act. A bond can be held, slashed, or restored through the penalty process.

Misbehavior is processed in penalty.rs. An OpenMarketPenaltyArtifact carries one of three dispositions against a posted bond: HoldBond, SlashBond, or ReverseSlash, the last of which restores a slash later found to be incorrect. The current implementation recognizes four abuse classes: SpamPublication, FraudulentListing, ReplayPublication, and UnverifiableListingBehavior. A dispute itself carries its own bond class, OpenMarketBondClass::Dispute, so contesting a penalty also requires a bond.

The venue cannot slash a bond by itself. Slashing requires an enforcedSanction case in chio-governance, the same case kind and the same enforced state that blocks admission elsewhere in the registry. See Listing Disputes for how a case reaches enforced, and what blocks_admission actually means.


Fail-closed admissibility

Before the venue mints a grant, it checks the bid, listing, and pricing hint independently. A valid signature does not make a stale listing admissible, and a fresh listing does not make an invalid signature valid.

  • Signature validity: the bid, the resolved listing, and its pricing hint each verify independently.
  • Listing status: the listing must be active; suspended, revoked, retired, or superseded listings are refused.
  • Freshness: a listing whose freshness window has elapsed is treated as stale and refused, the same as it would be dropped from a discovery search.
  • Pricing-expiry: a pricing hint past its own expiry is refused even when the listing itself is otherwise healthy.

A failed check denies the bid. The venue supplies no fallback price.


Binding bids to grants

The constraints on a minted grant come from the provider, never from the bid. BidMintContext is what the provider supplies at mint time, and its grant_constraints field is authored on the provider side. A buyer cannot request a constraint, alter one, or drop one, so the constraint set on the token a buyer receives is a statement the provider signed rather than a term the buyer negotiated.

On the plain marketplace path a provider may mint with no constraints at all. The delivery-committed path does not leave the choice open. bid_with_finding_purchase overwrites whatever the caller passed with exactly two constraints and turns proof-of-possession on:

rust
bid_context.grant_constraints = vec![
    Constraint::OutputDigestSha256(finding.payload_sha256.clone()),
    Constraint::RequireFindingPurchase(Box::new(FindingPurchaseMarkerV1 {
        finding_id: finding.finding_id.clone(),
        listing_id: admission.listing_id().to_string(),
        settlement: FindingSettlementSelector::LocalReversibleHold,
    })),
];
bid_context.dpop_required = Some(true);

The first constraint pins the grant to the exact payload digest the seller committed to, so a delivery whose bytes hash differently is refused at the kernel rather than argued about afterwards. The second names the finding, the listing, and the settlement selector the purchase runs under. Both are minted before the buyer sees the payload. Reveal and settlement follows what happens when the delivered bytes and the pinned digest disagree.


Next steps

Bidding in the Open Market · Chio Docs