Listing period

The seller wants to offer an item so the buyers can discover and commit to it.

Previous state
State
Next state(s)

/

LISTED

COMMITTED

Period actions

Seller
Buyer
Dispute resolver

Create an offer

Commit to an offer

/

*Premint vouchers

*Commit to price discovery offer

*Create a group

*Create a twin

*Create a bundle

Void an offer

Extend an offer

*Update offer royalty recipients

Actions marked with * are Advanced use cases and are described at the bottom.


Basic offer parameters

Boson dACP supports a variety of different offers. The basic offer has the following parameters:

  • Price: the amount the buyer needs to pay in order to receive rNFT. The price can be fixed or unknown at offer creation. If the price is unknown, the seller can put the rNFT in any ERC721 compatible external price discovery mechanism (NFS marketplaces, AMM) and the price is observed only at the commitment time.

  • Seller deposit: the amount that is added to the price and given to the buyer if the seller revokes the rNFT before it is redeemed.

  • Buyer cancellation penalty: the amount that is deducted from the price before it is returned to the buyer when the buyer cancels the rNFT before it is redeemed. The cancellation penalty is given to the seller.

  • Available quantity: the maximal number of rNFTs that can be issued for the offer. After the offer is depleted, new commits are automatically prevented. It is possible to make unlimited offers.

  • Exchange token: the currency for all financial properties (price, seller deposit, buyer cancelation penalty, dispute resolver fee). It can be any ERC20 address or native token.

  • Metadata URI and hash: additional metadata, describing the asset and normally stored off-chain. URI is reference to metadata location and hash is used to prove data validity. The metadata format and hashing method are arbitrary and not observable by the protocol.

  • Collection index: the seller can create multiple rNFT collections and choose for each offer, where corresponding rNFTs are issued.

  • Offer validity: the starting and ending date when the buyers can commit to the offer.

  • Redemption parameters: the definition when the rNFT can be redeemed. It can be the same for all rNFTs (fixed starting and ending date), or can be dynamic and starts only when the buyer commits and lasts for a predefined period.

  • Dispute period: the length of the period during which the asset must be delivered.

  • Resolution period:the length of the period during which the dispute must be resolved.

  • Royalty info: the addresses and corresponding percentages that are awarded to them on every secondary trade.

  • Dispute resolver: the identifier of the dispute resolver that resolves the escalated dispute.

You can read more about other use cases at the bottom of the page in Advanced use cases

TypeScript SDK

The seller creates an offer, which allows the buyers to commit.

const MSEC_PER_DAY = 24 * 60 * 60 * 1000;

const offer = {
    collectionIndex: "0",
    price: parseEther("0.03"),
    sellerDeposit: parseEther("0.01"),
    agentId: "0",
    buyerCancelPenalty: parseEther("0.01"),
    quantityAvailable: 10,
    validFromDateInMS: Date.now() + 1000,
    validUntilDateInMS: Date.now() + 20 * MSEC_PER_DAY,
    voucherRedeemableFromDateInMS: Date.now() + 1000,
    voucherRedeemableUntilDateInMS: Date.now() + 30 * MSEC_PER_DAY,
    disputePeriodDurationInMS: 40 * MSEC_PER_DAY,
    voucherValidDurationInMS: 0,
    resolutionPeriodDurationInMS: 50 * MSEC_PER_DAY,
    exchangeToken: 0x00000000000000000000000000000000, // native token
    disputeResolverId: "1",
    metadataUri: "https://offerMetadata.uri",
    metadataHash: "0xba5348e4fa8d40f569f0a83ab38ab799a3ef1a35b1",
}

await sellerCoreSDK.createOffer(offerArgs)

Multiple offers can be created in a single transaction.

Solidity

The seller creates an offer, which allows the buyers to commit.

IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);

uint256 MSEC_PER_DAY = 24 * 60 * 60 * 1000;

BosonTypes.Offer memory offer = BosonTypes.Offer({
    id: 0, // will be ignored and auto-assigned
    sellerId: 1, // existing seller ID
    price: 30000000000000000, // 0.03 ETH
    sellerDeposit: 10000000000000000, // 0.01 ETH
    buyerCancelPenalty: 10000000000000000, // 0.01 ETH
    quantityAvailable: 10,
    exchangeToken: address(0), // native token (ETH)
    priceType: BosonTypes.PriceType.Static,
    metadataUri: "https://offerMetadata.uri",
    metadataHash: "0xba5348e4fa8d40f569f0a83ab38ab799a3ef1a35b1",
    voided: false,
    collectionIndex: 0,
    royaltyInfo: new BosonTypes.RoyaltyInfo[](0) // no royalties
});

