Skip to content

Commit

Permalink
feat: royalty distribution for quad
Browse files Browse the repository at this point in the history
  • Loading branch information
capedcrusader21 committed Jul 18, 2024
1 parent 5d23368 commit 0cc809c
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 55 deletions.
54 changes: 54 additions & 0 deletions packages/land/contracts/libraries/QuadHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/// @author The Sandbox
/// @title QuadHelper: A library for finding quad IDs
/// @notice This library contains utility functions for identifying quadrants within a predefined grid.
library QuadHelper {
uint256 internal constant GRID_SIZE = 408;
/* solhint-disable const-name-snakecase */
uint256 internal constant LAYER = 0xFF00000000000000000000000000000000000000000000000000000000000000;
uint256 internal constant LAYER_1x1 = 0x0000000000000000000000000000000000000000000000000000000000000000;
uint256 internal constant LAYER_3x3 = 0x0100000000000000000000000000000000000000000000000000000000000000;
uint256 internal constant LAYER_6x6 = 0x0200000000000000000000000000000000000000000000000000000000000000;
uint256 internal constant LAYER_12x12 = 0x0300000000000000000000000000000000000000000000000000000000000000;
uint256 internal constant LAYER_24x24 = 0x0400000000000000000000000000000000000000000000000000000000000000;
/* solhint-enable const-name-snakecase */

/// @notice get the quad id given the layer and coordinates.
/// @param layer the layer of the quad see: _getQuadLayer
/// @param x The bottom left x coordinate of the quad
/// @param y The bottom left y coordinate of the quad
/// @return the tokenId of the quad
/// @dev this method is gas optimized, must be called with verified x,y and size, after a call to _isValidQuad
function getQuadId(uint256 layer, uint256 x, uint256 y) external pure returns (uint256) {
unchecked {
return layer + x + y * GRID_SIZE;
}
}
/// @notice get size related information (there is one-to-one relationship between layer and size)
/// @param size The size of the quad
/// @return layer the layers that corresponds to the size
/// @return parentSize the size of the parent (bigger quad that contains the current one)
/// @return childLayer the layer of the child (smaller quad contained by this one)
function getQuadLayer(uint256 size) external pure returns (uint256 layer, uint256 parentSize, uint256 childLayer) {
if (size == 1) {
layer = LAYER_1x1;
parentSize = 3;
} else if (size == 3) {
layer = LAYER_3x3;
parentSize = 6;
} else if (size == 6) {
layer = LAYER_6x6;
parentSize = 12;
childLayer = LAYER_3x3;
} else if (size == 12) {
layer = LAYER_12x12;
parentSize = 24;
childLayer = LAYER_6x6;
} else {
layer = LAYER_24x24;
childLayer = LAYER_12x12;
}
}
}
14 changes: 3 additions & 11 deletions packages/marketplace/contracts/ExchangeCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -173,26 +173,18 @@ abstract contract ExchangeCore is Initializable, ITransferManager {
orderRight.makeAsset.assetType
);

// Check if the order is a bundle and validate the bundle price
if (makeMatch.assetClass == LibAsset.AssetClass.BUNDLE) {
uint256 bundlePrice = orderRight.makeAsset.value; // colllective price provided by buyer
LibAsset.Bundle memory bundle = LibAsset.decodeBundle(makeMatch);
require(
bundlePrice == LibAsset.computeBundlePrice(bundle, orderLeft.makeAsset.bundlePriceDistribution),
"Bundle price mismatch"
);
}
LibAsset.verifyPriceDistribution(orderRight.makeAsset, orderLeft.makeAsset.priceDistribution);

LibOrder.FillResult memory newFill = _parseOrdersSetFillEmitMatch(sender, orderLeft, orderRight);

