lightninglabs / lightninglabs/taproot-assets

[feature]: RFQ: First-Class Limit Orders In Quote Negotiation

Open
#2,004 1 comment 0 reactions 1 assignee View on GitHub

@jtobin is already working on this.

Since Feb 24, 2026.

  • #2053 by @jtobin — merged
enhancement
Dominant language
Go
Stars
525
Forks
150
Avg merge
2d 15h
Merged PRs (30d)
31

Description

## Summary
Today, RFQ orders are mostly constrained by:
- Requested max size (`asset_max_amt` or `payment_max_amt`)
- Quote expiry
- Channel availability and balance shape
- Price-oracle tolerance checks

This is sufficient for basic RFQ flows, but it is not a full limit-order model. We should add explicit limit-order semantics (price bounds, fill constraints, and execution policy) to quote negotiation.

## Why This Matters
Limit-order semantics enable:
- Price protection: avoid execution outside user-defined acceptable price bounds.
- Slippage control: reduce risk from stale hints, spread moves, and short-term volatility.
- Deterministic UX: users can express explicit execution intent instead of best-effort behavior.
- Safer automation: wallet agents can auto-negotiate with tighter risk controls.
- Better policy explainability: clearer machine-readable reject reasons.
- Better multi-peer execution: easier to split across peers while preserving user constraints.

## Terminology
- `IOC` (`Immediate-Or-Cancel`): execute immediately for any amount that satisfies constraints; cancel any unfilled remainder.
- `FOK` (`Fill-Or-Kill`): execute immediately and fully, or cancel the entire order.

## Current State (As-Is)
### What RFQ can express today
- Buy flow: max asset amount (`AssetMaxAmt`) + expiry + peer + metadata.
- Sell flow: max payment amount (`PaymentMaxAmt`) + expiry + peer + metadata.
- Request/accept negotiation primarily settles on a rate + expiry.

### What is enforced in practice
- Requester-side quote validation checks include quote expiry bounds and oracle tolerance against the accepted rate.
- HTLC/policy enforcement is max-cap based, where sale path policy derives from `AssetMaxAmt` and purchase path policy derives from `PaymentMaxAmt`.
- Liquidity constraints are effectively channel-driven: channel existence checks gate order submission, and channel balances influence selection/sorting.

### Existing but underused protocol surface
- Wire format already includes optional `MinInAsset` and `MinOutAsset` fields.
- These min fields are not first-class in typed request structs and RPC order APIs.

### Current gap
Current behavior is closer to:
- "Market quote with max-size cap + expiry"
not:
- "Limit order with explicit price/fill constraints"

Users cannot directly encode:
- Minimum acceptable price / rate.
- Maximum acceptable price / rate.
- Minimum fill size.
- Execution policy (`IOC` vs `FOK`).

## Future Work (Proposed Workstreams)
### Workstream A: Add explicit limit constraints to request surfaces
- Add optional limit-price/rate bound fields to buy/sell order APIs.
- Add optional fill bound fields (`min_in_asset`, `min_out_asset`) to typed requests and RPC.
- Keep current max fields as-is for compatibility.
- Define a canonical rate-bound representation that avoids buy/sell direction ambiguity.

### Workstream B: Enforce limits during negotiation
- Responder side (`ResolveRequest`): reject when request limits cannot be satisfied.
- Requester side (`VerifyAcceptQuote`): enforce explicit requester limits in addition to oracle tolerance.
- Return explicit reject reasons for limit failures (price bound miss, min fill miss, insufficient liquidity).

### Workstream C: Add execution policy semantics
- Support explicit execution policy on orders.
- `IOC`: allow partial immediate execution above minimum fill.
- `FOK`: require full immediate execution or reject.
- Ensure policy is respected consistently in quote acceptance and HTLC policy installation.

### Workstream D: Negotiate explicit fill quantities
- Add accepted fill quantity semantics to quote acceptance path.
- Bind policy persistence and HTLC enforcement to negotiated fill values, not only request max caps.
- This removes ambiguity around partial fills and improves deterministic execution.

### Workstream E: Multi-peer selection that respects limits
- When multiple peers are available, rank and split using effective rate and usable capacity.
- Ensure every shard respects the same order limits and execution policy.

## Non-Goals
- Building a centralized order book.
- Long-lived resting/GTC orders in initial implementation.
- Redesigning the external price-oracle architecture.

## Compatibility Expectations
- New fields should be additive and optional.
- Old peers should continue to interoperate under current behavior.
- Limit-specific validation should only activate when limit fields are present.
- Use versioning only where strict incompatibility requires it.

## Acceptance Criteria
- Users can specify explicit limit-price bounds for buy and sell RFQ orders.
- Users can specify minimum fill constraints.
- Users can choose execution policy (`IOC` or `FOK`).
- Quotes violating constraints are rejected with clear machine-readable reasons.
- Accepted quotes cannot pass validation if they violate requester limits.
- Policy/HTLC enforcement is tied to negotiated constraints, not only broad request max caps.
- Test coverage includes buy and sell flows.
- Test coverage includes `IOC` and `FOK` behaviors.
- Test coverage includes backward compatibility with old peers.
- Test coverage includes volatility and timing edge cases.

## Open Questions
- What is the canonical limit-price representation to avoid buy/sell interpretation errors?
- Should both min and max rate bounds be supported initially, or only one directional bound per order type?
- Do we extend `QuoteRespStatus`, add new reject codes, or both?
- Is explicit fill quantity required in the first delivery, or can it follow once request-side limits are live?
- Should passive offer filtering and limit-order checks be merged into one decision pipeline?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.