BosonTypes.OfferDates memory offerDates = BosonTypes.OfferDates({
    validFrom: block.timestamp + 1000, 
    validUntil: (block.timestamp + 1000) + (20 * MSEC_PER_DAY),
    voucherRedeemableFrom: block.timestamp + 2000,
    voucherRedeemableUntil: (block.timestamp * 2000) + (30 * MSEC_PER_DAY) // 30 days
});

BosonTypes.OfferDurations memory offerDurations = BosonTypes.OfferDurations({
    disputePeriod: 40 * MSEC_PER_DAY, // 40 days
    voucherValid: 0,
    resolutionPeriod: 50 * MSEC_PER_DAY // 50 days
});

uint256 disputeResolverId = 1;
uint256 agentId = 0; // no agent
uint256 feeLimit = 100000000000000000; // 0.1 ETH

bosonProtocol.createOffer(offer, offerDates, offerDurations, disputeResolverId, agentId, feeLimit);

Multiple offers can be created in a single transaction.

IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);

uint256 MSEC_PER_DAY = 24 * 60 * 60 * 1000;

// Create array of 3 offers
BosonTypes.Offer[] memory offers = new BosonTypes.Offer[](3);
BosonTypes.OfferDates[] memory offerDatesArray = new BosonTypes.OfferDates[](3);
BosonTypes.OfferDurations[] memory offerDurationsArray = new BosonTypes.OfferDurations[](3);
uint256[] memory disputeResolverIds = new uint256[](3);
uint256[] memory agentIds = new uint256[](3);
uint256[] memory feeLimits = new uint256[](3);

// Configure each offer
for (uint256 i = 0; i < 3; i++) {
    offers[i] = BosonTypes.Offer({
        id: 0, // will be ignored and auto-assigned
        sellerId: 1, // existing seller ID
        price: 30000000000000000, // 0.03 ETH
        sellerDeposit: 10000000000000000, // 0.01 ETH
        buyerCancelPenalty: 10000000000000000, // 0.01 ETH
        quantityAvailable: 10,
        exchangeToken: address(0), // native token (ETH)
        priceType: BosonTypes.PriceType.Static,
        metadataUri: "https://offerMetadata.uri",
        metadataHash: "0xba5348e4fa8d40f569f0a83ab38ab799a3ef1a35b1",
        voided: false,
        collectionIndex: 0,
        royaltyInfo: new BosonTypes.RoyaltyInfo[](0) // no royalties
    });

    offerDatesArray[i] = BosonTypes.OfferDates({
        validFrom: (block.timestamp * 1000) + 1000,
        validUntil: (block.timestamp * 1000) + (20 * MSEC_PER_DAY),
        voucherRedeemableFrom: (block.timestamp * 1000) + 1000,
        voucherRedeemableUntil: (block.timestamp * 1000) + (30 * MSEC_PER_DAY)
    });

    offerDurationsArray[i] = BosonTypes.OfferDurations({
        disputePeriod: 40 * MSEC_PER_DAY,
        voucherValid: 0,
        resolutionPeriod: 50 * MSEC_PER_DAY
    });

    disputeResolverIds[i] = 1;
    agentIds[i] = 0; // no agent
    feeLimits[i] = 100000000000000000; // 0.1 ETH
}

bosonProtocol.createOfferBatch(offers, offerDatesArray, offerDurationsArray, disputeResolverIds, agentIds, feeLimits);

Widget

The buyer commits to an offer and receives a tradeable rNFT.

  1. Add the following <script> entries, either in <head> or <body> of their website:

<script type="text/javascript" src="https://widgets.bosonprotocol.io/scripts/zoid/zoid.min.js"></script>
<script type="text/javascript" src="https://widgets.bosonprotocol.io/scripts/commit-button.js"></script>
  1. Add the following code wherever you want to display the commit button:

<div id="container"></div>

<script>
  const instance = CommitButton({
    configId: "testing-80002-0",
    context: "iframe",
    productUuid: "086b32-3fcd-00d1-0624-67513e85415c",
    sellerId: "138",
    modalMargin: "2%",
    lookAndFeel: "modal",
    disabled: false,
    buttonStyle: {
      minWidth: "100px",
      minHeight: "100px",
      shape: "rounded",
      color: "white"
    },
    onGetDimensions: function (dimensions) {
      const { offsetHeight, offsetWidth } = dimensions;
      document.querySelector(
        "#container"
      ).style.height = `${offsetHeight}px`;
      document.querySelector(
        "#container"
      ).style.minWidth = `${offsetWidth}px`;
    }
  });

  instance.render("#container");
</script>

You can also update properties dynamically with updateProps(updatedPropertiesObject):

<script>
    let disabled = false;
    const instance = CommitButton({
      /* ... */
      disabled,
    });
    
    instance.render("#container");
    function toggleDisableState() {
      disabled = !disabled;
      instance.updateProps({ disabled });
    }
  </script>

