30817:nip-xx-dutch-auctions-reverse-procurement
NIP-XX: Dutch Auctions & Reverse Procurement
- signed by
- npub19jghh...z78wmn
- published
- 2026-07-09
- event
- 412f234e55...df88
draft optional
This NIP defines time-based price discovery mechanisms for Nostr: descending (Dutch) auctions where a seller's price ticks down from a start price toward a floor, and ascending (reverse-Dutch/procurement) auctions where a buyer's offered price ticks up toward a ceiling. In both modes the price schedule is published once as a signed event; any client computes the current price deterministically from the clock. First valid commit clears the auction.
Motivation
NIP-15 covers fixed-price stalls and traditional ascending bid auctions (kind 30020/1021/1022). This NIP adds a fundamentally different primitive: deterministic, time-based price curves where no live bidding occurs. The seller/buyer publishes a schedule; the first counterparty to commit at the current price wins. This is settlement-light (one payment, one winner), manipulation-resistant (no sniping, no gas wars), and requires no server to referee bids.
Event Kinds
kind 30402 — Auction Listing (addressable, replaceable)
An addressable event that defines a complete auction schedule.
Tags (required):
| Tag | Value | Description |
|---|---|---|
d |
<string> |
Unique auction identifier |
title |
<string> |
Auction title |
mode |
descending | ascending |
Descending = seller's price falls; ascending = buyer's price rises |
start_price |
<int msats> |
Starting price in millisatoshis |
end_price |
<int msats> |
Floor (descending) or ceiling (ascending) in millisatoshis |
start_at |
<int unix> |
Auction start timestamp |
end_at |
<int unix> |
Auction end timestamp |
curve |
linear | stepped | exponential |
Price curve type |
settle |
lightning | onchain | fiat |
Settlement method |
alt |
<string> |
NIP-31 human-readable description |
Tags (optional):
| Tag | Value | Description |
|---|---|---|
summary |
<string> |
Short description |
image |
<url> |
Cover image URL |
t |
<string> |
Category tag (multiple allowed) |
step |
<int seconds> |
Step interval for stepped curves |
step_amount |
<int msats> |
Price change per step |
trust |
reputation | hold-invoice | escrow-2of3 |
Trust/settlement tier |
p |
<hex pubkey> |
Arbiter pubkey (for escrow-2of3) |
requirements |
<string> |
Requirements for ascending (provider eligibility) |
min_reputation |
<int> |
Minimum cleared deals for eligibility |
tribe |
<string> |
Community/tribe scope identifier |
currency |
<string> |
Display currency code (default: sat) |
Content: Human-readable description of the auction item or commission.
kind 1402 — Clearing Commit (regular)
Published by the winner to clear the auction. For descending auctions this is the buyer; for ascending this is the accepting provider.
Tags:
| Tag | Value | Description |
|---|---|---|
a |
30402:<pubkey>:<d-tag> |
Reference to the auction listing |
p |
<hex pubkey> |
Counterparty (auction creator) |
price |
<int msats> |
Computed price at commit time |
commit_at |
<int unix> |
Timestamp of commitment |
bolt11 |
<string> |
Lightning invoice (if applicable) |
alt |
<string> |
NIP-31 fallback |
Content: Optional message from the committer.
Validation: A commit is valid if and only if price matches the deterministic price function evaluated at commit_at within a ±30 second clock-skew tolerance. First valid commit (by created_at) wins; later commits for the same auction are void.
kind 1403 — Deal Acknowledgement / State (regular)
Published by the auction creator to acknowledge and track deal state transitions.
Tags:
| Tag | Value | Description |
|---|---|---|
a |
30402:<pubkey>:<d-tag> |
Reference to the auction listing |
e |
<event id> |
Reference to the clearing commit (kind 1402) |
p |
<hex pubkey> |
Counterparty |
status |
committed | funded | delivered | released | refunded | disputed |
Current deal state |
alt |
<string> |
NIP-31 fallback |
Content: Optional status message or notes.
kind 1404 — Cancellation / No-clear (regular)
Published by the auction creator to cancel an auction or mark it as expired without clearing.
Tags:
| Tag | Value | Description |
|---|---|---|
a |
30402:<pubkey>:<d-tag> |
Reference to the auction listing |
reason |
cancelled | expired | withdrawn |
Reason for cancellation |
alt |
<string> |
NIP-31 fallback |
Content: Optional explanation.
kind 1986 — Deal Review (regular)
Published by either party after a deal is released. Provides on-graph reputation.
Tags:
| Tag | Value | Description |
|---|---|---|
p |
<hex pubkey> |
The reviewed counterparty |
a |
30402:<pubkey>:<d-tag> |
Reference to the auction listing |
e |
<event id> |
Reference to the clearing commit |
rating |
1 | 2 | 3 | 4 | 5 |
Star rating |
role |
buyer | provider |
Role of the reviewer in this deal |
alt |
<string> |
NIP-31 fallback |
Content: Review text (optional).
Deterministic Price Function (normative)
All clients MUST compute the current price using the same pure function. Given an auction's parameters, the current price at time now is:
Linear Curve
elapsed = clamp(now - start_at, 0, end_at - start_at)
progress = elapsed / (end_at - start_at)
For descending:
current_price = start_price - progress * (start_price - end_price)
For ascending:
current_price = start_price + progress * (end_price - start_price)
Stepped Curve
elapsed = clamp(now - start_at, 0, end_at - start_at)
steps_elapsed = floor(elapsed / step)
For descending:
current_price = max(end_price, start_price - steps_elapsed * step_amount)
For ascending:
current_price = min(end_price, start_price + steps_elapsed * step_amount)
Exponential Curve
elapsed = clamp(now - start_at, 0, end_at - start_at)
progress = elapsed / (end_at - start_at)
For descending:
current_price = end_price + (start_price - end_price) * (1 - progress)^2
For ascending:
current_price = start_price + (end_price - start_price) * progress^2
All prices are rounded to the nearest integer (millisatoshi). After end_at, the price rests at end_price.
Protocol Invariants
- Deterministic price — pure function of signed schedule + time. Clients reject commits whose price doesn't match (±30s tolerance).
- First-commit-wins — earliest validly-signed kind 1402 whose price matches clears the auction.
- Reputation on-graph — cleared-deal history and reviews as public Nostr events. No proprietary scores.
- No sealed bids — only settlement details (invoices) may use NIP-44 encryption where needed.
- Non-custodial — funds flow directly between parties or through named arbiter keys. The marketplace client never custodies.
Clock Skew Tolerance
Commits are valid if the claimed commit_at is within ±30 seconds of the relay's received timestamp (created_at). Clients SHOULD use NTP-synchronized clocks and SHOULD warn users when their system clock appears to be significantly off.
Discussion
Connect a key to comment.