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

V2.4.0. protocol initialization #838

Merged
merged 8 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 5 additions & 5 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ updates:
groups:
# Group only dev dependencies updates. Production dependencies should have a separate PR per update.
# Exclude major version updates from the dev dependencies group. Major version updates should have a separate PR per update.
- dev-dependencies:
dependency-type: "development"
update-types:
- "minor"
- "patch"
dev-dependencies:
dependency-type: "development"
update-types:
- "minor"
- "patch"
23 changes: 21 additions & 2 deletions contracts/protocol/bases/SellerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ contract SellerBase is ProtocolBase, IBosonAccountEvents {
RoyaltyRecipient[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[sellerId];
RoyaltyRecipient storage defaultRoyaltyRecipient = royaltyRecipients.push();
// We don't store the defaultRoyaltyRecipient.wallet, since it's always the trasury
// We don't store the defaultRoyaltyRecipient.externalId, since the default recipient is always the treasury
defaultRoyaltyRecipient.minRoyaltyPercentage = _voucherInitValues.royaltyPercentage;
defaultRoyaltyRecipient.externalId = DEFAULT_ROYALTY_RECIPIENT;

// Calculate seller salt and check that it is unique
bytes32 sellerSalt = keccak256(abi.encodePacked(sender, _voucherInitValues.collectionSalt));
Expand All @@ -113,7 +113,7 @@ contract SellerBase is ProtocolBase, IBosonAccountEvents {

// Notify watchers of state change
emit SellerCreated(sellerId, _seller, voucherCloneAddress, _authToken, sender);
emit RoyaltyRecipientsChanged(sellerId, royaltyRecipients, sender);
emit RoyaltyRecipientsChanged(sellerId, fetchRoyaltyRecipients(sellerId), sender);
}

/**
Expand Down Expand Up @@ -234,4 +234,23 @@ contract SellerBase is ProtocolBase, IBosonAccountEvents {
sellerPendingUpdate.assistant != address(0) ||
authTokenPendingUpdate.tokenType != AuthTokenType.None;
}

/**
* @notice Gets seller's royalty recipients.
*
* @param _sellerId - seller id
* @return royaltyRecipients - list of royalty recipients
*/
function fetchRoyaltyRecipients(
uint256 _sellerId
) internal view returns (RoyaltyRecipient[] memory royaltyRecipients) {
royaltyRecipients = protocolLookups().royaltyRecipientsBySeller[_sellerId];

// If the seller did not change the default recipient name, return the default name
// royaltyRecipients[0] exists because the default recipient is always present
if (bytes(royaltyRecipients[0].externalId).length == 0) {
royaltyRecipients[0].externalId = DEFAULT_ROYALTY_RECIPIENT;
}
return royaltyRecipients;
}
}
73 changes: 71 additions & 2 deletions contracts/protocol/facets/ProtocolInitializationHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,22 @@ contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandl
}
}

ProtocolLib.ProtocolStatus storage status = protocolStatus();
if (_isUpgrade) {
if (_version == bytes32("2.2.0")) {
initV2_2_0(_initializationData);
} else if (_version == bytes32("2.2.1")) {
initV2_2_1();
} else if (_version == bytes32("2.3.0")) {
initV2_3_0(_initializationData);
} else if (_version == bytes32("2.4.0")) {
initV2_4_0(_initializationData);
}
}

removeInterfaces(_interfacesToRemove);
addInterfaces(_interfacesToAdd);

status.version = _version;
protocolStatus().version = _version;

emit ProtocolInitialized(string(abi.encodePacked(_version)));
}
Expand Down Expand Up @@ -176,6 +177,74 @@ contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandl
protocolAddresses().beaconProxy = address(new BeaconClientProxy{ salt: VOUCHER_PROXY_SALT }());
}

