diff --git a/packages/marketplace/contracts/Exchange.sol b/packages/marketplace/contracts/Exchange.sol index 3095f5e5a5..bf33a8bf5d 100644 --- a/packages/marketplace/contracts/Exchange.sol +++ b/packages/marketplace/contracts/Exchange.sol @@ -42,6 +42,10 @@ contract Exchange is /// @return Hash for TSB_SELLER_ROLE. bytes32 public constant TSB_SELLER_ROLE = keccak256("TSB_SELLER_ROLE"); + /// @notice Role for TSB owned addresses that can sell bundled assets including quad in secondary market. + /// @return Hash for TSB_BUNDLE_SELLER_ROLE. + bytes32 public constant TSB_BUNDLE_SELLER_ROLE = keccak256("TSB_BUNDLE_SELLER_ROLE"); + /// @notice Role for addresses that should be whitelisted from any marketplace fees including royalties. /// @return Hash for FEE_WHITELIST_ROLE. bytes32 public constant FEE_WHITELIST_ROLE = keccak256("FEE_WHITELIST_ROLE"); @@ -180,6 +184,13 @@ contract Exchange is return hasRole(TSB_SELLER_ROLE, from); } + /// @dev Check if the address is a TSB Bundle Seller, which can sell bundled assets including quad in secondary market. + /// @param from Address to check. + /// @return True if the address is a TSB bundle seller, false otherwise. + function _isTSBBundleSeller(address from) internal view override returns (bool) { + return hasRole(TSB_BUNDLE_SELLER_ROLE, from); + } + function _msgSender() internal view diff --git a/packages/marketplace/contracts/TransferManager.sol b/packages/marketplace/contracts/TransferManager.sol index c851ee9182..8e426fe5ef 100644 --- a/packages/marketplace/contracts/TransferManager.sol +++ b/packages/marketplace/contracts/TransferManager.sol @@ -210,12 +210,7 @@ abstract contract TransferManager is Initializable, ITransferManager { bool isBundle = nftSide.asset.assetType.assetClass == LibAsset.AssetClass.BUNDLE; if (isBundle) { - if (!_isTSBSeller(nftSide.account)) { - remainder = _doTransfersWithFeesAndRoyaltiesForBundle(paymentSide, nftSide, nftSideRecipient); - } else { - // No royalties but primary fee should be paid on the total value of the bundle - remainder = _transferProtocolFees(remainder, paymentSide, fees); - } + remainder = _doTransfersWithFeesAndRoyaltiesForBundle(paymentSide, nftSide, nftSideRecipient); } else if (shouldTransferRoyalties) { remainder = _transferRoyalties(remainder, paymentSide, nftSide); } @@ -265,28 +260,14 @@ abstract contract TransferManager is Initializable, ITransferManager { ) internal returns (uint256 remainder) { remainder = paymentSide.asset.value; uint256 feePrimary = protocolFeePrimary; - uint256 feeSecondary = protocolFeeSecondary; LibAsset.Bundle memory bundle = LibAsset.decodeBundle(nftSide.asset.assetType); - remainder = _processERC721Bundles( - paymentSide, - nftSide, - nftSideRecipient, - remainder, - feePrimary, - feeSecondary, - bundle - ); - remainder = _processERC1155Bundles( - paymentSide, - nftSide, - nftSideRecipient, - remainder, - feePrimary, - feeSecondary, - bundle - ); - remainder = _processQuadBundles(paymentSide, nftSideRecipient, remainder, feeSecondary, bundle); + remainder = _processERC721Bundles(paymentSide, nftSide, nftSideRecipient, remainder, feePrimary, bundle); + remainder = _processERC1155Bundles(paymentSide, nftSide, nftSideRecipient, remainder, feePrimary, bundle); + uint256 quadSize = bundle.quads.xs.length; + if (quadSize > 0 && _isTSBBundleSeller(nftSide.account)) { + remainder = _processQuadBundles(paymentSide, nftSideRecipient, remainder, feePrimary, quadSize, bundle); + } return remainder; } @@ -296,7 +277,6 @@ abstract contract TransferManager is Initializable, ITransferManager { address nftSideRecipient, uint256 remainder, uint256 feePrimary, - uint256 feeSecondary, LibAsset.Bundle memory bundle ) internal returns (uint256) { uint256 bundledERC721Length = bundle.bundledERC721.length; @@ -310,7 +290,6 @@ abstract contract TransferManager is Initializable, ITransferManager { nftSideRecipient, remainder, feePrimary, - feeSecondary, token, bundle.bundledERC721[i].ids[j], bundle.priceDistribution.erc721Prices[i][j] @@ -326,7 +305,6 @@ abstract contract TransferManager is Initializable, ITransferManager { address nftSideRecipient, uint256 remainder, uint256 feePrimary, - uint256 feeSecondary, LibAsset.Bundle memory bundle ) internal returns (uint256) { for (uint256 i; i < bundle.bundledERC1155.length; ++i) { @@ -342,7 +320,6 @@ abstract contract TransferManager is Initializable, ITransferManager { nftSideRecipient, remainder, feePrimary, - feeSecondary, token, bundle.bundledERC1155[i].ids[j], bundle.priceDistribution.erc1155Prices[i][j] @@ -357,10 +334,10 @@ abstract contract TransferManager is Initializable, ITransferManager { DealSide memory paymentSide, address nftSideRecipient, uint256 remainder, - uint256 feeSecondary, + uint256 feePrimary, + uint256 quadSize, LibAsset.Bundle memory bundle ) internal returns (uint256) { - uint256 quadSize = bundle.quads.xs.length; for (uint256 i = 0; i < quadSize; ++i) { uint256 size = bundle.quads.sizes[i]; uint256 x = bundle.quads.xs[i]; @@ -374,7 +351,7 @@ abstract contract TransferManager is Initializable, ITransferManager { remainder, tokenId, bundle.priceDistribution.quadPrices[i], - feeSecondary + feePrimary ); } return remainder; @@ -386,7 +363,6 @@ abstract contract TransferManager is Initializable, ITransferManager { address nftSideRecipient, uint256 remainder, uint256 feePrimary, - uint256 feeSecondary, address token, uint256 tokenId, uint256 assetPrice @@ -403,6 +379,7 @@ abstract contract TransferManager is Initializable, ITransferManager { ); } } else { + require(_isTSBBundleSeller(nftSide.account), "only TSB bundle seller allowed"); remainder = _transferFeesAndRoyaltiesForBundledAsset( paymentSide, token, @@ -410,7 +387,7 @@ abstract contract TransferManager is Initializable, ITransferManager { remainder, tokenId, assetPrice, - feeSecondary + feePrimary ); } return remainder; @@ -671,6 +648,10 @@ abstract contract TransferManager is Initializable, ITransferManager { /// @param from Address to check function _isTSBSeller(address from) internal virtual returns (bool); + /// @notice Function deciding if the seller is a TSB bundle seller, to be overridden + /// @param from Address to check + function _isTSBBundleSeller(address from) internal virtual returns (bool); + // slither-disable-next-line unused-state uint256[49] private __gap; }