Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple collections per seller #592

Merged
merged 18 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ string constant INVALID_DISPUTE_RESOLVER = "Invalid dispute resolver";
string constant INVALID_QUANTITY_AVAILABLE = "Invalid quantity available";
string constant DR_UNSUPPORTED_FEE = "Dispute resolver does not accept this token";
string constant AGENT_FEE_AMOUNT_TOO_HIGH = "Sum of agent fee amount and protocol fee amount should be <= offer fee limit";
string constant NO_SUCH_COLLECTION = "No such collection";

// Revert Reasons: Group related
string constant NO_SUCH_GROUP = "No such group";
Expand Down
6 changes: 6 additions & 0 deletions contracts/domain/BosonTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ contract BosonTypes {
string metadataUri;
string metadataHash;
bool voided;
uint256 collectionIndex;
}

struct OfferDates {
Expand Down Expand Up @@ -285,4 +286,9 @@ contract BosonTypes {
string contractURI;
uint256 royaltyPercentage;
}

struct Collection {
address collectionAddress;
string externalId;
}
}
2 changes: 2 additions & 0 deletions contracts/interfaces/IInitializableVoucherClone.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ interface IInitializableVoucherClone {
* @notice Initializes a voucher with the given parameters.
*
* @param _sellerId - The ID of the seller.
* @param _collectionIndex - The index of the collection.
* @param _newOwner - The address of the new owner.
* @param _voucherInitValues - The voucher initialization values.
*/
function initializeVoucher(
uint256 _sellerId,
uint256 _collectionIndex,
address _newOwner,
BosonTypes.VoucherInitValues calldata _voucherInitValues
) external;
Expand Down
7 changes: 7 additions & 0 deletions contracts/interfaces/events/IBosonAccountEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,11 @@ interface IBosonAccountEvents {
address indexed executedBy
);
event AgentCreated(uint256 indexed agentId, BosonTypes.Agent agent, address indexed executedBy);
event CollectionCreated(
uint256 indexed sellerId,
uint256 collectionIndex,
address collectionAddress,
string indexed externalId,
address indexed executedBy
);
}
30 changes: 29 additions & 1 deletion contracts/interfaces/handlers/IBosonAccountHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IBosonAccountEvents } from "../events/IBosonAccountEvents.sol";
*
* @notice Handles creation, update, retrieval of accounts within the protocol.
*
* The ERC-165 identifier for this interface is: 0x15335ed7
* The ERC-165 identifier for this interface is: 0x868de65b
*/
interface IBosonAccountHandler is IBosonAccountEvents {
/**
Expand Down Expand Up @@ -303,6 +303,23 @@ interface IBosonAccountHandler is IBosonAccountEvents {
*/
function removeSellersFromAllowList(uint256 _disputeResolverId, uint256[] calldata _sellerAllowList) external;

/**
* @notice Creates a new seller collection.
*
* Emits a CollectionCreated event if successful.
*
* Reverts if:
* - The offers region of protocol is paused
* - Caller is not the seller assistant
*
* @param _externalId - external collection id
* @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct
*/
function createNewCollection(
string calldata _externalId,
BosonTypes.VoucherInitValues calldata _voucherInitValues
) external;

/**
* @notice Gets the details about a seller.
*
Expand Down Expand Up @@ -347,6 +364,17 @@ interface IBosonAccountHandler is IBosonAccountEvents {
BosonTypes.AuthToken calldata _associatedAuthToken
) external view returns (bool exists, BosonTypes.Seller memory seller, BosonTypes.AuthToken memory authToken);

/**
* @notice Gets the details about a seller's collections.
*
* @param _sellerId - the id of the seller to check
* @return defaultVoucherAddress - the address of the default voucher contract for the seller
* @return additionalCollections - an array of additional collections that the seller has created
*/
function getSellersCollections(
uint256 _sellerId
) external view returns (address defaultVoucherAddress, BosonTypes.Collection[] memory additionalCollections);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we worry about the Collection[] being too big ? Do we need something like a getCollection(CollectionID) or something ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even if this is a good idea, i wouldn't block on it ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think is a good idea, nice catch.
I create an issue so this doesn't get loss
#709


/**
* @notice Gets the details about a buyer.
*
Expand Down
4 changes: 3 additions & 1 deletion contracts/interfaces/handlers/IBosonOfferHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IBosonOfferEvents } from "../events/IBosonOfferEvents.sol";
*
* @notice Handles creation, voiding, and querying of offers within the protocol.
*
* The ERC-165 identifier for this interface is: 0xa1598d02
* The ERC-165 identifier for this interface is: 0xa1e3b91c
*/
interface IBosonOfferHandler is IBosonOfferEvents {
/**
Expand All @@ -35,6 +35,7 @@ interface IBosonOfferHandler is IBosonOfferEvents {
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When agent id is non zero:
* - If Agent does not exist
* - If the sum of agent fee amount and protocol fee amount is greater than the offer fee limit
Expand Down Expand Up @@ -79,6 +80,7 @@ interface IBosonOfferHandler is IBosonOfferEvents {
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When agent ids are non zero:
* - If Agent does not exist
* - If the sum of agent fee amount and protocol fee amount is greater than the offer fee limit
Expand Down
18 changes: 17 additions & 1 deletion contracts/interfaces/handlers/IBosonOrchestrationHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IBosonBundleEvents } from "../events/IBosonBundleEvents.sol";
*
* @notice Combines creation of multiple entities (accounts, offers, groups, twins, bundles) in a single transaction
*
* The ERC-165 identifier for this interface is: 0x0c62d8e3
* The ERC-165 identifier for this interface is: 0xb2539c77
*/
interface IBosonOrchestrationHandler is
IBosonAccountEvents,
Expand Down Expand Up @@ -90,6 +90,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When agent id is non zero:
* - If Agent does not exist
* - If the sum of agent fee amount and protocol fee amount is greater than the offer fee limit
Expand Down Expand Up @@ -159,6 +160,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When agent id is non zero:
* - If Agent does not exist
* - If the sum of agent fee amount and protocol fee amount is greater than the offer fee limit
Expand Down Expand Up @@ -216,6 +218,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When agent id is non zero:
* - If Agent does not exist
Expand Down Expand Up @@ -267,6 +270,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When agent id is non zero:
* - If Agent does not exist
Expand Down Expand Up @@ -321,6 +325,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When adding to the group if:
* - Group does not exists
* - Caller is not the assistant of the group
Expand Down Expand Up @@ -375,6 +380,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When adding to the group if:
* - Group does not exists
* - Caller is not the assistant of the group
Expand Down Expand Up @@ -433,6 +439,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When creating twin if
* - Not approved to transfer the seller's token
* - SupplyAvailable is zero
Expand Down Expand Up @@ -492,6 +499,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When creating twin if
* - Not approved to transfer the seller's token
* - SupplyAvailable is zero
Expand Down Expand Up @@ -556,6 +564,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When creating twin if
* - Not approved to transfer the seller's token
Expand Down Expand Up @@ -620,6 +629,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When creating twin if
* - Not approved to transfer the seller's token
Expand Down Expand Up @@ -702,6 +712,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When agent id is non zero:
* - If Agent does not exist
Expand Down Expand Up @@ -777,6 +788,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When agent id is non zero:
* - If Agent does not exist
Expand Down Expand Up @@ -856,6 +868,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When creating twin if
* - Not approved to transfer the seller's token
* - SupplyAvailable is zero
Expand Down Expand Up @@ -939,6 +952,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - When creating twin if
* - Not approved to transfer the seller's token
* - SupplyAvailable is zero
Expand Down Expand Up @@ -1026,6 +1040,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When creating twin if
* - Not approved to transfer the seller's token
Expand Down Expand Up @@ -1114,6 +1129,7 @@ interface IBosonOrchestrationHandler is
* - Seller is not on dispute resolver's seller allow list
* - Dispute resolver does not accept fees in the exchange token
* - Buyer cancel penalty is greater than price
* - Collection does not exist
* - Condition includes invalid combination of parameters
* - When creating twin if
* - Not approved to transfer the seller's token
Expand Down
92 changes: 54 additions & 38 deletions contracts/protocol/bases/OfferBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,46 +149,59 @@ contract OfferBase is ProtocolBase, IBosonOfferEvents {
// quantity must be greater than zero
require(_offer.quantityAvailable > 0, INVALID_QUANTITY_AVAILABLE);

// Specified resolver must be registered and active, except for absolute zero offers with unspecified dispute resolver.
// If price and sellerDeposit are 0, seller is not obliged to choose dispute resolver, which is done by setting _disputeResolverId to 0.
// In this case, there is no need to check the validity of the dispute resolver. However, if one (or more) of {price, sellerDeposit, _disputeResolverId}
// is different from 0, it must be checked that dispute resolver exists, supports the exchange token and seller is allowed to choose them.
DisputeResolutionTerms memory disputeResolutionTerms;
if (_offer.price != 0 || _offer.sellerDeposit != 0 || _disputeResolverId != 0) {
(
bool exists,
DisputeResolver storage disputeResolver,
DisputeResolverFee[] storage disputeResolverFees
) = fetchDisputeResolver(_disputeResolverId);
require(exists && disputeResolver.active, INVALID_DISPUTE_RESOLVER);

// Operate in a block to avoid "stack too deep" error
{
// Cache protocol lookups for reference
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();

// check that seller is on the DR allow list
if (lookups.allowedSellers[_disputeResolverId].length > 0) {
// if length == 0, dispute resolver allows any seller
// if length > 0, we check that it is on allow list
require(lookups.allowedSellerIndex[_disputeResolverId][_offer.sellerId] > 0, SELLER_NOT_APPROVED);
{
// Cache protocol lookups for reference
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();

// Specified resolver must be registered and active, except for absolute zero offers with unspecified dispute resolver.
// If price and sellerDeposit are 0, seller is not obliged to choose dispute resolver, which is done by setting _disputeResolverId to 0.
// In this case, there is no need to check the validity of the dispute resolver. However, if one (or more) of {price, sellerDeposit, _disputeResolverId}
// is different from 0, it must be checked that dispute resolver exists, supports the exchange token and seller is allowed to choose them.
if (_offer.price != 0 || _offer.sellerDeposit != 0 || _disputeResolverId != 0) {
(
bool exists,
DisputeResolver storage disputeResolver,
DisputeResolverFee[] storage disputeResolverFees
) = fetchDisputeResolver(_disputeResolverId);
require(exists && disputeResolver.active, INVALID_DISPUTE_RESOLVER);

// Operate in a block to avoid "stack too deep" error
{
// check that seller is on the DR allow list
if (lookups.allowedSellers[_disputeResolverId].length > 0) {
// if length == 0, dispute resolver allows any seller
// if length > 0, we check that it is on allow list
require(
lookups.allowedSellerIndex[_disputeResolverId][_offer.sellerId] > 0,
SELLER_NOT_APPROVED
);
}

// get the index of DisputeResolverFee and make sure DR supports the exchangeToken
uint256 feeIndex = lookups.disputeResolverFeeTokenIndex[_disputeResolverId][_offer.exchangeToken];
require(feeIndex > 0, DR_UNSUPPORTED_FEE);

uint256 feeAmount = disputeResolverFees[feeIndex - 1].feeAmount;

// store DR terms
disputeResolutionTerms.disputeResolverId = _disputeResolverId;
disputeResolutionTerms.escalationResponsePeriod = disputeResolver.escalationResponsePeriod;
disputeResolutionTerms.feeAmount = feeAmount;
disputeResolutionTerms.buyerEscalationDeposit =
(feeAmount * protocolFees().buyerEscalationDepositPercentage) /
10000;

protocolEntities().disputeResolutionTerms[_offer.id] = disputeResolutionTerms;
}
}

// get the index of DisputeResolverFee and make sure DR supports the exchangeToken
uint256 feeIndex = lookups.disputeResolverFeeTokenIndex[_disputeResolverId][_offer.exchangeToken];
require(feeIndex > 0, DR_UNSUPPORTED_FEE);

uint256 feeAmount = disputeResolverFees[feeIndex - 1].feeAmount;

// store DR terms
disputeResolutionTerms.disputeResolverId = _disputeResolverId;
disputeResolutionTerms.escalationResponsePeriod = disputeResolver.escalationResponsePeriod;
disputeResolutionTerms.feeAmount = feeAmount;
disputeResolutionTerms.buyerEscalationDeposit =
(feeAmount * protocolFees().buyerEscalationDepositPercentage) /
10000;

protocolEntities().disputeResolutionTerms[_offer.id] = disputeResolutionTerms;
// Collection must exist. Collections with index 0 exist by default.
if (_offer.collectionIndex > 0) {
require(
lookups.additionalCollections[_offer.sellerId].length >= _offer.collectionIndex,
NO_SUCH_COLLECTION
);
}
}

Expand Down Expand Up @@ -243,6 +256,7 @@ contract OfferBase is ProtocolBase, IBosonOfferEvents {
offer.exchangeToken = _offer.exchangeToken;
offer.metadataUri = _offer.metadataUri;
offer.metadataHash = _offer.metadataHash;
offer.collectionIndex = _offer.collectionIndex;

// Get storage location for offer dates
OfferDates storage offerDates = fetchOfferDates(_offer.id);
Expand Down Expand Up @@ -315,7 +329,9 @@ contract OfferBase is ProtocolBase, IBosonOfferEvents {
ProtocolLib.ProtocolCounters storage pc = protocolCounters();
uint256 _startId = pc.nextExchangeId;

IBosonVoucher bosonVoucher = IBosonVoucher(protocolLookups().cloneAddress[offer.sellerId]);
IBosonVoucher bosonVoucher = IBosonVoucher(
getCloneAddress(protocolLookups(), offer.sellerId, offer.collectionIndex)
);

address sender = msgSender();

Expand Down
Loading