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 13, 2024
1 parent 51e71fb commit 7533b58
Show file tree
Hide file tree
Showing 2 changed files with 300 additions and 132 deletions.
294 changes: 201 additions & 93 deletions packages/marketplace/contracts/TransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ abstract contract TransferManager is Initializable, ITransferManager {

(address paymentSideRecipient, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);

// Transfer ERC20 or right side if FeeSide.NONE
if (feeSide == LibAsset.FeeSide.NONE || _mustSkipFees(nftSide.account)) {
_transfer(paymentSide.asset, paymentSide.account, nftSideRecipient);
} else {
_doTransfersWithFeesAndRoyalties(paymentSide, nftSide);
}

// Transfer NFT or left side if FeeSide.NONE
// NFT transfer when exchanging more than one bundle of ERC1155s
if (nftSide.asset.assetType.assetClass == LibAsset.AssetClass.BUNDLE && nftSide.asset.value > 1) {
Expand All @@ -124,13 +131,6 @@ abstract contract TransferManager is Initializable, ITransferManager {
} else {
_transfer(nftSide.asset, nftSide.account, paymentSideRecipient);
}

// Transfer ERC20 or right side if FeeSide.NONE
if (feeSide == LibAsset.FeeSide.NONE || _mustSkipFees(nftSide.account)) {
_transfer(paymentSide.asset, paymentSide.account, nftSideRecipient);
} else {
_doTransfersWithFeesAndRoyalties(paymentSide, nftSide);
}
}

/// @notice Sets the royalties registry.
Expand Down Expand Up @@ -204,29 +204,188 @@ 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 = _doTransfersWithFeesAndRoyaltiesForBundle(
paymentSide,
nftSide,
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);
}
}

function _doTransfersWithFeesAndRoyaltiesForBundle(
DealSide memory paymentSide,
DealSide memory nftSide,
LibAsset.Bundle memory bundle,
uint256 remainder,
address nftSideRecipient
) internal returns (uint256) {
uint256 feePrimary = protocolFeePrimary;
uint256 feeSecondary = protocolFeeSecondary;

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 (_isPrimaryMarketForBundledAsset(nftSide.account, token, bundle.bundledERC721[i].ids[j])) {
// No royalties

if (feePrimary > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
defaultFeeReceiver,
feePrimary,
PROTOCOL_FEE_MULTIPLIER
);
}
} else {
remainder = _transferFeesAndRoyaltiesForBundledAsset(
paymentSide,
token,
nftSideRecipient,
nftSide.asset.value,
remainder,
bundle.bundledERC721[i].ids[j],
bundle.priceDistribution.erc721Prices[i][j],
feeSecondary
);
}
}
}

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 (_isPrimaryMarketForBundledAsset(nftSide.account, token, bundle.bundledERC1155[i].ids[j])) {
// No royalties

if (feePrimary > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
defaultFeeReceiver,
feePrimary,
PROTOCOL_FEE_MULTIPLIER
);
}
} else {
remainder = _transferFeesAndRoyaltiesForBundledAsset(
paymentSide,
token,
nftSideRecipient,
nftSide.asset.value,
remainder,
bundle.bundledERC1155[i].ids[j],
bundle.priceDistribution.erc1155Prices[i][j],
feeSecondary
);
}
}
}

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);
remainder = _transferFeesAndRoyaltiesForBundledAsset(
paymentSide,
address(landContract),
nftSideRecipient,
nftSide.asset.value,
remainder,
tokenId,
bundle.priceDistribution.quadPrices[i],
feeSecondary
);
}
}

return remainder;
}

function _transferFeesAndRoyaltiesForBundledAsset(
DealSide memory paymentSide,
address token,
address nftSideRecipient,
uint256 nftSideAssetValue,
uint256 remainder,
uint256 tokenId,
uint256 assetPrice,
uint256 fees
) internal returns (uint256) {
IRoyaltiesProvider.Part[] memory royalties;

royalties = royaltiesRegistry.getRoyalties(token, tokenId);
// royalty transfer when exchanging one or more than one bundle of ERC1155s
for (uint256 k; k < nftSideAssetValue; k++) {
remainder = _applyRoyalties(remainder, paymentSide, assetPrice, royalties, nftSideRecipient);
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
assetPrice,
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
}
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
Expand All @@ -235,6 +394,22 @@ abstract contract TransferManager is Initializable, ITransferManager {
return creator != address(0) && nftSide.account == creator;
}

/// @notice Return if this tx is on primary market for bundled asset
/// @param token the
/// @param tokenId the
/// @return creator Address or zero if is not able to retrieve it
function _isPrimaryMarketForBundledAsset(
address nftSideAccount,
address token,
uint256 tokenId
) internal view returns (bool) {
address creator;
if (token.supportsInterface(type(IRoyaltyUGC).interfaceId)) {
creator = IRoyaltyUGC(token).getCreatorAddress(tokenId);
}
return creator != address(0) && nftSideAccount == creator;
}

/// @notice Transfer royalties.
/// @param remainder How much of the amount left after previous transfers
/// @param paymentSide DealSide of the fee-side order
Expand All @@ -246,78 +421,11 @@ abstract contract TransferManager is Initializable, ITransferManager {
DealSide memory nftSide
) 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);

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]
);

remainder = _applyRoyalties(
remainder,
paymentSide,
bundle.priceDistribution.erc721Prices[i][j],
royalties,
nftSideRecipient
);
}
}

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]
);
(address token, uint256 tokenId) = LibAsset.decodeToken(nftSide.asset.assetType);
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(token, tokenId);
remainder = _applyRoyalties(remainder, paymentSide, remainder, royalties, nftSideRecipient);

// royalty transfer when exchanging one or more than one bundle of ERC1155s
for (uint256 k; k < nftSide.asset.value; k++) {
remainder = _applyRoyalties(
remainder,
paymentSide,
bundle.priceDistribution.erc1155Prices[i][j],
royalties,
nftSideRecipient
);
}
}
}

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
);

remainder = _applyRoyalties(
remainder,
paymentSide,
bundle.priceDistribution.quadPrices[i],
royalties,
nftSideRecipient
);
}
}
} 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;
}

Expand Down
Loading

0 comments on commit 7533b58

Please sign in to comment.