Listing period
The seller wants to offer an item so the buyers can discover and commit to it.
/
LISTED
COMMITTED
Period actions
Create an offer
Create an offer
/
*Premint vouchers
Commit to an offer
*Create a group
*Void an unlisted offer
*Create a twin
*Commit to a price discovery offer
*Create a bundle
*Create and commit to an offer
Void an offer
Void an unlisted offer
Extend an offer
*Update offer royalty recipients
*Commit to a price discovery offer
*Create and commit to an offer
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.
Creator: information whether offer is created by the seller or the buyer (default: seller)
Buyer ID: the buyer's Boson account identifier. Used only if the offer is created by the buyer.
Mutualizer: the address of the contract that covers the Dispute Resolver Fee. If set to 0, the Fee is covered by the seller.
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 offerArgs = {
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",
creator: 0, // seller
}
await sellerCoreSDK.createOffer(offerArgs)The seller can make an offer uncommittable before it expires. The existing commits and exchanges are unaffected.
const offerId = "5";
await sellerCoreSDK.voidOffer(offerId);Multiple offers can be voided in a single transaction.
The seller extends the offer validity before it expires. The existing commits and exchanges are unaffected.
const offerId = "5";
const offer = await coreSDK.getOfferById(offerId);
const newValidUntil = offer.validUntilDate + 1000;
await sellerCoreSDK.extendOffer(offerId, newValidUntil.toString());Multiple offers can be extended in a single transaction.
const offerId1 = "5";
const offerId2 = "10";
const offer = await coreSDK.getOfferById(offerId);
const newValidUntil = offer.validUntilDate + 1000;
await sellerCoreSDK.extendOfferBatch([offerId1, offerId2], newValidUntil.toString());For seller-initiated offers, the buyer commits to an offer and receives a tradeable rNFT.
const offerId = "5";
await buyerCoreSDK.commitToOffer(offerId);For buyer-initiated offers, the seller commits. The seller can provide additional info, unknown to the buyer at offer creation time (collection index, royalty info and mutualizer). The buyer receives a tradeable rNFT.
const offerId = "5";
const sellerParams = {
collectionIndex: "0",
royaltyInfo: {
recipients: [],
bps: [],
}
mutualizerAddress: "0x00000000000000000000000000000000",
}
await sellerCoreSDK.commitToBuyerOffer(offerId, sellerParams);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,
creator: BosonTypes.OfferCreator.Seller,
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
});
BosonTypes.DRParameters memory drParameters = BosonTypes.DRParameters({
disputeResolverId: 1,
mutualizerAddress: address(0) // self-mutualization
});
uint256 agentId = 0; // no agent
uint256 feeLimit = 100000000000000000; // 0.1 ETH
bosonProtocol.createOffer(offer, offerDates, offerDurations, drParameters, 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);
BosonTypes.DRParameters[] memory drParametersArray = new BosonTypes.DRParameters[](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,
creator: BosonTypes.OfferCreator.Seller,
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
});
drParametersArray[i] = BosonTypes.DRParameters({
disputeResolverId: 1,
mutualizerAddress: address(0) // self-mutualization
});
agentIds[i] = 0; // no agent
feeLimits[i] = 100000000000000000; // 0.1 ETH
}
bosonProtocol.createOfferBatch(offers, offerDatesArray, offerDurationsArray, drParametersArray, agentIds, feeLimits);The seller can make an offer uncommittable before it expires. The existing commits and exchanges are unaffected.
IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);
uint256 offerId = 1;
bosonProtocol.voidOffer(offerId);Multiple offers can be voided in a single transaction.
IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);
uint256[] memory offerIds = new uint256[](3);
offerIds[0] = 1;
offerIds[1] = 2;
offerIds[2] = 3;
bosonProtocol.voidOfferBatch(offerIds);The seller extends the offer validity before it expires. The existing commits and exchanges are unaffected.
IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);
uint256 offerId = 5;
(, , BosonTypes.OfferDates memory offerDates, , , ,) = bosonProtocol.getOffer(offerId);
uint256 newValidUntil = offerDates.validUntil + 1000;
bosonProtocol.extendOffer(offerId, newValidUntil);Multiple offers can be extended in a single transaction.
IBosonOfferHandler bosonProtocol = IBosonOfferHandler(_bosonProtocolAddress);
uint256 offerId1 = 5;
uint256 offerId2 = 10;
(, , BosonTypes.OfferDates memory offerDates, , , ,) = bosonProtocol.getOffer(offerId1);
uint256 newValidUntil = offerDates.validUntil + 1000;
uint256[] memory offerIds = new uint256[](2);
offerIds[0] = offerId1;
offerIds[1] = offerId2;
bosonProtocol.extendOfferBatch(offerIds, newValidUntil);For seller-initiated offers, the buyer commits to an offer and receives a tradeable rNFT.
IBosonExchangeCommitHandler bosonProtocol = IBosonExchangeHandler(_bosonProtocolAddress);
address payable buyer = address(this);
uint256 offerId = 5;
bosonProtocol.commitToOffer(buyer, offerId);For buyer-initiated offers, the seller commits. The seller can provide additional info, unknown to the buyer at offer creation time (collection index, royalty info and mutualizer). The buyer receives a tradeable rNFT.
IBosonExchangeCommitHandler bosonProtocol = IBosonExchangeHandler(_bosonProtocolAddress);
uint256 offerId = 5;
BosonTypes.SellerOfferParams memory sellerOfferParams = BosonTypes.SellerOfferParams({
collectionIndex: 1,
royaltyInfo: new BosonTypes.RoyaltyInfo[](0), // no royalties
mutualizerAddress: address(0)
});
bosonProtocol.commitToBuyerOffer(offerId);The seller and buyer can negotiate the offer parameters off-chain. Once they agree on them, one party signs the parameters and the other submits them the the protocol. The one who signed them is considered the offer creator, while the one submitting them is committing to that offer.
The other party signed the offer, and this contract is submitting the transaction
IBosonExchangeCommitHandler bosonProtocol = IBosonExchangeHandler(_bosonProtocolAddress);
BosonTypes.FullOffer memory fullOffer;
address offerCreator = 0x2a91A0148EE62fA638bE38C7eE05c29a3e568dD8;
address payable committer = address(this);
bytes calldata signature = 0x...; // must be provided by other signer;
uint256 conditionalTokenId = 0;
BosonTypes.SellerOfferParams memory sellerParams; // use the default parameters
fullOffer.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,
creator: BosonTypes.OfferCreator.Seller,
metadataUri: "https://offerMetadata.uri",
metadataHash: "0xba5348e4fa8d40f569f0a83ab38ab799a3ef1a35b1",
voided: false,
collectionIndex: 0,
royaltyInfo: new BosonTypes.RoyaltyInfo[](0) // no royalties
});
fullOffer.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
});
fullOffer.offerDurations = BosonTypes.OfferDurations({
disputePeriod: 40 * MSEC_PER_DAY, // 40 days
voucherValid: 0,
resolutionPeriod: 50 * MSEC_PER_DAY // 50 days
});
fullOffer.drParameters = BosonTypes.DRParameters({
disputeResolverId: 1,
mutualizerAddress: address(0) // self-mutualization
});
fullOffer.feeLimit = 100000000000000000; // 0.1 ETH
bosonProtocol.createOfferAndCommit(fullOffer, offerCreator, committer, signature, conditionalTokenId, sellerParams);The other party is submitting the transaction, and this contract uses EIP1271 verification
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
function isValidSignature(bytes32, bytes calldata) public view override returns (bytes4) {
// validate the signature, e.g. ECDSA verification
...
// If everything is ok, return the correct magic value
return IERC1271.isValidSignature.selector;
}Widget
The buyer commits to an offer and receives a tradeable rNFT.
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>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
Last updated