Integration Reference

OTC Orders and RFQ

Integrate EVM PT/YT rate orders, signed quote requests and MetaVault responses.

Spectra OTC settles individual PT/YT trades through Spectra's deployment of the 1inch Limit Order Protocol. The API validates and stores signed orders; it does not hold funds or execute fills. RFQ adds a non-binding request and response workflow around those same orders.

This is the EVM OTC integration. Stellar limit orders use a different engine and API. OTC is currently enabled on Base (8453), Katana (747474) and Flare (14).

Resolve the deployment

See the deployed contracts for Base, Katana and Flare.

Resolve current addresses at runtime with GET https://api.spectra.finance/v1/{network}/otc/config, where network is a supported chain ID or network slug. A successful response is { success: true, data: { limitOrderProtocol, rateAmountGetter, authorizedTakersGuard, orderSigner } }.

These addresses come from the execution chain's global MetaVaultsRegistryV2 keys:

Response fieldRegistry key
limitOrderProtocolspectra.otc.settlement
rateAmountGetterspectra.otc.rate_getter
authorizedTakersGuardspectra.otc.takers_guard
orderSignerspectra.otc.order_signer

The endpoint is uncached. A 503 can mean that OTC is disabled or a complete deployment cannot be resolved. Treat that as unavailable; do not substitute test addresses. Keep each stored order's original settlement for history and cancellation when the current configuration changes.

Rate-order model

The maker signs an eight-field 1inch v4 order with Spectra's getter installed on both amount-getter legs. The extension encodes the PT address, derivative kind, quote kind and implied APY. The salt binds the extension to the order.

TermMeaning
derivativeKind0 = PT, 1 = YT.
quoteKind0 = underlying, 1 = IBT, from the same market.
makingAmountFixed quantity sent by the maker, in that token's base units.
takingAmountCreation-time baseline; use the getter for the current payment.
impliedApyWadAnnually compounded rate: 5% is 50000000000000000.
ExpiryDeadline for filling, distinct from the PT/YT maturity.

The getter recalculates the counter-amount at execution. Do not price a fill from the stored baseline or floating-point display values. Use each token's decimals. The supported pairs are PT ↔ underlying, PT ↔ IBT, YT ↔ underlying and YT ↔ IBT. No AMM pool is required.

The current envelope policy requires full fills, disables multiple fills, and requires receiver = 0 and allowedSender = 0. Private orders use SpectraAuthorizedTakersGuard with up to 64 authorized addresses. It checks the immediate caller of the fill: for a MetaVault taker, authorize the Safe. The list is signed data, not confidential data.

Publish and fill

Use the primitives in @perspective/spectra-sdk/otc/codec, otc/types, otc/validation and otc/abi.

  1. Resolve configuration and verify the market's PT, YT, IBT and underlying addresses onchain.
  2. Build the canonical extension, salt and traits. Allocate a unique nonce per maker and settlement domain; reusing it can invalidate multiple orders.
  3. Call the getter's previewOrder, set the creation baseline and cross-check the local hash with the settlement's hashOrder.
  4. Approve the maker asset to the settlement and sign the order. Its EIP-712 domain is 1inch Limit Order Protocol, version 4, with the chain ID and settlement as verifyingContract.
  5. POST { envelope } to /v1/{network}/otc/create.

The OtcOrderEnvelope uses schema: "spectra-otc", schemaVersion: 1 and orderType: "RATE". It carries chainId, settlementAddress, orderHash, order, extension, signature, createdAt, createdAtBlock and, for private orders, authorizedTakers. Serialize uints as decimal strings. metadata is untrusted display assistance.

validateOtcEnvelope(envelope, chainId, instance) checks signed structure, configuration, traits, expiry, hash and extension binding. It is a pure check: it does not verify signatures, balances, allowances or current fillability. Refresh those from the chain, including the bit invalidator and a fresh getter preview. Failed reads must remain unknown.

Pass the full nonce to bitInvalidatorForOrder(maker, nonce); the contract shifts it internally. Test bit nonce & 255 in the returned bitmap. A set bit takes precedence over low balance or allowance when displaying closed orders.

