lightninglabs / lightninglabs/taproot-assets
[feature]: RFQ: First-Class Limit Orders In Quote Negotiation
- 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.