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:
| Side | Description | Typical use |
|---|---|---|
| Ask | Buyer (or someone on their behalf) fulfils | AMM / bonding curve, Seaport listing |
| Bid | Seller fulfils after an off-chain bid | Sealed-bid auction, RFQ |
| Wrapper | A wrapper contract has already settled externally; the protocol just finalises | On-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
- Snapshot balances.
- Call the price-discovery contract with
priceDiscoveryData. - Compute
actualPrice = tokens flowed in − unused refund. - 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
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:
- Accepts a real Boson voucher, mints a wrapped twin to the sender.
- Implements an
unwrap(tokenId, recipient)(or similar) entry point that transfers the real voucher back out and sends the proceeds to the Diamond. - 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
BosonPriceDiscoveryClientwhich 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
- Sellers → Pre-minted vouchers — required prerequisite for most discovery flows.
- Buyers → Sequential commit — secondary-market resale uses the same price-discovery machinery.
- Reference → Contracts → Diamond facets →
PriceDiscoveryHandlerFacet.