30817:nip-xx-dutch-auctions-reverse-procurement

NIP-XX: Dutch Auctions & Reverse Procurement

Milosz

published
2026-07-09

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

  1. Deterministic price — pure function of signed schedule + time. Clients reject commits whose price doesn't match (±30s tolerance).
  2. First-commit-wins — earliest validly-signed kind 1402 whose price matches clears the auction.
  3. Reputation on-graph — cleared-deal history and reviews as public Nostr events. No proprietary scores.
  4. No sealed bids — only settlement details (invoices) may use NIP-44 encryption where needed.
  5. 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.