Skip to content
Fossa

Orders

Booking generations, order payloads, matching, and expiry.

Initialize reusable booking storage

The default build enables receipt-prototype. Instruction 26 initializes reusable storage with [market, user, owner(s), booking(ws), payer(s)] and an empty payload. Precreate the booking as a zeroed, program-owned 368-byte account. Initialization pins owner, market, user, and rent payer, then sets status to 5, available.

Each reusable order carries a generation greater than the booking's previous generation and exactly one above the user account's counter. The counter occupies seven little-endian bytes at 113..120; valid generations remain below 2^56. It is shared across that user's booking accounts. The booking stores its generation as u64 at 152.

Instruction 6 can also initialize fresh reusable storage directly, requiring the fresh account's signature. After initialization, reused storage is writable without its own signature; the owner still signs every order. The recorded rent payer must match. Claims, releases, and expiry include the generation so an old instruction cannot settle a later reservation.

Submit an order

Send instruction 6 with [market, model, terms, capacity(w), user(w), owner(s), booking(w), rent_payer] for initialized reusable storage. Use the model's canonical books. The payload contains the following fields in this exact byte order:

  • 0..32: model ID.
  • 32/36/40: input, cached-input, and output rate caps, each u32.
  • 44: minimum declared speed, u16; 46/50: maximum input and output tokens, each u32.
  • 54: required privacy flag, u8; 55: maximum spend, u64.
  • 63/67/71: estimated input, cached-input, and output tokens, each u32.
  • 75..107: provider public key eligible for cached pricing.
  • 107: reusable generation, u64.

This is a 115-byte payload, or 116 instruction-data bytes including opcode 6. Estimated input includes cached tokens. Estimates must fit the token bounds; a nonzero cached estimate requires a nonzero cache-provider key. Maximum input and output must both be positive. Available spend is the smaller of maximum spend and free balance.

The legacy path uses only the 107-byte base payload and requires fresh booking(ws) plus rent_payer(s) for each order. Its generation is 0. Both paths execute the same eligibility, pricing, reserve, and deadline checks.

Matching and price selection

Instruction 6 scans populated slots in ascending order. An offer must be active and approved, satisfy the privacy flag and minimum declared speed, and stay within all three rate caps. The cached-rate cap applies even when that provider cannot receive cached pricing. Speed and privacy are stored declarations used by these checks. The effective minimum speed is max(requested_min_speed, ceil(max_output / (86400 - CLAIM_SECONDS))). An offer whose declared speed would produce a claim window above 86,400 seconds is skipped before scoring, allowing another eligible offer to win. Full concurrency or obligation counts are also rejected before pricing.

For each eligible offer, the score is the sum of separately rounded input, cached-input, and output fees. Cached estimates apply only when the offer's provider equals the submitted cache-provider key; every other provider prices that input as uncached. The smallest estimated charge wins. Strictly lower scores replace the current choice, so equal scores retain the earlier permanent slot. Once a fully feasible offer scores zero, scanning can stop: later slots cannot improve its score or tie position.

The candidate must also have an available concurrency slot, an incrementable obligation count, enough user spend for its reserve, and sufficient bond for the resulting exposure. The reserve is fee(max_input, input_rate) + fee(max_output, output_rate) + 1 atomic unit. The extra unit covers rounding when input splits into cached and uncached categories. If no offer survives these checks, the order fails with custom error 6.

Read the matched booking

A successful order subtracts the reserve from free balance, increases offer exposure, and increments active bookings and obligations. The 368-byte BOOK0V20 account identifies the selected provider at 40..72, owner at 8..40, market at 72..104, reserve at 104, and pinned input/cached/output rates at 112/116/120.

Maximum input/output are u32 at 124/128. Claim and dispute deadlines are i64 Unix seconds at 132/140; status is u8 at 148; reusable generation is u64 at 152. User, model, rent payer, payout, and bond-vault keys begin at 192/224/256/288/320. Slot is u32 at 352, bond bump at 356, requested privacy at 357, cache permission at 358, and estimated score as u64 at 360. Later offer updates cannot modify these pinned values.

Reserved status is 0. The standard claim window is 300 + ceil(max_output / selected_speed) seconds from the booking clock; matching excludes offers whose window would exceed 86,400 seconds, and the booking instruction checks this bound again. The dispute deadline is another 3,600 seconds after the claim deadline. Local-test builds use different timer constants. Check the stored deadlines rather than assuming a fixed window.

Release or expire a reservation

Instruction 15 releases an unused reservation with [market, model, capacity(w), user(w), booking(w), provider(s)]. Only the selected provider can release it. Instruction 8 expires one with [market, model, capacity(w), user(w), booking(w)]; it requires no signer but succeeds only when the clock is strictly greater than the stored claim deadline.

Both take generation:u64 for reusable bookings and an empty payload for legacy bookings. The booking must still be reserved and reference the supplied market, model, user, and offer. Each returns the full reserve to free balance, subtracts reserve exposure, and decrements active bookings and obligations. Reusable storage becomes available 5; legacy storage becomes released/expired 3.

At the exact claim deadline, a valid claim remains permitted and expiry is rejected. Advancing time does not mutate accounts: an expiry instruction must execute to release the reservation.