Sequential commit
A normal Boson voucher (rNFT) can be resold on any ERC-721 marketplace, but the protocol doesn't see those resales. If the original seller fails to deliver, escrow only covers the original price — secondary buyers who paid more take a loss.
Sequential commit solves this by routing secondary sales through the protocol via sequentialCommitToOffer. Each resale tops up the escrow with the price difference, so every buyer in the chain is protected up to what they actually paid. Royalties for each resale are recorded and paid out at finalisation. The protocol becomes the canonical secondary marketplace for redeemable vouchers.
This is a buyer-side action: a new buyer is committing to an existing exchange, taking ownership from the previous holder. The pricing happens through the same price-discovery machinery used for primary sales — Ask, Bid, and Wrapper flows all work.
Why it matters for buyers
- Pay what you mean to pay. Buy a voucher on a secondary market and your protection scales with your purchase price, not the original.
- Royalty enforcement is on-chain. Every resale recorded in the protocol pays the offer's royalty recipients automatically — no marketplace-by-marketplace tweaking.
- The voucher behaves the same post-commit. Redeem, dispute, complete all work as normal.
How it works
Exchange in COMMITTED state, owned by reseller A
Where reseller A is the original buyer or a prior resale buyer.
sequentialCommitToOffer(newBuyer, exchangeId, PriceDiscovery)
The new buyer (or someone on their behalf) calls into the protocol with an Ask | Bid | Wrapper price-discovery payload.
Diamond executes the hop
- Resolve the price via the external price-discovery contract.
- Compute the escrow delta — new money locked vs. already locked.
- Pull the delta from the new buyer.
- Release the prior price to the previous holder (minus protocol fees, royalties, and the cancellation-penalty cap).
- Transfer the voucher to the new buyer.
- Record
(price, fees, royalty recipients)for this hop so finalisation pays everyone fairly.
Exchange remains in COMMITTED state, now owned by newBuyer
The redemption clock is unchanged; the new buyer can redeem / dispute / complete as normal.
If the new price is higher than the previous price, the delta is locked in escrow and the reseller's profit (after fees / royalties / capped buyer-cancellation-penalty) is released immediately. If the new price is equal or lower, no additional escrow is needed — the reseller just gets the secondary price and the protocol records the hop. Either way, on the worst-case finalisation (e.g. original seller revokes), every party gets reimbursed to their net cost basis.
When to use
- You're buying a voucher second-hand and want full protocol protection.
- You're building a secondary marketplace for Boson vouchers — route trades through
sequentialCommitToOfferinstead of plain ERC-721 transfers.
For a sale that doesn't route through the protocol (e.g. a normal transferFrom), buyer protection stays at the original price level. That's not a bug — the protocol can't see what it can't see.
Doing it from code
The sequential-commit surface mirrors price discovery. Use the same PriceDiscovery struct (side, priceDiscoveryContract, priceDiscoveryData, etc.); the only difference is the entry point and the identifier (exchange ID, not offer ID).
// At the current Core SDK version, there is no `sdk.sequentialCommitToOffer`
// helper. Build the call against the Diamond's `IBosonSequentialCommitHandler`
// facet via ethers — see Reference → Contracts → Facets.
import type { PriceDiscoveryStruct } from "@bosonprotocol/common"
import IBosonSequentialCommitHandler from "@bosonprotocol/common/dist/cjs/abis/IBosonSequentialCommitHandler.json"
const pd: PriceDiscoveryStruct = {
price: secondaryPrice,
side: 0, // Ask
priceDiscoveryContract: SEAPORT_ADDRESS,
conduit: SEAPORT_CONDUIT_ADDRESS,
priceDiscoveryData: encodedFulfilmentCalldata,
}
const diamond = new ethers.Contract(DIAMOND_ADDRESS, IBosonSequentialCommitHandler, signer)
await (await diamond.sequentialCommitToOffer(
newBuyerAddress,
exchangeId,
pd,
{ value: pd.price }, // for native-token offers
)).wait()The reseller must approve the protocol to pull the voucher (Bid / Wrapper) or list it on the price-discovery contract in advance (Ask). See Price discovery for the per-side mechanics — the rules are identical.
Batch expiration (anyone can call)
A buyer who never redeems and never disputes lets the voucher expire silently. To clean up these dead exchanges and release any locked funds, anyone can run the expiration tools:
| Tool | When it applies |
|---|---|
expire_dispute / expire_dispute_batch (MCP) / expireDispute (SDK) | Disputes that have aged out of the resolution window with no action |
expire_escalated_dispute (MCP) / expireEscalatedDispute (SDK) | Escalated disputes where the dispute resolver didn't act before the response window closed |
These are idempotent — running them on an already-expired exchange is a no-op. Gas is paid by the caller. They're useful for marketplace operators and DR ops who want their finalisation backlog cleared.
Finalisation math
At final state (COMPLETED, DISPUTED → RESOLVED, etc.) the protocol pays out:
- Original seller — based on the latest sale price, minus protocol fee, agent fee, royalties, and any buyer-cancellation-penalty cap.
- Each prior reseller — the difference between what they were paid at their hop and what they should keep given the final state, with royalties always paid for every hop the voucher passed through.
- Royalty recipients — sum of each hop's contribution.
- Final buyer — whatever's left if the seller failed (revoked, lost dispute).
Common gotchas
- Capital efficiency. The protocol locks only the minimum delta needed. If price stays flat or drops, no extra capital required.
- Royalties paid at finalisation, not at each hop. Even though resale records the recipient list, the actual transfer happens once at the end. Designers of secondary marketplaces that expect immediate royalty payout should plan for this.
- Vouchers can't be sequentially committed across the dispute boundary. Once
DISPUTED, secondary resale via the protocol is closed — the next buyer would inherit the dispute, which is too messy. - Off-protocol transfers still work (plain ERC-721
transferFrom) but bypass all this machinery — the new holder gets only the original seller's escrow as protection.
Related
- Sellers → Price discovery — sequential commit reuses the same machinery.
- Sellers → Royalties — royalty recipients are paid per hop at finalisation.
- Concepts → Exchange state machine.
- Reference → Contracts → Diamond facets →
SequentialCommitHandlerFacet.