Skip to content

Commit

Permalink
feat: added logic to calculate protocol fee individually for bundled …
Browse files Browse the repository at this point in the history
…assets
  • Loading branch information
capedcrusader21 committed Sep 12, 2024
1 parent 51e71fb commit 2cedff2
Show file tree
Hide file tree
Showing 2 changed files with 294 additions and 110 deletions.
266 changes: 195 additions & 71 deletions packages/marketplace/contracts/TransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -204,59 +204,98 @@ abstract contract TransferManager is Initializable, ITransferManager {
function _doTransfersWithFeesAndRoyalties(DealSide memory paymentSide, DealSide memory nftSide) internal {
uint256 fees;
uint256 remainder = paymentSide.asset.value;
if (_isTSBSeller(nftSide.account) || _isPrimaryMarket(nftSide)) {
fees = protocolFeePrimary;
// No royalties
(, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);

if (nftSide.asset.assetType.assetClass == LibAsset.AssetClass.BUNDLE) {
LibAsset.Bundle memory bundle = LibAsset.decodeBundle(nftSide.asset.assetType);
if (_isTSBSeller(nftSide.account)) {
fees = protocolFeePrimary;
// No royalties
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
} else {
remainder = _doTransfersWithFeesAndRoyaltiesForBundledERC721(
paymentSide,
nftSide,
bundle,
remainder,
nftSideRecipient
);

remainder = _doTransfersWithFeesAndRoyaltiesForBundledERC1155(
paymentSide,
nftSide,
bundle,
remainder,
nftSideRecipient
);

remainder = _doTransfersWithFeesAndRoyaltiesForBundledQuads(
paymentSide,
bundle,
remainder,
nftSideRecipient
);
}
} else {
fees = protocolFeeSecondary;
remainder = _transferRoyalties(remainder, paymentSide, nftSide);
}
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
if (_isTSBSeller(nftSide.account) || _isPrimaryMarket(nftSide)) {
fees = protocolFeePrimary;
// No royalties
} else {
fees = protocolFeeSecondary;
remainder = _transferRoyalties(remainder, paymentSide, nftSide);
}

if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
}
if (remainder > 0) {
(, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);
_transfer(LibAsset.Asset(paymentSide.asset.assetType, remainder), paymentSide.account, nftSideRecipient);
}
}

/// @notice Return if this tx is on primary market
/// @param nftSide DealSide of the nft-side order
/// @return creator Address or zero if is not able to retrieve it
function _isPrimaryMarket(DealSide memory nftSide) internal view returns (bool) {
address creator = _getCreator(nftSide.asset.assetType);
return creator != address(0) && nftSide.account == creator;
}

/// @notice Transfer royalties.
/// @param remainder How much of the amount left after previous transfers
/// @param paymentSide DealSide of the fee-side order
/// @param nftSide DealSide of the nft-side order
/// @return How much left after paying royalties
function _transferRoyalties(
uint256 remainder,
function _doTransfersWithFeesAndRoyaltiesForBundledERC721(
DealSide memory paymentSide,
DealSide memory nftSide
DealSide memory nftSide,
LibAsset.Bundle memory bundle,
uint256 remainder,
address nftSideRecipient
) internal returns (uint256) {
(, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);
if (nftSide.asset.assetType.assetClass == LibAsset.AssetClass.BUNDLE) {
LibAsset.Bundle memory bundle = LibAsset.decodeBundle(nftSide.asset.assetType);
uint256 fees;
address creator;
IRoyaltiesProvider.Part[] memory royalties;

for (uint256 i; i < bundle.bundledERC721.length; i++) {
address token = bundle.bundledERC721[i].erc721Address;
uint256 idLength = bundle.bundledERC721[i].ids.length;
for (uint256 j; j < idLength; j++) {
if (token.supportsInterface(type(IRoyaltyUGC).interfaceId)) {
creator = IRoyaltyUGC(token).getCreatorAddress(bundle.bundledERC721[i].ids[j]);
}

for (uint256 i; i < bundle.bundledERC721.length; i++) {
address token = bundle.bundledERC721[i].erc721Address;
uint256 idLength = bundle.bundledERC721[i].ids.length;
for (uint256 j; j < idLength; j++) {
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(
token,
bundle.bundledERC721[i].ids[j]
);
if (creator != address(0) && nftSide.account == creator) {
fees = protocolFeePrimary;
// No royalties
} else {
royalties = royaltiesRegistry.getRoyalties(token, bundle.bundledERC721[i].ids[j]);

fees = protocolFeeSecondary;

remainder = _applyRoyalties(
remainder,
Expand All @@ -265,62 +304,147 @@ abstract contract TransferManager is Initializable, ITransferManager {
royalties,
nftSideRecipient
);
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
bundle.priceDistribution.erc721Prices[i][j],
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
}
}
}
return remainder;
}

for (uint256 i; i < bundle.bundledERC1155.length; i++) {
address token = bundle.bundledERC1155[i].erc1155Address;
uint256 idLength = bundle.bundledERC1155[i].ids.length;
require(idLength == bundle.bundledERC1155[i].supplies.length, "ERC1155 array error");
for (uint256 j; j < idLength; j++) {
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(
token,
bundle.bundledERC1155[i].ids[j]
);
function _doTransfersWithFeesAndRoyaltiesForBundledERC1155(
DealSide memory paymentSide,
DealSide memory nftSide,
LibAsset.Bundle memory bundle,
uint256 remainder,
address nftSideRecipient
) internal returns (uint256) {
uint256 fees;
address creator;
IRoyaltiesProvider.Part[] memory royalties;

for (uint256 i; i < bundle.bundledERC1155.length; i++) {
address token = bundle.bundledERC1155[i].erc1155Address;
uint256 idLength = bundle.bundledERC1155[i].ids.length;
require(idLength == bundle.bundledERC1155[i].supplies.length, "ERC1155 array error");

for (uint256 j; j < idLength; j++) {
if (token.supportsInterface(type(IRoyaltyUGC).interfaceId)) {
creator = IRoyaltyUGC(token).getCreatorAddress(bundle.bundledERC1155[i].ids[j]);
}
if (creator != address(0) && nftSide.account == creator) {
fees = protocolFeePrimary;
// No royalties
} else {
royalties = royaltiesRegistry.getRoyalties(token, bundle.bundledERC1155[i].ids[j]);

// royalty transfer when exchanging one or more than one bundle of ERC1155s
for (uint256 k; k < nftSide.asset.value; k++) {
fees = protocolFeeSecondary;

remainder = _applyRoyalties(
remainder,
paymentSide,
bundle.priceDistribution.erc1155Prices[i][j],
royalties,
nftSideRecipient
);
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
bundle.priceDistribution.erc1155Prices[i][j],
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
}
}
}
}
return remainder;
}

uint256 quadSize = bundle.quads.xs.length;
if (quadSize > 0) {
for (uint256 i = 0; i < quadSize; i++) {
uint256 size = bundle.quads.sizes[i];
uint256 x = bundle.quads.xs[i];
uint256 y = bundle.quads.ys[i];

uint256 tokenId = idInPath(0, size, x, y);
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(
address(landContract),
tokenId
);
function _doTransfersWithFeesAndRoyaltiesForBundledQuads(
DealSide memory paymentSide,
LibAsset.Bundle memory bundle,
uint256 remainder,
address nftSideRecipient
) internal returns (uint256) {
uint256 fees;
IRoyaltiesProvider.Part[] memory royalties;

remainder = _applyRoyalties(
uint256 quadSize = bundle.quads.xs.length;
if (quadSize > 0) {
for (uint256 i = 0; i < quadSize; i++) {
uint256 size = bundle.quads.sizes[i];
uint256 x = bundle.quads.xs[i];
uint256 y = bundle.quads.ys[i];

uint256 tokenId = idInPath(0, size, x, y);

royalties = royaltiesRegistry.getRoyalties(address(landContract), tokenId);

fees = protocolFeeSecondary;

remainder = _applyRoyalties(
remainder,
paymentSide,
bundle.priceDistribution.quadPrices[i],
royalties,
nftSideRecipient
);
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
bundle.priceDistribution.quadPrices[i],
royalties,
nftSideRecipient
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
}
} else {
(address token, uint256 tokenId) = LibAsset.decodeToken(nftSide.asset.assetType);
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(token, tokenId);
remainder = _applyRoyalties(remainder, paymentSide, remainder, royalties, nftSideRecipient);
}
return remainder;
}

/// @notice Return if this tx is on primary market
/// @param nftSide DealSide of the nft-side order
/// @return creator Address or zero if is not able to retrieve it
function _isPrimaryMarket(DealSide memory nftSide) internal view returns (bool) {
address creator = _getCreator(nftSide.asset.assetType);
return creator != address(0) && nftSide.account == creator;
}

/// @notice Transfer royalties.
/// @param remainder How much of the amount left after previous transfers
/// @param paymentSide DealSide of the fee-side order
/// @param nftSide DealSide of the nft-side order
/// @return How much left after paying royalties
function _transferRoyalties(
uint256 remainder,
DealSide memory paymentSide,
DealSide memory nftSide
) internal returns (uint256) {
(, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);

(address token, uint256 tokenId) = LibAsset.decodeToken(nftSide.asset.assetType);
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(token, tokenId);
remainder = _applyRoyalties(remainder, paymentSide, remainder, royalties, nftSideRecipient);

return remainder;
}

/// @notice Apply and transfer royalties based on the asset price and royalties information.
/// @param remainder How much of the amount left after previous transfers
/// @param paymentSide DealSide of the fee-side order
Expand Down
Loading

1 comment on commit 2cedff2

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage for this commit

96.70%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
packages/marketplace/contracts
   Exchange.sol94.52%93.33%94.44%96%126, 194, 72
   ExchangeCore.sol98.84%96.67%100%100%85
   OrderValidator.sol98.44%96.15%100%100%36
   RoyaltiesRegistry.sol96.32%88.89%100%98.78%194, 216–217, 263, 65
   TransferManager.sol90.31%77.78%100%95.48%163, 211–212, 214, 214, 214–215, 257, 268, 288–289, 292–293, 307, 337, 340–341, 343–344, 360, 406, 542, 547, 558, 570–571, 582, 93
   Whitelist.sol75.81%60%85.71%82.14%104, 108–109, 122, 125, 141–142, 54, 66, 66–67, 71, 76
packages/marketplace/contracts/interfaces
   IOrderValidator.sol100%100%100%100%
   IRoyaltiesProvider.sol100%100%100%100%
   ITransferManager.sol100%100%100%100%
   IWhitelist.sol100%100%100%100%
packages/marketplace/contracts/libraries
   LibAsset.sol100%100%100%100%
   LibMath.sol100%100%100%100%
   LibOrder.sol100%100%100%100%

Please sign in to comment.