LOPWDocumentation

Docs / Protocol reference

Offer, receipt and terms

Three structures carry the whole protocol: the terms a service registers, the offer it signs before payment and the receipt it signs after. All three are contract types; their canonical encoding is ScVal XDR, which is what gets hashed and signed.

JSON Schemas derived from the contract types: terms.json, offer.json, receipt.json. In JSON, 32-byte values are lowercase hex, i128 amounts are decimal strings, u64 times are Unix seconds, and addresses are strkeys.

Terms

A service’s registered warranty terms. Hashed (terms_hash) and versioned; every offer pins one version. min_stake is deliberately not part of the terms, so changing the stake requirement never invalidates offers or receipts a buyer holds.

FieldTypeMeaning
predicatesVec<u32>Predicate IDs this warranty covers: 1 to 6, non-empty, no duplicates
claim_windowu64Seconds after served_at during which a claim may be filed
max_data_ageu64P3: most seconds allowed between data_as_of and served_at
allowed_statusVec<u32>P4: HTTP statuses the seller may charge for
max_response_bytesu32P5: largest response body in bytes
oracleOracleWarrantyP6 parameters: None, or Some(OracleTerms). Required when P6 is listed

OracleTerms

FieldTypeMeaning
oracle_contractAddressThe SEP-40 oracle the warranty is measured against
oracle_assetOracleAssetStellar(Address) or Other(Symbol), as that oracle names the asset
max_deviation_bpsu32Allowed difference, in basis points of the oracle price
max_oracle_ageu64Most seconds allowed between the oracle observation and served_at

Validity rules

The registry rejects terms with InvalidTerms unless all of the following hold:

Terms from the shared test vectors
{
  "predicates": [3, 4, 6], "claim_window": 3600, "max_data_age": 30, "allowed_status": [200], "max_response_bytes": 4096,
  "oracle": { "oracle_contract": "C…", "oracle_asset": { "Other": "EUR" }, "max_deviation_bps": 50, "max_oracle_age": 600 }
}

Offer

The seller’s signed promise for one call, shown with the 402 response. It does not repeat the warranty conditions: those live on-chain as the registered terms, and terms_hash pins the exact version the buyer is paying under. The offer is the signed body; the signature travels beside it in SignedOffer { offer, signature }.

FieldTypeMeaning
versionu32Protocol version, 1. Any other value fails a claim with UnsupportedVersion
service_idBytesN<32>Assigned by the Bond Registry at registration
registryAddressBond Registry contract that holds the stake. A claim requires it to be the registry the Claims contract serves
terms_hashBytesN<32>Hash of the registered terms for this service. The buyer SDK requires it to be the service’s current terms
pricei128Price of this call, in the asset’s smallest unit. The buyer SDK requires it to equal the x402 amount
assetAddressToken contract of the payment asset
max_claimi128Most a single receipt can pay out
valid_untilu64Unix seconds after which the offer must not be paid
offer_nonceBytesN<32>Random, unique per offer; makes every offer hash unique
key_versionu32Which registered signing key signed this offer
signature (in SignedOffer)BytesN<64>Ed25519 signature over offer_hash by the service signing key

The middleware sets valid_until = now + offerValiditySeconds (default 120 s), max_claim from its configuration, price and asset from the matching x402 requirement, and key_version from the service’s current key.

Receipt

The seller’s signed statement of what it served and under which offer. Every field is a claim the seller can be held to. Signed body; the signature travels beside it in SignedReceipt { receipt, signature }.

FieldTypeBinds the receipt to
versionu32Protocol version, 1
service_idBytesN<32>The registered service
offer_hashBytesN<32>The exact warranty offer
terms_hashBytesN<32>The terms the seller says it served under (P2 compares it with the offer’s)
buyerAddressThe payer, who receives any claim payout
payment_referenceBytesN<32>The settlement transaction hash
request_hashBytesN<32>Method, path with query, and body of the request
response_hashBytesN<32>The response body as sent
response_bytesu32Size of the response body (P5)
statusu32HTTP status served (P4)
served_atu64Unix seconds when the response was sent; starts the claim window
data_as_ofu64Unix seconds the data was true as of; 0 if not applicable (P3)
oracleOracleEvidenceOptNone, or Some(OracleEvidence) for P6 services
key_versionu32Which signing key signed this receipt
signature (in SignedReceipt)BytesN<64>Ed25519 signature over receipt_hash by the service signing key

OracleEvidence

FieldTypeMeaning
oracle_contractAddressThe SEP-40 oracle named in the terms
oracle_assetOracleAssetThe asset or pair, as the oracle names it
oracle_timestampu64The timestamp the oracle returned for the observation used
oracle_pricei128The oracle price the seller saw. Informational: the validator reads the oracle itself
reported_valuei128The value the seller served, in the oracle’s decimals

The contract does not check that the payment happened. The seller’s signature over payment_reference and buyer is its admission that it did, and any payout comes from the seller’s own stake. The middleware takes buyer and payment_reference from the facilitator’s settlement result and only issues receipts for offers it signed itself.

Registry records

Service

FieldTypeMeaning
sellerAddressThe account that registered the service and may manage it
stateServiceStatePending 0, Active 1, Suspended 2, Unbonding 3, Closed 4
stakei128Stake currently held
min_stakei128Stake below which the service is Suspended; at least the protocol floor
terms_hashBytesN<32>Current terms version
key_versionu32Current signing key version
max_claim_windowu64Largest claim window of any terms version ever registered; sets the unbonding delay
pending_withdrawali128Stake requested for withdrawal and still slashable
unlock_atu64When the pending withdrawal may be paid out
full_exitboolTrue when the pending withdrawal closes the service
epoch_start, epoch_base_stake, epoch_slashedu64, i128, i128Slash rate limit bookkeeping
fee_accrued_atu64Time up to which the annual stake fee has been collected

SigningKey

FieldTypeMeaning
public_keyBytesN<32>Ed25519 public key
registered_atu64When this version was registered
retired_atu640 while current; otherwise the time it was rotated out

ProtocolConfig, LaunchLimits, FeeConfig

TypeFieldsSet
ProtocolConfigmin_stake_floor i128, max_claim_window u64, max_oracle_claim_window u64, unbond_margin u64, slash_epoch_cap_bps u32Once, at registry construction
LaunchLimitsmax_stake_per_service i128, max_total_stake i128, max_payout i128, allowlist_only bool (0 means no limit)Tightening immediate; loosening time-locked
FeeConfigtreasury Address, registration_fee i128, stake_fee_bps_per_year u32Changes time-locked

Validator types

TypeDefinition
ClaimExtraNone | SecondReceipt(SignedReceipt): predicate-specific evidence; the second receipt for P1, nothing for P2 to P6
ValidationErrorNotProven = 1 | NotVerifiable = 2: why a validator did not prove a breach
PriceData{ price: i128, timestamp: u64 }: a SEP-40 price record as the oracle returns it
Sep40Oracleprice(asset, timestamp) -> Option<PriceData>, decimals() -> u32: what the P6 validator reads