Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Price discovery · Boson Protocol
Skip to content

Price discovery

A vanilla Boson offer carries a fixed price. Price discovery generalises this to a callable price discovery contract — anything from a Seaport English auction to a Sudoswap bonding curve to a custom order book. The Diamond doesn't make any market itself; it delegates to an external contract at commit time, observes the actual price paid, and encumbers the right amount in escrow.

The price discovery integration runs through a dedicated facet (PriceDiscoveryHandlerFacet) and a stateless helper contract (BosonPriceDiscoveryClient) that owns nothing but executes the external calls — keeping the Diamond out of unrestricted-call surface area.

The three flows

Every price-discovery commit has a side that determines who fulfils the order:

SideDescriptionTypical use
AskBuyer (or someone on their behalf) fulfilsAMM / bonding curve, Seaport listing
BidSeller fulfils after an off-chain bidSealed-bid auction, RFQ
WrapperA wrapper contract has already settled externally; the protocol just finalisesOn-chain auction where the wrapper poses as the seller

The price-discovery payload is the same struct in all three cases:

struct PriceDiscovery {
    uint256 price;                  // buyer's expected price
    Side side;                      // Ask | Bid | Wrapper
    address priceDiscoveryContract; // e.g. Seaport
    address conduit;                // approved-to-pull contract
    bytes priceDiscoveryData;       // arbitrary calldata for the external contract
}

Prerequisite: pre-mint

Price discovery only makes sense for pre-minted offers. The voucher must already exist as an ERC-721 (so it can be sold on external marketplaces) before discovery happens. See Pre-minted vouchers.

createOffer with priceType = PriceDiscovery

Mark the offer as price-discovery-driven at creation time.

depositFunds → seller pool

Fund the seller treasury so the protocol can pull the seller deposit at commit time.

reserveRange + preMint

Reserve token IDs and pre-mint the vouchers. They now exist as ERC-721s.

List vouchers on an external marketplace

Seaport, AMM, etc. — the vouchers are normal NFTs from the marketplace's point of view.

Buyer or seller calls commitToPriceDiscoveryOffer

Pass the PriceDiscovery payload (Ask | Bid | Wrapper). The Diamond delegates to the external contract and finalises the commit.

Ask flow — buyer fulfils

Typical example: a seller drops vouchers into a Sudoswap bonding-curve pool. A buyer wants to buy at the current curve price.

Buyer approves Boson Diamond for the exchange token

Seller pre-approves too — the seller deposit is encumbered at commit.

commitToPriceDiscoveryOffer(buyer, tokenIdOrOfferId, priceDiscovery {Ask, …})

The buyer (or someone on their behalf) calls into the Diamond.

Diamond → BosonPriceDiscoveryClient

  1. Snapshot balances.
  2. Call the price-discovery contract with priceDiscoveryData.
  3. Compute actualPrice = tokens flowed in − unused refund.
  4. Transfer the received voucher back to the Diamond.

Diamond finalises the exchange

Pulls the seller deposit from the seller pool, mints the exchange, and transfers the voucher to the buyer.

If the actual price < the price the buyer expected, the surplus is refunded.

Bid flow — seller fulfils

Typical example: a sealed-bid off-chain auction. The seller runs the auction, picks a winner, and finalises through the protocol.

Seller approves Boson Diamond for the voucher

So the Diamond can pull the voucher when the seller's bid is accepted.

commitToPriceDiscoveryOffer(buyer, tokenIdOrOfferId, priceDiscovery {Bid, …})

The seller initiates the on-chain settlement after an off-chain bid.

Diamond pulls voucher and calls the price-discovery contract

For example, Seaport's match to settle the bid.

Diamond verifies actualPrice ≥ seller's expected price

Reverts otherwise — the whole commit fails atomically.

If the auction returns less than the seller said they'd accept, the whole commit reverts.

Wrapper flow — pre-settled

Typical example: an on-chain auction (like a wrapped Seaport listing) that must be the on-chain seller of the voucher. The seller transfers the voucher to a wrapper contract; the wrapper sells it via the auction; either party then calls commitToPriceDiscoveryOffer to "unwrap" — the wrapper transfers the true voucher to the auction winner and sends the proceeds to the Diamond.

Seller transfers voucher → wrapper

Receives a wrapped voucher in return.

Wrapper lists the wrapped voucher in the auction

The wrapper now poses as the on-chain seller.

Auction concludes

The winner holds the wrapped voucher; proceeds sit in the wrapper.

Either party calls commitToPriceDiscoveryOffer with side = Wrapper

Diamond invokes the wrapper's unwrap method. The wrapper sends the true voucher to the winner and forwards the proceeds to the Diamond.

Diamond encumbers the proceeds

The voucher is now a normal redeemable rNFT in the protocol.

Wrappers are use-case-specific; the protocol doesn't ship a canonical one, just the protocol-side machinery.

Calling from code

SDK
import type { PriceDiscoveryStruct } from "@bosonprotocol/common"
 
const pd: PriceDiscoveryStruct = {
  price: expectedPrice,
  side: 0,                  // 0 = Ask, 1 = Bid, 2 = Wrapper
  priceDiscoveryContract: SEAPORT_ADDRESS,
  conduit: SEAPORT_CONDUIT_ADDRESS,
  priceDiscoveryData: encodedSeaportFulfilmentCalldata,
}
 
const tx = await sdk.commitToPriceDiscoveryOffer(
  buyerAddress,
  tokenIdOrOfferId,
  pd,
  { value: pd.price }, // only for native-token offers
)

Building a wrapper

A wrapper contract is just an ERC-721 receiver that:

  1. Accepts a real Boson voucher, mints a wrapped twin to the sender.
  2. Implements an unwrap(tokenId, recipient) (or similar) entry point that transfers the real voucher back out and sends the proceeds to the Diamond.
  3. Approves the Boson Price Discovery client to pull the unwrapped voucher.

The shape is intentionally use-case-driven; see the SeaportWrapper in the contracts repo for a reference implementation.

Common gotchas

  • Currency mismatch. The external contract must trade in the offer's exchangeToken. The Diamond reverts if the actual flow is in a different currency.
  • Negative implied price. If post-call tracking reports the protocol lost tokens (or received fewer than zero net), the commit reverts to protect the protocol's solvency.
  • Reentrancy surface. All external calls flow through BosonPriceDiscoveryClient which holds no funds and is treated as adversarial by the Diamond. Don't reuse this contract for anything else; you'll break the trust boundary.
  • Conduit approval. For Seaport flows, the seller (or the wrapper) must approve Seaport's conduit to pull the voucher — not the Diamond.

Related