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 field | Registry key |
|---|---|
limitOrderProtocol | spectra.otc.settlement |
rateAmountGetter | spectra.otc.rate_getter |
authorizedTakersGuard | spectra.otc.takers_guard |
orderSigner | spectra.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.
| Term | Meaning |
|---|---|
derivativeKind | 0 = PT, 1 = YT. |
quoteKind | 0 = underlying, 1 = IBT, from the same market. |
makingAmount | Fixed quantity sent by the maker, in that token's base units. |
takingAmount | Creation-time baseline; use the getter for the current payment. |
impliedApyWad | Annually compounded rate: 5% is 50000000000000000. |
| Expiry | Deadline 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.
- Resolve configuration and verify the market's PT, YT, IBT and underlying addresses onchain.
- Build the canonical extension, salt and traits. Allocate a unique nonce per maker and settlement domain; reusing it can invalidate multiple orders.
- Call the getter's
previewOrder, set the creation baseline and cross-check the local hash with the settlement'shashOrder. - Approve the maker asset to the settlement and sign the order. Its EIP-712
domain is
1inch Limit Order Protocol, version4, with the chain ID and settlement asverifyingContract. - 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:
| Maker | Entrypoint |
|---|---|
| EOA | fillOrderArgs, using the compact EIP-2098 signature. |
| Contract wallet | fillContractOrderArgs, 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 path | Request |
|---|---|
GET /orders | Optional market (PT address), makerAddress, derivativeKind, includeInactive, limit, offset. |
POST /create | { envelope: OtcOrderEnvelope }. |
GET /rfq/requests | Exactly 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.
- The requester signs
QuoteRequest: requester, derivative/quote kinds, requester-sideBUYorSELL, underlying, IBT, PT, maturity, derivative amount, recipient Safe addresses, request expiry and salt. - POST its
QuoteRequestEnvelope. The API allows up to 16 recipient vaults and 20 open requests per requester per chain. The expiry cannot exceed maturity. - Each vault either signs
DeclineQuoteRequestwith 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. - The requester view includes per-vault
PENDING,DECLINEDorRESPONDEDstates 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.