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 16, 2024
1 parent 51e71fb commit 1d956a0
Showing 1 changed file with 183 additions and 87 deletions.
270 changes: 183 additions & 87 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,27 +204,174 @@ 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) {
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, nftSideRecipient);
}
} else {
fees = protocolFeeSecondary;
remainder = _transferRoyalties(remainder, paymentSide, nftSide);
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) {
_transfer(LibAsset.Asset(paymentSide.asset.assetType, remainder), paymentSide.account, nftSideRecipient);
}
}

function _doTransfersWithFeesAndRoyaltiesForBundle(
DealSide memory paymentSide,
DealSide memory nftSide,
address nftSideRecipient
) internal returns (uint256 remainder) {
remainder = paymentSide.asset.value;
uint256 feePrimary = protocolFeePrimary;
uint256 feeSecondary = protocolFeeSecondary;
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++) {
if (_isPrimaryMarketForBundledAsset(nftSide.account, token, bundle.bundledERC721[i].ids[j])) {
// No royalties

if (feePrimary > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
bundle.priceDistribution.erc721Prices[i][j],
defaultFeeReceiver,
feePrimary,
PROTOCOL_FEE_MULTIPLIER
);
}
} else {
remainder = _transferFeesAndRoyaltiesForBundledAsset(
paymentSide,
token,
nftSideRecipient,
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,
bundle.priceDistribution.erc1155Prices[i][j],
defaultFeeReceiver,
feePrimary,
PROTOCOL_FEE_MULTIPLIER
);
}
} else {
// for exchanging one or more than one bundle of ERC1155s
for (uint256 k = 0; k < nftSide.asset.value; k++) {
remainder = _transferFeesAndRoyaltiesForBundledAsset(
paymentSide,
token,
nftSideRecipient,
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,
remainder,
tokenId,
bundle.priceDistribution.quadPrices[i],
feeSecondary
);
}
}

return remainder;
}

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

royalties = royaltiesRegistry.getRoyalties(token, tokenId);
remainder = _applyRoyalties(remainder, paymentSide, assetPrice, royalties, nftSideRecipient);
if (fees > 0 && remainder > 0) {
remainder = _transferPercentage(
remainder,
paymentSide,
paymentSide.asset.value,
assetPrice,
defaultFeeReceiver,
fees,
PROTOCOL_FEE_MULTIPLIER
);
}
if (remainder > 0) {
(, address nftSideRecipient) = _getRecipients(paymentSide, nftSide);
_transfer(LibAsset.Asset(paymentSide.asset.assetType, remainder), paymentSide.account, nftSideRecipient);
}
return remainder;
}

/// @notice Return if this tx is on primary market
Expand All @@ -235,6 +382,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 +409,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]
);

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

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

0 comments on commit 1d956a0

Please sign in to comment.