<button onclick="toggleDisableState()" style="margin: 20px;">toggle disable state</button>

The Commit button has been created with zoid so you can use their drivers to have components in your favorite framework.

Discover more...

The Commit Widget is part of the React Component library from Boson Core Component you can discover on this Storybook page. Also, you can play around with the Commit button itself Storybook page.

You can find an example HTML file which embeds the commit button on the the widgets subdomain: https://widgets.bosonprotocol.io/scripts/commit-button-example.html

Advanced use cases

Vouchers with a fixed validity period

In basic offer, the voucher validity starts at commit time and ends after the predefined amount. Each voucher gets its own end of validity.

Alternatively, the validity period can be set to be the same for all the vouchers. The redemption period starts and ends regardless of commitment time.

  • Voucher redeemable to must be higher than Voucher redeemable from

  • Voucher valid must be 0

Unlimited offers

Same as basic offer, except:

  • Quantity available is 2^256-1

  • In normal offers, quantity is decreased at every commit, and once it reaches 0, committing becomes impossible. In unlimited offers, the quantity is never decreased. Committing is stopped once the offer expires, it’s voided or the seller deposit cannot be covered by the available funds

Non-default collection

Sellers can have multiple collections, where rNFTs are issued, which allows better organization. Each seller has a default collection, which is initialized when the seller is created. If they want they can add new collections later.

To assign an offer to a non-default collection

  • Set collection index to non-zero value (collection must exist)

  • If the collection does not exist yet, the seller must create it before creating the offer

Offer with royalties

To add royalty recipients to the offer:

  • Populate the Royalty info with list of recipients and corresponding percentages

  • The recipients must be allowlisteded by the seller’s admin

  • The royalty percentages in the offer must be higher than the minimal percentages defined by the admin

  • The total royalty percentage must be lower than max percentage defined by the protocol

  • The offer’s royalty recipients can be changed at any time

Offer with an agent

Part of the offer proceeds can be paid out to an agent of choice. The percentage that the agent takes is defined by the agent itself.

  • Set agentId to non-zero value

Absolute zero offer

Special offer type, where

  • Price = 0

  • Seller deposit = 0

  • Buyer cancelation penalty = 0

  • DR can also be set to 0 in this case and the dispute cannot be raised

  • Can be used to distribute the freebies (the offer can also be token-gated if the distribution needs to be restricted)

Token-gated offers

Offers can be restricted, so not every one can commit to it.

  • Create offer as normal

  • Define the conditions

    • Evaluation method:

      • Threshold: you need some amount of tokens (applicable to ERC20, ERC1155)

      • SpecificToken: you need an exact token from a range (applicable to ERC721)

    • TokenType (ERC20, ERC721, ERC1155)

    • Token contract address

    • Gating type

      • Per address: how many times a single address commits to an offer

      • Per token id: how many times the same token can be used for committing

    • minTokenId (minimal token Id that can be used; applicable for “SpecificToken” method)

    • Threshold (the amount of tokens needed; applicable for “Threshold” method)

    • Max commits (the number of times the same address/token can be used for committing)

    • maxTokenId (maximal token Id that can be used; applicable for “SpecificToken” method)

  • Create a group with offer(s) and condition. Multiple offers can be gated by the same condition.

  • If the condition already exists, a new offer can be added to it.

Perminted rNFT

Normally rNFT is issued only after the commit. If the seller wants to list in on external marketplace before that, they can permit the rNFTs. They are similar to normal rNFTs, except they are not associated with any exchange in the protocol (i.e. redemption period is inactive). Once the 1st transfer happens, the protocol exchange is created and the period starts. The recipient becomes the 1s buyer.

  • Create offer as normal

  • Call reserve range to specify how many rNFT can be preminted

  • Permint the tokens directly on the rNFT contract

  • List the tokens on marketplace of choice

The proceeds from the sale go to rNFT contract from where they can be moved to the protocol and then withdrawn by the seller.

Before the commit happens, the seller must deposit enough funds to cover both the seller deposit and the price.

Preminted offers can be combined with all the non-default features from above (fixed validity period, non-default collection, royalties, agent offers, unlimited offers, token-gated offers).

Price discovery offers

If the price is unknown at the offer creation date, the seller can create a price discovery offer and put it into an external price discovery mechanism. This can be auctions, AMMs or any other price discovery that is settled on chain. The price is then observed in the protocol at the commit time.

  • Create and offer and set price type to PriceDiscovery

  • Call reserve range to specify how many rNFT can be preminted

  • Permint the tokens directly on the rNFT contract

  • List the tokens on marketplace of choice

  • After the price is established via an external PD discovery, the seller must finalize it in the protocol.

  • The seller deposit must be in the protocol before the finalization, while the price is encumbered during the finalization.

Last updated