For a fill, require an explicit maximum payment, approve the payment token, then simulate the full maker amount with the extension and threshold packed by buildTakerTraits. Choose the entrypoint by the maker's deployed code:

MakerEntrypoint
EOAfillOrderArgs, using the compact EIP-2098 signature.
Contract walletfillContractOrderArgs, passing the original ERC-1271 signature bytes.

A 65-byte signature does not prove that the maker is an EOA. Contract signature validity can change, so verify it again before execution. For non-ECDSA contract signatures, including a Safe's empty 0x, call isValidSignature(orderHash, signature) directly and require 0x1626ba7e; reject failed or reverting reads. A successful preview or simulation does not reserve the order.

Cancel with cancelOrder(makerTraits, orderHash) from the maker on the order's original settlement. Delisting, API shutdown and allowance revocation do not invalidate the signature. The bit invalidator alone reports filled or cancelled; use transaction events to distinguish the two.

API reference

All paths below follow /v1/{network}/otc. Successful list calls return { success: true, data }; order creation returns { success: true, orderHash }.

Method and pathRequest
GET /ordersOptional market (PT address), makerAddress, derivativeKind, includeInactive, limit, offset.
POST /create{ envelope: OtcOrderEnvelope }.
GET /rfq/requestsExactly one of requester or vault; requester history can use includeClosed=true.
POST /rfq/create{ envelope: QuoteRequestEnvelope }.
POST /rfq/respond{ requestHash, vault, orderHash } for an already published response.
POST /rfq/decline{ requestHash, vault, signer, signature }.
POST /rfq/cancel{ requestHash, signature }.

Load maker history separately with makerAddress, includeInactive=true and pagination. A page of the public book is not a complete list of a maker's outstanding signatures. Validate historical identity with validateOtcOrderIdentity; do not require old orders to match today's deployment just to show or cancel them.

Write endpoints distinguish malformed input (400), semantic validation failure (422) and unavailable service (503). An identical accepted order can be published again without creating a new order; this does not reactivate an expired or invalidated signature.

RFQ lifecycle

Use @perspective/spectra-sdk/otc/rfq for the canonical types, typed-data definitions, hashing and wire conversion. The Spectra OTC RFQ EIP-712 domain has version 1, a chain ID and no verifying contract: these signatures authenticate API operations only.

  1. The requester signs QuoteRequest: requester, derivative/quote kinds, requester-side BUY or SELL, underlying, IBT, PT, maturity, derivative amount, recipient Safe addresses, request expiry and salt.
  2. POST its QuoteRequestEnvelope. The API allows up to 16 recipient vaults and 20 open requests per requester per chain. The expiry cannot exceed maturity.
  3. Each vault either signs DeclineQuoteRequest with an authorized operator, or publishes a private order on the opposite side and links it with /rfq/respond. Linking requires no extra signature: the stored vault-signed order proves the offer's authorization.
  4. The requester view includes per-vault PENDING, DECLINED or RESPONDED states and response order envelopes. Validate and fill a response through the normal order flow.

The response must match the market and derivative/quote kinds and authorize the requester. Linking does not enforce that its live quantity equals the requested amount; display the actual offer terms before acceptance.

For a new market, set request pt to the zero address. The responding curator deploys a PT for the requested IBT. The API accepts maturity from the requested timestamp through 24 hours later to accommodate the factory's relative-duration deployment. Show the resulting maturity to the requester.

CancelQuoteRequest withdraws the request. Cancellation, expiry or filling another response does not invalidate any response order. A declined recipient cannot become responded; an existing response can only be relinked to the same order.

Safe makers

Use buildOtcSignOrderIntent from otc/signing to validate the envelope and build a structured signOrder(Order) delegatecall to the configured signer. Plan approval and signing separately; a queued proposal is not a valid signature. Publish only after confirmed execution and live ERC-1271 verification.

The per-market curator-otc-order template constrains the Safe maker, receiver, asset pairs, traits and signer target. The signer does not inspect the extension; shared validation remains necessary. Generic message-signing permission is not required. Generated approval amounts are not permission-level spending caps.

The current curator interface fills EOA-made orders only. Safe-made offers can be taken through the retail app's contract-maker path. Batch fills and tokenization during a fill are outside this OTC flow.

On this page