/**
* @notice Initializes the version 2.4.0.
*
* Initialization data is used to back-fill the royalty recipients for existing offers and sellers.
* The data is grouped by royalty percentage, so if more sellers and/or have the same royalty percentage, they can be grouped together.
* Supplied royalty percentage should match the current percentage set in seller's boson voucher contract.
* If seller has multiple collections with different royalty precentages:
* - the sellerId should be included in the group, corresponding to the minimal royalty percentage
* - the _offerIds array should be included in the group, corresponding to the royalty percentage that matches the royalty of the collection to which offer belongs
* If some offer is voided, or has no active vouchers, it can be omitted.
*
* If the amount of data is too large, it can be split into multiple `initV2_4_0Public` calls, that should be made before calling initialize.
*
* N.B. this method does not validate that the seller exists, or that it's unique.
*
* Reverts if:
* - Current version is not 2.3.0
* - Length of _sellerIds, _royaltyPercentages and _offerIds arrays do not match
* - Any of the offerIds does not exist
*
* @param _initializationData - data representing uint256[] _sellerIds, uint256[] _royaltyPercentages, uint256[][] _offerIds
*/
function initV2_4_0(bytes calldata _initializationData) internal {
// Current version must be 2.3.0
if (protocolStatus().version != bytes32("2.3.0")) revert WrongCurrentVersion();

(uint256[] memory _royaltyPercentages, uint256[][] memory _sellerIds, uint256[][] memory _offerIds) = abi
.decode(_initializationData, (uint256[], uint256[][], uint256[][]));

if (_royaltyPercentages.length != _sellerIds.length || _royaltyPercentages.length != _offerIds.length)
revert ArrayLengthMismatch();
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();

for (uint256 i = 0; i < _royaltyPercentages.length; i++) {
// Populate sellers' Royalty Recipients
for (uint256 j = 0; j < _sellerIds[i].length; j++) {
RoyaltyRecipient storage defaultRoyaltyRecipient = lookups
.royaltyRecipientsBySeller[_sellerIds[i][j]]
.push();
defaultRoyaltyRecipient.minRoyaltyPercentage = _royaltyPercentages[i];
}

// Populate offers' Royalty Info
for (uint256 j = 0; j < _offerIds[i].length; j++) {
(bool exist, Offer storage offer) = fetchOffer(_offerIds[i][j]);
if (!exist) revert NoSuchOffer();

RoyaltyInfo storage royaltyInfo = offer.royaltyInfo.push();
royaltyInfo.recipients.push(payable(address(0))); // default recipient
royaltyInfo.bps.push(_royaltyPercentages[i]);
}
}
}

/**
* @notice Method to initialize the protocol if it cannot be done in a single transaction.
*
* This should be used only if the amount of data is too large, and it cannot be done in a single `initialize` transaction.
* This method should be called before `initialize` method.
* This method should not be registered as a diamond public method.
* Refer to initV2_4_0 for more details about the data structure.
*
* @param _initializationData - data representing uint256[] _sellerIds, uint256[] _royaltyPercentages, uint256[][] _offerIds
*/
function initV2_4_0External(bytes calldata _initializationData) external onlyRole(UPGRADER) {
initV2_4_0(_initializationData);
}

/**
* @notice Gets the current protocol version.
*
Expand Down
13 changes: 7 additions & 6 deletions contracts/protocol/facets/SellerHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ contract SellerHandlerFacet is SellerBase {
.royaltyRecipientIndexBySellerAndRecipient[_seller.id];
uint256 royaltyRecipientId = royaltyRecipientIndexBySellerAndRecipient[_seller.treasury];

RoyaltyRecipient[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_seller.id];
if (royaltyRecipientId != 0) {
RoyaltyRecipient[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_seller.id];

// If the new treasury is already a royalty recipient, remove it
royaltyRecipientId--; // royaltyRecipientId is 1-based, so we need to decrement it to get the index
uint256 lastRoyaltyRecipientsId = royaltyRecipients.length - 1;
Expand All @@ -178,7 +179,7 @@ contract SellerHandlerFacet is SellerBase {

updateApplied = true;

emit RoyaltyRecipientsChanged(_seller.id, royaltyRecipients, msgSender());
emit RoyaltyRecipientsChanged(_seller.id, fetchRoyaltyRecipients(_seller.id), msgSender());
}

if (keccak256(bytes(_seller.metadataUri)) != keccak256(bytes(seller.metadataUri))) {
Expand Down Expand Up @@ -505,7 +506,7 @@ contract SellerHandlerFacet is SellerBase {
}
}

emit RoyaltyRecipientsChanged(_sellerId, royaltyRecipients, sender);
emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), sender);
}

/**
Expand Down Expand Up @@ -584,7 +585,7 @@ contract SellerHandlerFacet is SellerBase {
}
}

emit RoyaltyRecipientsChanged(_sellerId, royaltyRecipients, msgSender());
emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), msgSender());
}

/**
Expand Down Expand Up @@ -648,7 +649,7 @@ contract SellerHandlerFacet is SellerBase {
}
}

emit RoyaltyRecipientsChanged(_sellerId, royaltyRecipients, sender);
emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), sender);
}

/**
Expand Down Expand Up @@ -845,7 +846,7 @@ contract SellerHandlerFacet is SellerBase {
function getRoyaltyRecipients(
uint256 _sellerId
) external view returns (RoyaltyRecipient[] memory royaltyRecipients) {
return protocolLookups().royaltyRecipientsBySeller[_sellerId];
return fetchRoyaltyRecipients(_sellerId);
}

/**
Expand Down
Loading