doTransfers(
ITransferManager.DealSide(
LibAsset.Asset(makeMatch, newFill.leftValue, orderLeft.makeAsset.bundlePriceDistribution),
LibAsset.Asset(makeMatch, newFill.leftValue, orderLeft.makeAsset.priceDistribution),
orderLeft.maker,
orderLeft.makeRecipient
),
ITransferManager.DealSide(
LibAsset.Asset(takeMatch, newFill.rightValue, orderRight.makeAsset.bundlePriceDistribution),
LibAsset.Asset(takeMatch, newFill.rightValue, orderRight.makeAsset.priceDistribution),
orderRight.maker,
orderRight.makeRecipient
),
Expand Down
35 changes: 19 additions & 16 deletions packages/marketplace/contracts/TransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IRoyaltyUGC} from "@sandbox-smart-contracts/dependency-royalty-management/contracts/interfaces/IRoyaltyUGC.sol";
import {QuadHelper} from "@sandbox-smart-contracts/land/contracts/libraries/QuadHelper.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IRoyaltiesProvider, TOTAL_BASIS_POINTS} from "./interfaces/IRoyaltiesProvider.sol";
import {ITransferManager} from "./interfaces/ITransferManager.sol";
Expand Down Expand Up @@ -178,7 +179,7 @@ abstract contract TransferManager is Initializable, ITransferManager {
}
if (remainder > 0) {
_transfer(
LibAsset.Asset(paymentSide.asset.assetType, remainder, paymentSide.asset.bundlePriceDistribution),
LibAsset.Asset(paymentSide.asset.assetType, remainder, paymentSide.asset.priceDistribution),
paymentSide.account,
nftSide.recipient
);
Expand All @@ -203,19 +204,12 @@ abstract contract TransferManager is Initializable, ITransferManager {
DealSide memory paymentSide,
DealSide memory nftSide
) internal returns (uint256) {
// code to decode for bundle: done
// for bundle royalty is calc for loop // for a same creator club royalties
// royalties are getting fetched
// decode royalties
//trasfer royalties
if (paymentSide.asset.assetType.assetClass == LibAsset.AssetClass.BUNDLE) {
LibAsset.Bundle memory bundle = LibAsset.decodeBundle(nftSide.asset.assetType);

uint256 erc721Length = bundle.bundledERC721.length;
uint256 erc1155Length = bundle.bundledERC1155.length;
uint256 quadsLength = bundle.quads.xs.length;

for (uint256 i; i < erc721Length; i++) {
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++) {
Expand All @@ -225,22 +219,31 @@ abstract contract TransferManager is Initializable, ITransferManager {
}
}

for (uint256 i; i < erc1155Length; i++) {
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++) {
uint256 tokenId = bundle.bundledERC1155[i].ids[j];
uint256 supply = bundle.bundledERC1155[i].supplies[j];
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(token, tokenId);
remainder = _applyRoyalties(remainder, paymentSide, royalties, nftSide.recipient);
}
}

if (quadsLength > 0) {
address landTokenAddress = address(landContract);

// TODO: fetch tokenId and call _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 layer, , ) = QuadHelper.getQuadLayer(size);
uint256 quadId = QuadHelper.getQuadId(layer, x, y);
IRoyaltiesProvider.Part[] memory royalties = royaltiesRegistry.getRoyalties(
address(landContract),
quadId
);
remainder = _applyRoyalties(remainder, paymentSide, royalties, nftSide.recipient);
}
}
} else {
(address token, uint256 tokenId) = LibAsset.decodeToken(nftSide.asset.assetType);
Expand Down Expand Up @@ -288,7 +291,7 @@ abstract contract TransferManager is Initializable, ITransferManager {
LibAsset.Asset memory payment = LibAsset.Asset(
paymentSide.asset.assetType,
0,
paymentSide.asset.bundlePriceDistribution
paymentSide.asset.priceDistribution
);
uint256 fee = (paymentSide.asset.value * percentage) / multiplier;
if (remainder > fee) {
Expand Down
61 changes: 33 additions & 28 deletions packages/marketplace/contracts/libraries/LibAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ library LibAsset {
struct Asset {
AssetType assetType; // The type of the asset.
uint256 value; // The amount or value of the asset.
BundlePriceDistribution bundlePriceDistribution; // The price distribution for individual assets in bundle.
PriceDistribution priceDistribution; // The price distribution for individual assets in bundle.
}

/// @dev Represents a group (i.e. bundle) of ERC20 assets on the Ethereum blockchain.
Expand Down Expand Up @@ -73,7 +73,7 @@ library LibAsset {
}

/// @dev Represents the price of each asset in a bundle.
struct BundlePriceDistribution {
struct PriceDistribution {
uint256[] erc20Prices;
uint256[][] erc721Prices;
uint256[][] erc1155Prices;
Expand Down Expand Up @@ -157,36 +157,41 @@ library LibAsset {
return abi.decode(assetType.data, (Bundle));
}

/// @dev function to compute the total of individual prices in bundle.
/// @param bundle The bundle.
/// @param bundlePriceDistribution The bundle price details.
/// @return The total price of the bundle.
function computeBundlePrice(
Bundle memory bundle,
BundlePriceDistribution memory bundlePriceDistribution
) internal pure returns (uint256) {
uint256 totalPrice = 0;

// total price of all bundled ERC20 assets
for (uint256 i = 0; i < bundlePriceDistribution.erc20Prices.length; i++) {
totalPrice += bundlePriceDistribution.erc20Prices[i];
}
/// @dev function to verify if the order is bundle and validate the bundle price
/// @param rightMakeAsset The make asset from buyer.
/// @param priceDistribution The price distribution details.
function verifyPriceDistribution(
Asset memory rightMakeAsset,
PriceDistribution memory priceDistribution
) internal pure {
if (rightMakeAsset.assetType.assetClass == AssetClass.BUNDLE) {
uint256 bundlePrice = rightMakeAsset.value; // bundle price provided by buyer
Bundle memory bundle = LibAsset.decodeBundle(rightMakeAsset.assetType);
uint256 collectiveBundlePrice = 0;

// total price of all bundled ERC20 assets
for (uint256 i = 0; i < priceDistribution.erc20Prices.length; i++) {
collectiveBundlePrice += priceDistribution.erc20Prices[i];
}

// calculate the total price of all bundled ERC721 assets
for (uint256 i = 0; i < bundlePriceDistribution.erc721Prices.length; i++) {
for (uint256 j = 0; j < bundlePriceDistribution.erc721Prices[i].length; j++)
totalPrice += bundlePriceDistribution.erc721Prices[i][j];
}
// calculate the total price of all bundled ERC721 assets
for (uint256 i = 0; i < priceDistribution.erc721Prices.length; i++) {
for (uint256 j = 0; j < priceDistribution.erc721Prices[i].length; j++)
collectiveBundlePrice += priceDistribution.erc721Prices[i][j];
}

// calculate the total price of all bundled ERC1155 assets
for (uint256 i = 0; i < bundlePriceDistribution.erc1155Prices.length; i++) {
for (uint256 j = 0; j < bundlePriceDistribution.erc1155Prices[i].length; j++) {
totalPrice += bundle.bundledERC1155[i].supplies[j] * bundlePriceDistribution.erc1155Prices[i][j];
// calculate the total price of all bundled ERC1155 assets
for (uint256 i = 0; i < priceDistribution.erc1155Prices.length; i++) {
for (uint256 j = 0; j < priceDistribution.erc1155Prices[i].length; j++) {
collectiveBundlePrice +=
bundle.bundledERC1155[i].supplies[j] *
priceDistribution.erc1155Prices[i][j];
}
}
}

totalPrice += bundlePriceDistribution.quadPrice;
collectiveBundlePrice += priceDistribution.quadPrice;

return totalPrice;
require(bundlePrice == collectiveBundlePrice, "Bundle price mismatch");
}
}
}

0 comments on commit 0cc809c

Please sign in to comment.