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 4 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
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;
}
}
75 changes: 73 additions & 2 deletions contracts/protocol/facets/ProtocolInitializationHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandler, ProtocolBase {
address private immutable thisAddress; // used to prevent invocation of initialize directly on deployed contract. Variable is not used by the protocol.
bytes32 internal constant PROTOCOL_INIT_TX_POSITION = keccak256("boson.protocol.initFunctions");
Fixed Show fixed Hide fixed

/**
* @notice Modifier to protect initializer function from being invoked twice for a given version.
Expand Down Expand Up @@ -94,21 +95,23 @@
}
}

ProtocolLib.ProtocolStatus storage status = protocolStatus();
// ProtocolLib.ProtocolStatus storage status = protocolStatus();
Copy link
Contributor

Choose a reason for hiding this comment

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

remove comment

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed

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 +179,74 @@
protocolAddresses().beaconProxy = address(new BeaconClientProxy{ salt: VOUCHER_PROXY_SALT }());
}

/**
* @notice Initializes the version 2.4.0.
*
* Initliaziation data is used to back-fill the royalty recipients for existing offers and sellers.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Initliaziation data is used to back-fill the royalty recipients for existing offers and sellers.
* Initialization data is used to back-fill the royalty recipients for existing offers and sellers.

Copy link
Member Author

Choose a reason for hiding this comment

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

Corrected

* 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 should be included in the group, corresponding to the royalty percentage that matches the royalty of the collection to which offer belongs
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* - the _offerIds array should should be included in the group, corresponding to the royalty percentage that matches the royalty of the collection to which offer belongs
* - 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

Copy link
Member Author

Choose a reason for hiding this comment

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

Corrected

* 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 shuld be used only if the amount of data is too large, and it cannot be done in a single `initialize` transaction.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* This shuld be used only if the amount of data is too large, and it cannot be done in a single `initialize` transaction.
* This should be used only if the amount of data is too large, and it cannot be done in a single `initialize` transaction.

Copy link
Member Author

Choose a reason for hiding this comment

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

Corrected

* 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