Skip to content

For integrators

If you're building a frontend, an indexer, a market-data tool, or any application that reads from or writes to wenrange, this page lists the on-chain and off-chain surfaces you'll interact with and how to use them.

For the formal protocol model, see Protocol reference.

Surface overview

Three surfaces are exposed:

Surface What it's for Where to find it
On-chain contracts All custody, settlement, and registry state. The source of truth. OptionsMarket (UUPS proxy) + PythSettlementOracle. ABIs published per release.
API service (REST) Read-only views over indexed chain state — positions, settlement history, solver liquidity. Hosted at api.wenrange.com. Documented at api.docs.wenrange.com.
Orchestrator (REST) Live quoting. Request a price, get a signed commitment, submit on-chain. Hosted at the orchestrator's public endpoint. Documented at api.docs.wenrange.com.

If you only need to read state, use the API service. If you want to trade, you need the orchestrator (for commitments) plus the on-chain contract (to submit them).

Reading on-chain state

Contract addresses

Published in the project README per network. Both the market and the settlement oracle have stable addresses; the market is upgradeable behind an ERC-1967 proxy, so the proxy address is what you target.

ABI

The canonical ABI is checked in to the contracts repo at contracts/abis/ and re-published with every release. Both the proxy's surface and the settlement oracle's surface are exported.

Events you'll want to follow

Position lifecycle is best reconstructed from standard ERC-1155 events plus one protocol-specific event for the premium:

  • TransferSingle / TransferBatch — every mint, burn, and holder-to-holder transfer of a position.
  • PositionBought(buyer, solverId, tokenId, quantity, premium, nonce) — emitted on buy. The premium field is the only place to recover what the buyer paid, which is needed for cost-basis reporting.
  • PositionBoughtBack(seller, solverId, tokenId, quantity, amountPaid, isIssuer, nonce) — emitted on buyback.
  • PositionSettled(tokenId, holder, quantity, userWon, amountPaid) — emitted on settle.
  • PositionEmergencyCanceled(tokenId, holder, quantity, refund) — emitted on emergencyCancel.

For solver-side state:

  • SolverRegistered, SolverPaused, SolverUnpaused, SolverRemoved.
  • CollateralDeposited, CollateralWithdrawn.
  • Frozen-collateral state is not directly emitted — derive it from position lifecycle events (a buy freezes quantity * 1 USDC; an issuer buyback, settlement, or emergency-cancel releases it).

For governance and parameters:

  • ParameterUpdated(key, value), AdminTransferred, OracleProposed, OracleUpdated.

For the bounty pool:

  • BountyPoolFunded, BountyPoolWithdrawn, SettleRewardPaid.

Position token IDs

Position NFTs are ERC-1155 with a packed token ID:

Byte offset Field Notes
[0..4) lowerBound In ticks. 0 means open-below (no lower bound).
[4..8) upperBound In ticks. 0xFFFFFFFF means open-above (no upper bound).
[8..12) maturityHour Unix timestamp / 3600.
[12..16) solverId Index into the solver registry.

Two buyers of the same range, maturity, and solver get the same token ID — positions are semi-fungible per (range, maturity, solver).

Tick size

Per-asset, configured at deployment. For BTC the tick is $100. Multiply lowerBound/upperBound (as integers in ticks) by the tick size to get the price in quote-token units.

Reading via the API service

Three read-only endpoints, all served from indexed chain state:

Endpoint What it returns
GET /positions?owner=<address>&limit=&offset= Current ERC-1155 holdings: token ID, balance, cost basis, range bounds, maturity, solver ID.
GET /settlement-prices?limit=&offset= Recorded oracle settlement prices, most recent first.
GET /solver-liquidity Pool-wide solver collateral aggregates (total_liquidity, frozen_liquidity).

Full schemas — request parameters, response shapes, error codes — are on the API docs site.

These endpoints are convenient but not authoritative. The on-chain contract is the source of truth; the indexer can lag by a few seconds, and a reorg could in principle move state out from under you. For anything safety-critical (showing a user their position before they sign a transaction), prefer reading the chain directly.

Submitting transactions

Two entry points the user calls:

  • buy(bytes packed, bytes signature) — opens a position. Requires msg.value == openFee (a small ETH amount, see Protocol reference).
  • buyback(bytes packed, bytes signature) — closes a position early (or assigns it to a new solver).

The packed argument is the solver's commitment in a compact 140-byte (buy) or 160-byte (buyback) wire format. The signature is the solver's EIP-712 signature over the canonical struct. The wire format is documented on the API docs site and a reference codec lives in contracts/src/Commitments.sol with round-trip tests.

You don't construct these yourself — you request a quote from the orchestrator, which returns both packed and signature ready to submit. The flow is:

  1. POST to the orchestrator's /position/open (or /position/close) with the request parameters and the user's wallet address.
  2. Orchestrator returns {price, commitment_packed, commitment_signature, solver_id}.
  3. Pass commitment_packed and commitment_signature straight into buy / buyback along with the right msg.value.

The smart contract verifies that the transaction sender matches the buyer in the commitment, so the commitment is single-use and not stealable.

Triggering settlement

You don't have to wait for the team to settle positions; anyone can.

  • recordPrice(uint32 maturityHour, bytes[] updateData) on the settlement oracle — submits a Pyth price update VAA for the hour. Requires forwarding Pyth's update fee in ETH. Permissionless; first valid recording is final.
  • settle(uint256 tokenId, address holder) on the market — settles a position whose maturity hour has been recorded. Pays the caller a small ETH bounty from the shared pool.
  • emergencyCancel(uint256 tokenId, address holder) — only callable after the grace period (currently 7 days from maturity) if no price was ever recorded. Refunds the holder's premium and releases the solver's collateral.

A reference relayer (backend/pyth-relayer/) runs recordPrice on the team's behalf, but it's not trusted — anyone can run an equivalent.

EIP-712 domain

If you're building anything that verifies commitments off-chain (e.g. a competing orchestrator, a portfolio tracker that wants to display pending commitments), the EIP-712 domain is:

name              : "OptionsMarket"
version           : "1"
chainId           : <pinned to the deployment's chain>
verifyingContract : <market proxy address>

The BuyCommitment and BuybackCommitment type strings are the canonical EIP-712 types and live in contracts/src/Commitments.sol. There is no asset field — single-asset deployments rely on the domain separator's verifyingContract for cross-deployment replay protection.


Next steps