diff --git a/packages/deploy/deploy/marketplace/03_deploy_asset_matcher.ts b/packages/deploy/deploy/marketplace/03_deploy_asset_matcher.ts index ff497ecbf5..12c6fae7f4 100644 --- a/packages/deploy/deploy/marketplace/03_deploy_asset_matcher.ts +++ b/packages/deploy/deploy/marketplace/03_deploy_asset_matcher.ts @@ -6,25 +6,13 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const {deploy} = deployments; const {deployer} = await getNamedAccounts(); - // TODO: to be fetched from env? - const deployMeta = process.env.DEPLOY_META; - const exchangeContract = deployMeta ? 'ExchangeMeta' : 'Exchange'; - - const assetMatcher = await deploy('AssetMatcher', { + await deploy('AssetMatcher', { from: deployer, contract: '@sandbox-smart-contracts/marketplace/contracts/exchange/AssetMatcher.sol:AssetMatcher', log: true, skipIfAlreadyDeployed: true, }); - - await deployments.execute( - exchangeContract, - {from: deployer}, - 'setAssetMatcherContract', - assetMatcher.address - ); }; export default func; func.tags = ['AssetMatcher', 'AssetMatcher_deploy']; -func.dependencies = ['Exchange']; diff --git a/packages/deploy/deploy/marketplace/02_deploy_exchange.ts b/packages/deploy/deploy/marketplace/10_deploy_exchange.ts similarity index 66% rename from packages/deploy/deploy/marketplace/02_deploy_exchange.ts rename to packages/deploy/deploy/marketplace/10_deploy_exchange.ts index 0a2d64b729..7b18a0943f 100644 --- a/packages/deploy/deploy/marketplace/02_deploy_exchange.ts +++ b/packages/deploy/deploy/marketplace/10_deploy_exchange.ts @@ -4,7 +4,7 @@ import {DeployFunction} from 'hardhat-deploy/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const {deployments, getNamedAccounts} = hre; const {deploy} = deployments; - const {deployer, upgradeAdmin, exchangeFeeRecipient} = + const {deployer, sandAdmin, upgradeAdmin, exchangeFeeRecipient} = await getNamedAccounts(); let TRUSTED_FORWARDER = await deployments.getOrNull('TRUSTED_FORWARDER'); @@ -17,32 +17,28 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { } const orderValidator = await deployments.get('OrderValidator'); const royaltiesRegistry = await deployments.get('RoyaltiesRegistry'); + const assetMatcher = await deployments.get('AssetMatcher'); - // TODO: Do we need oll the combinations of flags ? Can we have two deployments scripts with different tags each ? - // TODO: to be fetched from env? - const deployMeta = process.env.DEPLOY_META; - const nativeOrder = process.env.NATIVE_ORDER; - const metaNative = process.env.META_NATIVE; - - const contract = deployMeta ? 'ExchangeMeta' : 'Exchange'; + const newProtocolFeePrimary = 0; + const newProtocolFeeSecondary = 250; await deploy('Exchange', { from: deployer, - contract: `@sandbox-smart-contracts/marketplace/contracts/exchange/${contract}.sol:${contract}`, + contract: `@sandbox-smart-contracts/marketplace/contracts/exchange/Exchange.sol:Exchange`, proxy: { owner: upgradeAdmin, proxyContract: 'OpenZeppelinTransparentProxy', execute: { methodName: '__Exchange_init', args: [ + sandAdmin, TRUSTED_FORWARDER.address, - 0, - 250, + newProtocolFeePrimary, + newProtocolFeeSecondary, exchangeFeeRecipient, - royaltiesRegistry, + royaltiesRegistry.address, orderValidator.address, - nativeOrder, - metaNative, + assetMatcher.address, ], }, upgradeIndex: 0, @@ -53,4 +49,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { }; export default func; func.tags = ['Exchange', 'Exchange_deploy']; -func.dependencies = ['RoyaltiesRegistry_deploy', 'OrderValidator_deploy']; +func.dependencies = [ + 'RoyaltiesRegistry_deploy', + 'OrderValidator_deploy', + 'AssetMatcher_deploy', +]; diff --git a/packages/marketplace/contracts/exchange/AssetMatcher.sol b/packages/marketplace/contracts/exchange/AssetMatcher.sol index a02c19e1c5..247834e6e1 100644 --- a/packages/marketplace/contracts/exchange/AssetMatcher.sol +++ b/packages/marketplace/contracts/exchange/AssetMatcher.sol @@ -3,26 +3,11 @@ pragma solidity 0.8.21; import {IAssetMatcher, LibAsset} from "../interfaces/IAssetMatcher.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; /// @title AssetMatcher contract /// @notice matchAssets function should calculate if Asset types match with each other -contract AssetMatcher is Ownable, IAssetMatcher { +contract AssetMatcher is IAssetMatcher { bytes internal constant EMPTY = ""; - mapping(bytes4 => address) internal matchers; - - /// @notice event emitted when an AssetMacher is set - /// @param assetType represented by bytes4 - /// @param matcher address of the matcher - event MatcherChange(bytes4 indexed assetType, address indexed matcher); - - /// @notice set AssetMacher - /// @param assetType to be matched by the matcher contract - /// @param matcher address of the matcher - function setAssetMatcher(bytes4 assetType, address matcher) external onlyOwner { - matchers[assetType] = matcher; - emit MatcherChange(assetType, matcher); - } /// @notice calculate if Asset types match with each other /// @param leftAssetType to be matched with rightAssetType @@ -31,55 +16,15 @@ contract AssetMatcher is Ownable, IAssetMatcher { function matchAssets( LibAsset.AssetType memory leftAssetType, LibAsset.AssetType memory rightAssetType - ) external view returns (LibAsset.AssetType memory) { - LibAsset.AssetType memory result = matchAssetOneSide(leftAssetType, rightAssetType); - if (result.assetClass == 0) { - return matchAssetOneSide(rightAssetType, leftAssetType); - } else { - return result; - } - } - - function matchAssetOneSide( - LibAsset.AssetType memory leftAssetType, - LibAsset.AssetType memory rightAssetType - ) private view returns (LibAsset.AssetType memory) { - bytes4 classLeft = leftAssetType.assetClass; - bytes4 classRight = rightAssetType.assetClass; - require(classLeft != LibAsset.ETH_ASSET_CLASS, "maker cannot transfer native token"); - require(classRight != LibAsset.ETH_ASSET_CLASS, "taker cannot transfer native token"); - if (classLeft == LibAsset.ERC20_ASSET_CLASS) { - if (classRight == LibAsset.ERC20_ASSET_CLASS) { - return simpleMatch(leftAssetType, rightAssetType); - } - return LibAsset.AssetType(0, EMPTY); - } - if (classLeft == LibAsset.ERC721_ASSET_CLASS) { - if (classRight == LibAsset.ERC721_ASSET_CLASS) { - return simpleMatch(leftAssetType, rightAssetType); - } - return LibAsset.AssetType(0, EMPTY); - } - if (classLeft == LibAsset.ERC1155_ASSET_CLASS) { - if (classRight == LibAsset.ERC1155_ASSET_CLASS) { - return simpleMatch(leftAssetType, rightAssetType); - } - return LibAsset.AssetType(0, EMPTY); - } - if (classLeft == LibAsset.BUNDLE) { - if (classRight == LibAsset.BUNDLE) { - return simpleMatch(leftAssetType, rightAssetType); - } - return LibAsset.AssetType(0, EMPTY); - } - address matcher = matchers[classLeft]; - if (matcher != address(0)) { - return IAssetMatcher(matcher).matchAssets(leftAssetType, rightAssetType); - } + ) external pure returns (LibAsset.AssetType memory) { + LibAsset.AssetClassType classLeft = leftAssetType.assetClass; + LibAsset.AssetClassType classRight = rightAssetType.assetClass; + require(classLeft != LibAsset.AssetClassType.INVALID_ASSET_CLASS, "not found IAssetMatcher"); + require(classRight != LibAsset.AssetClassType.INVALID_ASSET_CLASS, "not found IAssetMatcher"); if (classLeft == classRight) { return simpleMatch(leftAssetType, rightAssetType); } - revert("not found IAssetMatcher"); + return LibAsset.AssetType(LibAsset.AssetClassType.INVALID_ASSET_CLASS, EMPTY); } function simpleMatch( @@ -91,6 +36,6 @@ contract AssetMatcher is Ownable, IAssetMatcher { if (leftHash == rightHash) { return leftAssetType; } - return LibAsset.AssetType(0, EMPTY); + return LibAsset.AssetType(LibAsset.AssetClassType.INVALID_ASSET_CLASS, EMPTY); } } diff --git a/packages/marketplace/contracts/exchange/ExchangeCore.sol b/packages/marketplace/contracts/exchange/ExchangeCore.sol index e3d3f81ae3..f765c170e8 100644 --- a/packages/marketplace/contracts/exchange/ExchangeCore.sol +++ b/packages/marketplace/contracts/exchange/ExchangeCore.sol @@ -122,20 +122,20 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana } /// @dev function, validate orders - /// @param from the message sender + /// @param sender the message sender /// @param orderLeft left order /// @param signatureLeft order left signature /// @param orderRight right order /// @param signatureRight order right signature function _validateOrders( - address from, + address sender, LibOrder.Order memory orderLeft, bytes memory signatureLeft, LibOrder.Order memory orderRight, bytes memory signatureRight ) internal view { - _validateFull(from, orderLeft, signatureLeft); - _validateFull(from, orderRight, signatureRight); + orderValidator.validate(orderLeft, signatureLeft, sender); + orderValidator.validate(orderRight, signatureRight, sender); if (orderLeft.taker != address(0)) { if (orderRight.maker != address(0)) require(orderRight.maker == orderLeft.taker, "leftOrder.taker failed"); } @@ -145,11 +145,11 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana } /// @notice matches valid orders and transfers their assets - /// @param from the message sender + /// @param sender the message sender /// @param orderLeft the left order of the match /// @param orderRight the right order of the match function _matchAndTransfer( - address from, + address sender, LibOrder.Order memory orderLeft, LibOrder.Order memory orderRight ) internal { @@ -162,7 +162,7 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana LibOrderDataGeneric.GenericOrderData memory leftOrderData, LibOrderDataGeneric.GenericOrderData memory rightOrderData, LibFill.FillResult memory newFill - ) = _parseOrdersSetFillEmitMatch(from, orderLeft, orderRight); + ) = _parseOrdersSetFillEmitMatch(sender, orderLeft, orderRight); doTransfers( LibDeal.DealSide({ @@ -180,14 +180,14 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana } /// @notice parse orders with LibOrderDataGeneric parse() to get the order data, then create a new fill with setFillEmitMatch() - /// @param from the message sender + /// @param sender the message sender /// @param orderLeft left order /// @param orderRight right order /// @return leftOrderData generic order data from left order /// @return rightOrderData generic order data from right order /// @return newFill fill result function _parseOrdersSetFillEmitMatch( - address from, + address sender, LibOrder.Order memory orderLeft, LibOrder.Order memory orderRight ) @@ -202,17 +202,17 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana bytes32 rightOrderKeyHash = LibOrder.hashKey(orderRight); if (orderLeft.maker == address(0)) { - orderLeft.maker = from; + orderLeft.maker = sender; } if (orderRight.maker == address(0)) { - orderRight.maker = from; + orderRight.maker = sender; } leftOrderData = LibOrderDataGeneric.parse(orderLeft); rightOrderData = LibOrderDataGeneric.parse(orderRight); newFill = _setFillEmitMatch( - from, + sender, orderLeft, orderRight, leftOrderKeyHash, @@ -223,14 +223,14 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana } /// @notice calculates fills for the matched orders and set them in "fills" mapping - /// @param from the message sender + /// @param sender the message sender /// @param orderLeft left order of the match /// @param orderRight right order of the match /// @param leftMakeFill true if the left orders uses make-side fills, false otherwise /// @param rightMakeFill true if the right orders uses make-side fills, false otherwise /// @return newFill returns change in orders' fills by the match function _setFillEmitMatch( - address from, + address sender, LibOrder.Order memory orderLeft, LibOrder.Order memory orderRight, bytes32 leftOrderKeyHash, @@ -261,7 +261,7 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana } emit Match({ - from: from, + from: sender, leftHash: leftOrderKeyHash, rightHash: rightOrderKeyHash, newFill: newFill, @@ -293,20 +293,9 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana LibOrder.Order memory orderRight ) internal view returns (LibAsset.AssetType memory makeMatch, LibAsset.AssetType memory takeMatch) { makeMatch = assetMatcher.matchAssets(orderLeft.makeAsset.assetType, orderRight.takeAsset.assetType); - require(makeMatch.assetClass != 0, "assets don't match"); + require(makeMatch.assetClass != LibAsset.AssetClassType.INVALID_ASSET_CLASS, "assets don't match"); takeMatch = assetMatcher.matchAssets(orderLeft.takeAsset.assetType, orderRight.makeAsset.assetType); - require(takeMatch.assetClass != 0, "assets don't match"); - } - - /// @notice full validation of an order - /// @param from the message sender - /// @param order LibOrder.Order - /// @param signature order signature - /// @dev first validate time if order start and end are within the block timestamp - /// @dev validate signature if maker is different from sender - function _validateFull(address from, LibOrder.Order memory order, bytes memory signature) internal view { - LibOrder.validateOrderTime(order); - orderValidator.validate(order, signature, from); + require(takeMatch.assetClass != LibAsset.AssetClassType.INVALID_ASSET_CLASS, "assets don't match"); } uint256[49] private __gap; diff --git a/packages/marketplace/contracts/exchange/OrderValidator.sol b/packages/marketplace/contracts/exchange/OrderValidator.sol index 4566a42fbe..4ea1eb7b15 100644 --- a/packages/marketplace/contracts/exchange/OrderValidator.sol +++ b/packages/marketplace/contracts/exchange/OrderValidator.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.21; -import {LibOrder, LibAsset} from "../lib-order/LibOrder.sol"; +import {LibOrder} from "../lib-order/LibOrder.sol"; import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; @@ -45,10 +45,10 @@ contract OrderValidator is IOrderValidator, Initializable, EIP712Upgradeable, Wh /// @param signature signature of order /// @param sender order sender function validate(LibOrder.Order memory order, bytes memory signature, address sender) public view { - if (order.makeAsset.assetType.assetClass != LibAsset.ETH_ASSET_CLASS) { - address makeToken = abi.decode(order.makeAsset.assetType.data, (address)); - verifyWhiteList(makeToken); - } + LibOrder.validateOrderTime(order); + + address makeToken = abi.decode(order.makeAsset.assetType.data, (address)); + verifyWhiteList(makeToken); if (order.salt == 0) { if (order.maker != address(0)) { diff --git a/packages/marketplace/contracts/lazy-mint/erc-1155/IERC1155LazyMint.sol b/packages/marketplace/contracts/lazy-mint/erc-1155/IERC1155LazyMint.sol deleted file mode 100644 index 0c150e0923..0000000000 --- a/packages/marketplace/contracts/lazy-mint/erc-1155/IERC1155LazyMint.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {IERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; -import {LibERC1155LazyMint} from "./LibERC1155LazyMint.sol"; -import {LibPart} from "../../lib-part/LibPart.sol"; - -/// @title interface for 1155LazyMint -/// @notice contains function signatures for mintAndTransfer and transferFromOrMint -interface IERC1155LazyMint is IERC1155Upgradeable { - event Supply(uint256 tokenId, uint256 value); - event Creators(uint256 tokenId, LibPart.Part[] creators); - - /// @notice function to mintAndTransfer - /// @param data mint data for ERC1155 - /// @param to address that will receive the minted token - /// @param amount amount of tokens - function mintAndTransfer(LibERC1155LazyMint.Mint1155Data memory data, address to, uint256 amount) external; - - /// @notice function that transfer a token if already exists, otherwise mint and transfer it - /// @param data token data - /// @param from address from which the token is taken or transferred - /// @param to address that receives the token - /// @param amount amount of tokens - function transferFromOrMint( - LibERC1155LazyMint.Mint1155Data memory data, - address from, - address to, - uint256 amount - ) external; -} diff --git a/packages/marketplace/contracts/lazy-mint/erc-1155/LibERC1155LazyMint.sol b/packages/marketplace/contracts/lazy-mint/erc-1155/LibERC1155LazyMint.sol deleted file mode 100644 index 161b5e01dc..0000000000 --- a/packages/marketplace/contracts/lazy-mint/erc-1155/LibERC1155LazyMint.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {LibPart} from "../../lib-part/LibPart.sol"; - -/// @title library for ERC1155 lazy minting -/// @notice contains struct for ERC1155 mint data and hash function for said data -library LibERC1155LazyMint { - /// @notice hash identifier of ERC1155 lazy asset class - /// @return ERC1155_LAZY_ASSET_CLASS identifier - bytes4 public constant ERC1155_LAZY_ASSET_CLASS = bytes4(keccak256("ERC1155_LAZY")); - - struct Mint1155Data { - uint256 tokenId; - string tokenURI; - uint256 supply; - LibPart.Part[] creators; - LibPart.Part[] royalties; - bytes[] signatures; - } - - /// @notice type hash of mint and transfer - /// @return typehash of functions - bytes32 public constant MINT_AND_TRANSFER_TYPEHASH = - keccak256( - "Mint1155(uint256 tokenId,uint256 supply,string tokenURI,Part[] creators,Part[] royalties)Part(address account,uint96 value)" - ); - - /// @notice hash function for Mint1155Data - /// @param data Mint1155Data to be hashed - /// @return bytes32 hash of data - function hash(Mint1155Data memory data) internal pure returns (bytes32) { - bytes32[] memory royaltiesBytes = new bytes32[](data.royalties.length); - for (uint256 i = 0; i < data.royalties.length; ++i) { - royaltiesBytes[i] = LibPart.hash(data.royalties[i]); - } - bytes32[] memory creatorsBytes = new bytes32[](data.creators.length); - for (uint256 i = 0; i < data.creators.length; ++i) { - creatorsBytes[i] = LibPart.hash(data.creators[i]); - } - return - keccak256( - abi.encode( - MINT_AND_TRANSFER_TYPEHASH, - data.tokenId, - data.supply, - keccak256(bytes(data.tokenURI)), - keccak256(abi.encodePacked(creatorsBytes)), - keccak256(abi.encodePacked(royaltiesBytes)) - ) - ); - } -} diff --git a/packages/marketplace/contracts/lazy-mint/erc-721/IERC721LazyMint.sol b/packages/marketplace/contracts/lazy-mint/erc-721/IERC721LazyMint.sol deleted file mode 100644 index 4c8637c8f2..0000000000 --- a/packages/marketplace/contracts/lazy-mint/erc-721/IERC721LazyMint.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; -import {LibERC721LazyMint} from "./LibERC721LazyMint.sol"; -import {LibPart} from "../../lib-part/LibPart.sol"; - -/// @title interface for ERC721LazyMint -/// @notice contains function signatures for mintAndTransfer and transferFromOrMint -interface IERC721LazyMint is IERC721Upgradeable { - /// @notice event for listing the creators or fee partakers of a token - /// @param tokenId uint256 token identifier - /// @param creators array of participants - event Creators(uint256 tokenId, LibPart.Part[] creators); - - /// @notice function to mintAndTransfer - /// @param data mint data for ERC721 - /// @param to address that will receive the minted token - function mintAndTransfer(LibERC721LazyMint.Mint721Data memory data, address to) external; - - /// @notice function that transfer a token if already exists, otherwise mint and transfer it - /// @param data token data - /// @param from address from which the token is taken or transferred - /// @param to address that receives the token - function transferFromOrMint(LibERC721LazyMint.Mint721Data memory data, address from, address to) external; -} diff --git a/packages/marketplace/contracts/lazy-mint/erc-721/LibERC721LazyMint.sol b/packages/marketplace/contracts/lazy-mint/erc-721/LibERC721LazyMint.sol deleted file mode 100644 index ee52230fa3..0000000000 --- a/packages/marketplace/contracts/lazy-mint/erc-721/LibERC721LazyMint.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {LibPart} from "../../lib-part/LibPart.sol"; - -/// @title library for ERC721 lazy minting -/// @notice contains struct for ERC721 mint data and hash function for said data -library LibERC721LazyMint { - /// @notice hash identifier of ERC721 lazy asset class - /// @return ERC721_LAZY_ASSET_CLASS identifier - bytes4 public constant ERC721_LAZY_ASSET_CLASS = bytes4(keccak256("ERC721_LAZY")); - - struct Mint721Data { - uint256 tokenId; - string tokenURI; - LibPart.Part[] creators; - LibPart.Part[] royalties; - bytes[] signatures; - } - - /// @notice type hash of mint and transfer - /// @return typehash of functions - bytes32 public constant MINT_AND_TRANSFER_TYPEHASH = - keccak256( - "Mint721(uint256 tokenId,string tokenURI,Part[] creators,Part[] royalties)Part(address account,uint96 value)" - ); - - /// @notice hash function for Mint721Data - /// @param data Mint721Data to be hashed - /// @return bytes32 hash of data - function hash(Mint721Data memory data) internal pure returns (bytes32) { - bytes32[] memory royaltiesBytes = new bytes32[](data.royalties.length); - for (uint256 i = 0; i < data.royalties.length; ++i) { - royaltiesBytes[i] = LibPart.hash(data.royalties[i]); - } - bytes32[] memory creatorsBytes = new bytes32[](data.creators.length); - for (uint256 i = 0; i < data.creators.length; ++i) { - creatorsBytes[i] = LibPart.hash(data.creators[i]); - } - return - keccak256( - abi.encode( - MINT_AND_TRANSFER_TYPEHASH, - data.tokenId, - keccak256(bytes(data.tokenURI)), - keccak256(abi.encodePacked(creatorsBytes)), - keccak256(abi.encodePacked(royaltiesBytes)) - ) - ); - } -} diff --git a/packages/marketplace/contracts/lib-asset/LibAsset.sol b/packages/marketplace/contracts/lib-asset/LibAsset.sol index 5bab461947..73412ff7e5 100644 --- a/packages/marketplace/contracts/lib-asset/LibAsset.sol +++ b/packages/marketplace/contracts/lib-asset/LibAsset.sol @@ -7,24 +7,19 @@ pragma solidity 0.8.21; /// @dev Asset represents any asset on ethereum blockchain. /// @dev AssetType is a type of a specific asset library LibAsset { - bytes4 public constant ETH_ASSET_CLASS = bytes4(keccak256("ETH")); - bytes4 public constant ERC20_ASSET_CLASS = bytes4(keccak256("ERC20")); - bytes4 public constant ERC721_ASSET_CLASS = bytes4(keccak256("ERC721")); - bytes4 public constant ERC1155_ASSET_CLASS = bytes4(keccak256("ERC1155")); - // TODO: Unused ? - bytes4 public constant ERC721_TSB_CLASS = bytes4(keccak256("ERC721_TSB")); - // TODO: Unused ? - bytes4 public constant ERC1155_TSB_CLASS = bytes4(keccak256("ERC1155_TSB")); - // TODO: rename to BUNDLE_ASSET_CLASS ? - bytes4 public constant BUNDLE = bytes4(keccak256("BUNDLE")); - - bytes32 internal constant ASSET_TYPE_TYPEHASH = keccak256("AssetType(bytes4 assetClass,bytes data)"); + enum AssetClassType { + INVALID_ASSET_CLASS, + ERC20_ASSET_CLASS, + ERC721_ASSET_CLASS, + ERC1155_ASSET_CLASS + } + bytes32 internal constant ASSET_TYPE_TYPEHASH = keccak256("AssetType(uint256 assetClass,bytes data)"); bytes32 internal constant ASSET_TYPEHASH = - keccak256("Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)"); + keccak256("Asset(AssetType assetType,uint256 value)AssetType(uint256 assetClass,bytes data)"); struct AssetType { - bytes4 assetClass; + LibAsset.AssetClassType assetClass; bytes data; } diff --git a/packages/marketplace/contracts/lib-order/LibOrder.sol b/packages/marketplace/contracts/lib-order/LibOrder.sol index 9b25a98af6..dd7873ff5a 100644 --- a/packages/marketplace/contracts/lib-order/LibOrder.sol +++ b/packages/marketplace/contracts/lib-order/LibOrder.sol @@ -10,7 +10,7 @@ import {LibMath} from "./LibMath.sol"; library LibOrder { bytes32 internal constant ORDER_TYPEHASH = keccak256( - "Order(address maker,Asset makeAsset,address taker,Asset takeAsset,uint256 salt,uint256 start,uint256 end,bytes4 dataType,bytes data)Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)" + "Order(address maker,Asset makeAsset,address taker,Asset takeAsset,uint256 salt,uint256 start,uint256 end,bytes4 dataType,bytes data)Asset(AssetType assetType,uint256 value)AssetType(uint256 assetClass,bytes data)" ); bytes4 internal constant DEFAULT_ORDER_TYPE = 0xffffffff; diff --git a/packages/marketplace/contracts/mocks/ERC1155LazyMintTest.sol b/packages/marketplace/contracts/mocks/ERC1155LazyMintTest.sol deleted file mode 100644 index f90eee2278..0000000000 --- a/packages/marketplace/contracts/mocks/ERC1155LazyMintTest.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {ERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; -import {IERC1155LazyMint, LibERC1155LazyMint} from "../lazy-mint/erc-1155/IERC1155LazyMint.sol"; - -contract ERC1155LazyMintTest is IERC1155LazyMint, ERC1155Upgradeable { - function mintAndTransfer( - LibERC1155LazyMint.Mint1155Data memory data, - address to, - uint256 _amount - ) external override { - _mint(to, data.tokenId, _amount, ""); - } - - function transferFromOrMint( - LibERC1155LazyMint.Mint1155Data memory data, - address from, - address to, - uint256 amount - ) external override { - uint256 balance = balanceOf(from, data.tokenId); - if (balance != 0) { - safeTransferFrom(from, to, data.tokenId, amount, ""); - } else { - this.mintAndTransfer(data, to, amount); - } - } - - function encode(LibERC1155LazyMint.Mint1155Data memory data) external view returns (bytes memory) { - return abi.encode(address(this), data); - } -} diff --git a/packages/marketplace/contracts/mocks/ERC1155Test.sol b/packages/marketplace/contracts/mocks/ERC1155Test.sol index ef93b13e9c..41d1a025ad 100644 --- a/packages/marketplace/contracts/mocks/ERC1155Test.sol +++ b/packages/marketplace/contracts/mocks/ERC1155Test.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.21; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; -import {LibERC1155LazyMint} from "../lazy-mint/erc-1155/LibERC1155LazyMint.sol"; contract ERC1155Test is EIP712Upgradeable { using ECDSAUpgradeable for bytes32; @@ -13,13 +12,4 @@ contract ERC1155Test is EIP712Upgradeable { function __ERC1155Test_init() external initializer { __EIP712_init("Mint1155", "1"); } - - function recover( - LibERC1155LazyMint.Mint1155Data memory data, - bytes memory signature - ) external view returns (address) { - bytes32 structHash = LibERC1155LazyMint.hash(data); - bytes32 hash = _hashTypedDataV4(structHash); - return hash.recover(signature); - } } diff --git a/packages/marketplace/contracts/mocks/ERC721LazyMintTest.sol b/packages/marketplace/contracts/mocks/ERC721LazyMintTest.sol deleted file mode 100644 index c6b8320947..0000000000 --- a/packages/marketplace/contracts/mocks/ERC721LazyMintTest.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import {IERC721LazyMint, LibERC721LazyMint} from "../lazy-mint/erc-721/IERC721LazyMint.sol"; - -contract ERC721LazyMintTest is IERC721LazyMint, ERC721Upgradeable { - function mintAndTransfer(LibERC721LazyMint.Mint721Data memory data, address to) external override { - _mint(to, data.tokenId); - } - - function transferFromOrMint(LibERC721LazyMint.Mint721Data memory data, address from, address to) external override { - if (_exists(data.tokenId)) { - safeTransferFrom(from, to, data.tokenId); - } else { - this.mintAndTransfer(data, to); - } - } - - function encode(LibERC721LazyMint.Mint721Data memory data) external view returns (bytes memory) { - return abi.encode(address(this), data); - } -} diff --git a/packages/marketplace/contracts/mocks/ERC721Test.sol b/packages/marketplace/contracts/mocks/ERC721Test.sol index 09a2efc4c3..2e9ec8b8de 100644 --- a/packages/marketplace/contracts/mocks/ERC721Test.sol +++ b/packages/marketplace/contracts/mocks/ERC721Test.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.21; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; -import {LibERC721LazyMint} from "../lazy-mint/erc-721/LibERC721LazyMint.sol"; contract ERC721Test is EIP712Upgradeable { using ECDSAUpgradeable for bytes32; @@ -13,13 +12,4 @@ contract ERC721Test is EIP712Upgradeable { function __ERC721Test_init() external initializer { __EIP712_init("Mint721", "1"); } - - function recover( - LibERC721LazyMint.Mint721Data memory data, - bytes memory signature - ) external view returns (address) { - bytes32 structHash = LibERC721LazyMint.hash(data); - bytes32 hash = _hashTypedDataV4(structHash); - return hash.recover(signature); - } } diff --git a/packages/marketplace/contracts/mocks/LibFeeSideTest.sol b/packages/marketplace/contracts/mocks/LibFeeSideTest.sol index 913807a498..f4dda31ac0 100644 --- a/packages/marketplace/contracts/mocks/LibFeeSideTest.sol +++ b/packages/marketplace/contracts/mocks/LibFeeSideTest.sol @@ -2,10 +2,15 @@ pragma solidity 0.8.21; +import {LibAsset} from "../lib-asset/LibAsset.sol"; import {LibFeeSide} from "../transfer-manager/lib/LibFeeSide.sol"; +// TODO: MAKE THE TESTS!!! contract LibFeeSideTest { - function getFeeSideTest(bytes4 maker, bytes4 taker) external pure returns (LibFeeSide.FeeSide) { + function getFeeSideTest( + LibAsset.AssetClassType maker, + LibAsset.AssetClassType taker + ) external pure returns (LibFeeSide.FeeSide) { return LibFeeSide.getFeeSide(maker, taker); } } diff --git a/packages/marketplace/contracts/mocks/SimpleTest.sol b/packages/marketplace/contracts/mocks/SimpleTest.sol index a006e23089..bf58cfada1 100644 --- a/packages/marketplace/contracts/mocks/SimpleTest.sol +++ b/packages/marketplace/contracts/mocks/SimpleTest.sol @@ -2,21 +2,12 @@ pragma solidity 0.8.21; -import {LibERC721LazyMint} from "../lazy-mint/erc-721/LibERC721LazyMint.sol"; -import {LibERC1155LazyMint, LibPart} from "../lazy-mint/erc-1155/LibERC1155LazyMint.sol"; import {TransferManager} from "../transfer-manager/TransferManager.sol"; import {TransferExecutor, LibAsset} from "../transfer-manager/TransferExecutor.sol"; +import {LibPart} from "../lib-part/LibPart.sol"; contract SimpleTest is TransferManager, TransferExecutor { function getRoyaltiesByAssetTest(LibAsset.AssetType memory matchNft) external returns (LibPart.Part[] memory) { return getRoyaltiesByAssetType(matchNft); } - - function encode721(LibERC721LazyMint.Mint721Data memory data) external view returns (bytes memory) { - return abi.encode(address(this), data); - } - - function encode1155(LibERC1155LazyMint.Mint1155Data memory data) external view returns (bytes memory) { - return abi.encode(address(this), data); - } } diff --git a/packages/marketplace/contracts/mocks/TestAssetMatcher.sol b/packages/marketplace/contracts/mocks/TestAssetMatcher.sol deleted file mode 100644 index 330d3e93f7..0000000000 --- a/packages/marketplace/contracts/mocks/TestAssetMatcher.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {IAssetMatcher, LibAsset} from "../interfaces/IAssetMatcher.sol"; - -contract TestAssetMatcher is IAssetMatcher { - function matchAssets( - LibAsset.AssetType memory leftAssetType, - LibAsset.AssetType memory rightAssetType - ) external pure override returns (LibAsset.AssetType memory) { - if (leftAssetType.assetClass == bytes4(keccak256("BLA"))) { - address leftToken = abi.decode(leftAssetType.data, (address)); - address rightToken = abi.decode(rightAssetType.data, (address)); - if (leftToken == rightToken) { - return LibAsset.AssetType(rightAssetType.assetClass, rightAssetType.data); - } - } - return LibAsset.AssetType(0, ""); - } -} diff --git a/packages/marketplace/contracts/transfer-manager/TransferExecutor.sol b/packages/marketplace/contracts/transfer-manager/TransferExecutor.sol index be5ee7ef91..aedf8f68c0 100644 --- a/packages/marketplace/contracts/transfer-manager/TransferExecutor.sol +++ b/packages/marketplace/contracts/transfer-manager/TransferExecutor.sol @@ -2,16 +2,13 @@ pragma solidity 0.8.21; +import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {IERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import {IERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {LibTransfer} from "./lib/LibTransfer.sol"; import {LibAsset} from "../lib-asset/LibAsset.sol"; -import {LibERC1155LazyMint} from "../lazy-mint/erc-1155/LibERC1155LazyMint.sol"; -import {IERC1155LazyMint} from "../lazy-mint/erc-1155/IERC1155LazyMint.sol"; -import {LibERC721LazyMint} from "../lazy-mint/erc-721/LibERC721LazyMint.sol"; -import {IERC721LazyMint} from "../lazy-mint/erc-721/IERC721LazyMint.sol"; import {ITransferExecutor} from "./interfaces/ITransferExecutor.sol"; /// @title abstract contract for TransferExecutor @@ -46,7 +43,7 @@ abstract contract TransferExecutor is Initializable, ITransferExecutor { /// @param from account holding the asset /// @param to account that will receive the asset function transfer(LibAsset.Asset memory asset, address from, address to) internal override { - if (asset.assetType.assetClass == LibAsset.ERC721_ASSET_CLASS) { + if (asset.assetType.assetClass == LibAsset.AssetClassType.ERC721_ASSET_CLASS) { //not using transfer proxy when transferring from this contract (address token, uint256 tokenId) = abi.decode(asset.assetType.data, (address, uint256)); require(asset.value == 1, "erc721 value error"); @@ -55,15 +52,15 @@ abstract contract TransferExecutor is Initializable, ITransferExecutor { } else { erc721safeTransferFrom(IERC721Upgradeable(token), from, to, tokenId); } - } else if (asset.assetType.assetClass == LibAsset.ERC20_ASSET_CLASS) { + } else if (asset.assetType.assetClass == LibAsset.AssetClassType.ERC20_ASSET_CLASS) { //not using transfer proxy when transferring from this contract address token = abi.decode(asset.assetType.data, (address)); if (from == address(this)) { - require(IERC20Upgradeable(token).transfer(to, asset.value), "erc20 transfer failed"); + SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(token), to, asset.value); } else { - erc20safeTransferFrom(IERC20Upgradeable(token), from, to, asset.value); + SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(token), from, to, asset.value); } - } else if (asset.assetType.assetClass == LibAsset.ERC1155_ASSET_CLASS) { + } else if (asset.assetType.assetClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS) { //not using transfer proxy when transferring from this contract (address token, uint256 tokenId) = abi.decode(asset.assetType.data, (address, uint256)); if (from == address(this)) { @@ -71,95 +68,9 @@ abstract contract TransferExecutor is Initializable, ITransferExecutor { } else { erc1155safeTransferFrom(IERC1155Upgradeable(token), from, to, tokenId, asset.value, ""); } - } else if (asset.assetType.assetClass == LibAsset.ETH_ASSET_CLASS) { - if (to != address(this)) { - payable(to).transferEth(asset.value); - } - } else if (asset.assetType.assetClass == LibAsset.BUNDLE) { - // unpack the asset data - ERC20Details[] memory erc20Details; - ERC721Details[] memory erc721Details; - ERC1155Details[] memory erc1155Details; - - (erc20Details, erc721Details, erc1155Details) = abi.decode( - asset.assetType.data, - (ERC20Details[], ERC721Details[], ERC1155Details[]) - ); - - // check if bundles don't exceed the limit - require(erc20Details.length <= MAX_BUNDLE_LIMIT, "erc20 limit exceeded"); - require(erc721Details.length <= MAX_BUNDLE_LIMIT, "erc721 limit exceeded"); - require(erc1155Details.length <= MAX_BUNDLE_LIMIT, "erc1155 limit exceeded"); - - // transfer ERC20 - for (uint256 i; i < erc20Details.length; ) { - if (from == address(this)) { - require( - erc20Details[i].token.transferFrom(from, to, erc20Details[i].value), - "erc20 bundle transfer failed" - ); - } else { - erc20safeTransferFrom(erc20Details[i].token, from, to, erc20Details[i].value); - } - - unchecked { - ++i; - } - } - - // transfer ERC721 assets - for (uint256 i; i < erc721Details.length; ) { - require(erc721Details[i].value == 1, "erc721 value error"); - if (from == address(this)) { - erc721Details[i].token.safeTransferFrom(address(this), to, erc721Details[i].id); - } else { - erc721safeTransferFrom(erc721Details[i].token, from, to, erc721Details[i].id); - } - - unchecked { - ++i; - } - } - - // transfer ERC1155 assets - for (uint256 i; i < erc1155Details.length; ) { - if (from == address(this)) { - erc1155Details[i].token.safeTransferFrom( - address(this), - to, - erc1155Details[i].id, - erc1155Details[i].value, - "" - ); - } else { - erc1155safeTransferFrom( - erc1155Details[i].token, - from, - to, - erc1155Details[i].id, - erc1155Details[i].value, - "" - ); - } - - unchecked { - ++i; - } - } - } else { - lazyTransfer(asset, from, to); } } - /// @notice function for safe transfer of ERC20 tokens - /// @param token ERC20 token to be transferred - /// @param from address from which tokens will be taken - /// @param to address that will receive tokens - /// @param value how many tokens are going to be transferred - function erc20safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal { - require(token.transferFrom(from, to, value), "failure while transferring"); - } - /// @notice function for safe transfer of ERC721 tokens /// @param token ERC721 token to be transferred /// @param from address from which token will be taken @@ -186,26 +97,5 @@ abstract contract TransferExecutor is Initializable, ITransferExecutor { token.safeTransferFrom(from, to, id, value, data); } - /// @notice function for lazy transfer - /// @param asset asset that will be lazy transferred - /// @param from address that minted token - /// @param to address that will receive tokens - function lazyTransfer(LibAsset.Asset memory asset, address from, address to) internal { - if (asset.assetType.assetClass == LibERC721LazyMint.ERC721_LAZY_ASSET_CLASS) { - require(asset.value == 1, "erc721 value error"); - (address token, LibERC721LazyMint.Mint721Data memory data) = abi.decode( - asset.assetType.data, - (address, LibERC721LazyMint.Mint721Data) - ); - IERC721LazyMint(token).transferFromOrMint(data, from, to); - } else if (asset.assetType.assetClass == LibERC1155LazyMint.ERC1155_LAZY_ASSET_CLASS) { - (address token, LibERC1155LazyMint.Mint1155Data memory data) = abi.decode( - asset.assetType.data, - (address, LibERC1155LazyMint.Mint1155Data) - ); - IERC1155LazyMint(token).transferFromOrMint(data, from, to, asset.value); - } - } - uint256[49] private __gap; } diff --git a/packages/marketplace/contracts/transfer-manager/TransferManager.sol b/packages/marketplace/contracts/transfer-manager/TransferManager.sol index c59f3082ce..2f6aeaa06e 100644 --- a/packages/marketplace/contracts/transfer-manager/TransferManager.sol +++ b/packages/marketplace/contracts/transfer-manager/TransferManager.sol @@ -3,8 +3,6 @@ pragma solidity 0.8.21; import {ERC165Upgradeable, IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; -import {LibERC721LazyMint} from "../lazy-mint/erc-721/LibERC721LazyMint.sol"; -import {LibERC1155LazyMint} from "../lazy-mint/erc-1155/LibERC1155LazyMint.sol"; import {IRoyaltiesProvider} from "../interfaces/IRoyaltiesProvider.sol"; import {BpLibrary} from "../lib-bp/BpLibrary.sol"; import {IRoyaltyUGC} from "./interfaces/IRoyaltyUGC.sol"; @@ -140,8 +138,8 @@ abstract contract TransferManager is ERC165Upgradeable, ITransferManager { // check if primary or secondary market if ( - nftSide.asset.assetType.assetClass == LibAsset.ERC1155_ASSET_CLASS || - nftSide.asset.assetType.assetClass == LibAsset.ERC721_ASSET_CLASS + nftSide.asset.assetType.assetClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS || + nftSide.asset.assetType.assetClass == LibAsset.AssetClassType.ERC721_ASSET_CLASS ) { (address token, uint256 tokenId) = abi.decode(nftSide.asset.assetType.data, (address, uint)); try IERC165Upgradeable(token).supportsInterface(INTERFACE_ID_IROYALTYUGC) returns (bool result) { @@ -186,8 +184,8 @@ abstract contract TransferManager is ERC165Upgradeable, ITransferManager { LibPart.Part[] memory royalties = getRoyaltiesByAssetType(nftAssetType); if ( - nftAssetType.assetClass == LibAsset.ERC1155_ASSET_CLASS || - nftAssetType.assetClass == LibAsset.ERC721_ASSET_CLASS + nftAssetType.assetClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS || + nftAssetType.assetClass == LibAsset.AssetClassType.ERC721_ASSET_CLASS ) { (address token, uint256 tokenId) = abi.decode(nftAssetType.data, (address, uint)); try IERC165Upgradeable(token).supportsInterface(INTERFACE_ID_IROYALTYUGC) returns (bool resultInterface) { @@ -211,28 +209,16 @@ abstract contract TransferManager is ERC165Upgradeable, ITransferManager { return result; } - /// @notice calculates royalties by asset type. If it's a lazy NFT, then royalties are extracted from asset. otherwise using royaltiesRegistry + /// @notice calculates royalties by asset type. /// @param nftAssetType NFT Asset Type to calculate royalties for /// @return calculated royalties (Array of LibPart.Part) function getRoyaltiesByAssetType(LibAsset.AssetType memory nftAssetType) internal returns (LibPart.Part[] memory) { if ( - nftAssetType.assetClass == LibAsset.ERC1155_ASSET_CLASS || - nftAssetType.assetClass == LibAsset.ERC721_ASSET_CLASS + nftAssetType.assetClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS || + nftAssetType.assetClass == LibAsset.AssetClassType.ERC721_ASSET_CLASS ) { (address token, uint256 tokenId) = abi.decode(nftAssetType.data, (address, uint)); return royaltiesRegistry.getRoyalties(token, tokenId); - } else if (nftAssetType.assetClass == LibERC1155LazyMint.ERC1155_LAZY_ASSET_CLASS) { - (, LibERC1155LazyMint.Mint1155Data memory data) = abi.decode( - nftAssetType.data, - (address, LibERC1155LazyMint.Mint1155Data) - ); - return data.royalties; - } else if (nftAssetType.assetClass == LibERC721LazyMint.ERC721_LAZY_ASSET_CLASS) { - (, LibERC721LazyMint.Mint721Data memory data) = abi.decode( - nftAssetType.data, - (address, LibERC721LazyMint.Mint721Data) - ); - return data.royalties; } LibPart.Part[] memory empty; return empty; diff --git a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol b/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol index c13724885f..e676a5abbe 100644 --- a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol +++ b/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol @@ -11,23 +11,20 @@ library LibFeeSide { RIGHT } - function getFeeSide(bytes4 leftClass, bytes4 rightClass) internal pure returns (FeeSide) { - if (leftClass == LibAsset.ETH_ASSET_CLASS) { + function getFeeSide( + LibAsset.AssetClassType leftClass, + LibAsset.AssetClassType rightClass + ) internal pure returns (FeeSide) { + if (leftClass == LibAsset.AssetClassType.ERC20_ASSET_CLASS) { return FeeSide.LEFT; } - if (rightClass == LibAsset.ETH_ASSET_CLASS) { + if (rightClass == LibAsset.AssetClassType.ERC20_ASSET_CLASS) { return FeeSide.RIGHT; } - if (leftClass == LibAsset.ERC20_ASSET_CLASS) { + if (leftClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS) { return FeeSide.LEFT; } - if (rightClass == LibAsset.ERC20_ASSET_CLASS) { - return FeeSide.RIGHT; - } - if (leftClass == LibAsset.ERC1155_ASSET_CLASS) { - return FeeSide.LEFT; - } - if (rightClass == LibAsset.ERC1155_ASSET_CLASS) { + if (rightClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS) { return FeeSide.RIGHT; } return FeeSide.NONE; diff --git a/packages/marketplace/test/exchange/AssetMatcher.test.ts b/packages/marketplace/test/exchange/AssetMatcher.test.ts index 97c3194d57..c159fce4fd 100644 --- a/packages/marketplace/test/exchange/AssetMatcher.test.ts +++ b/packages/marketplace/test/exchange/AssetMatcher.test.ts @@ -1,45 +1,41 @@ import {deployFixtures} from '../fixtures'; import {loadFixture} from '@nomicfoundation/hardhat-network-helpers'; import {expect} from 'chai'; - -const MOCK_ADDRESS_1 = '0x0000000000000000000000000000000000000001'; +import {AssetClassType} from '../utils/assets'; describe('AssetMatcher.sol', function () { - it('should revert setAssetMatcher call if msg sender is not owner', async function () { - const {assetMatcherAsUser} = await loadFixture(deployFixtures); - - await expect( - assetMatcherAsUser.setAssetMatcher('0x00000001', MOCK_ADDRESS_1) - ).to.revertedWith('Ownable: caller is not the owner'); - }); - - it('should be able to set matcher address', async function () { - const {assetMatcherAsDeployer} = await loadFixture(deployFixtures); - - await expect( - assetMatcherAsDeployer.setAssetMatcher('0x00000001', MOCK_ADDRESS_1) - ) - .to.emit(assetMatcherAsDeployer, 'MatcherChange') - .withArgs('0x00000001', MOCK_ADDRESS_1); - }); - - it('should revert matchAsset call if asset class do not match', async function () { + it('should revert matchAsset call if asset class is invalid', async function () { const {assetMatcherAsUser} = await loadFixture(deployFixtures); - const leftAssetType = {assetClass: '0x00000001', data: '0x1234'}; + const leftAssetType = { + assetClass: AssetClassType.INVALID_ASSET_CLASS, + data: '0x1234', + }; - const rightAssetType = {assetClass: '0x00000002', data: '0x1234'}; + const rightAssetType = { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: '0x1234', + }; await expect( assetMatcherAsUser.matchAssets(leftAssetType, rightAssetType) ).to.be.revertedWith('not found IAssetMatcher'); + await expect( + assetMatcherAsUser.matchAssets(rightAssetType, leftAssetType) + ).to.be.revertedWith('not found IAssetMatcher'); }); it('should call return the expected AssetType', async function () { const {assetMatcherAsUser} = await loadFixture(deployFixtures); - const leftAssetType = {assetClass: '0x00000001', data: '0x1234'}; + const leftAssetType = { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: '0x1234', + }; - const rightAssetType = {assetClass: '0x00000001', data: '0x1234'}; + const rightAssetType = { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: '0x1234', + }; const result = await assetMatcherAsUser.matchAssets( leftAssetType, rightAssetType @@ -51,10 +47,19 @@ describe('AssetMatcher.sol', function () { it('should return null when data does not match', async function () { const {assetMatcherAsUser} = await loadFixture(deployFixtures); - const leftAssetType = {assetClass: '0x00000001', data: '0x1234'}; + const leftAssetType = { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: '0x1234', + }; - const rightAssetType = {assetClass: '0x00000001', data: '0x1254'}; - const nullAsetType = {assetClass: '0x00000000', data: '0x'}; + const rightAssetType = { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: '0x1254', + }; + const nullAsetType = { + assetClass: AssetClassType.INVALID_ASSET_CLASS, + data: '0x', + }; const result = await assetMatcherAsUser.matchAssets( leftAssetType, rightAssetType diff --git a/packages/marketplace/test/exchange/Exchange.test.ts b/packages/marketplace/test/exchange/Exchange.test.ts index c19cdade68..e5956f6fb1 100644 --- a/packages/marketplace/test/exchange/Exchange.test.ts +++ b/packages/marketplace/test/exchange/Exchange.test.ts @@ -1,12 +1,7 @@ import {expect} from 'chai'; import {deployFixtures} from '../fixtures'; import {loadFixture} from '@nomicfoundation/hardhat-network-helpers'; -import { - AssetERC1155, - AssetERC20, - AssetERC721, - AssetETH, -} from '../utils/assets.ts'; +import {AssetERC1155, AssetERC20, AssetERC721} from '../utils/assets.ts'; import { getSymmetricOrder, @@ -20,11 +15,16 @@ import {signOrder} from '../utils/signature'; describe('Exchange.sol', function () { it('should not cancel the order if caller is not maker', async function () { - const {ExchangeContractAsDeployer, user1, user2, ERC20Contract} = - await loadFixture(deployFixtures); + const { + ExchangeContractAsDeployer, + user1, + user2, + ERC20Contract, + ERC721Contract, + } = await loadFixture(deployFixtures); const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetETH(100); + const takerAsset = await AssetERC721(ERC721Contract, 100); const leftOrder = await OrderDefault( user1, @@ -44,12 +44,11 @@ describe('Exchange.sol', function () { }); it('should not cancel order with zero salt', async function () { - const {ExchangeContractAsUser, user, ERC20Contract} = await loadFixture( - deployFixtures - ); + const {ExchangeContractAsUser, user, ERC20Contract, ERC721Contract} = + await loadFixture(deployFixtures); const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetETH(100); + const takerAsset = await AssetERC721(ERC721Contract, 100); const leftOrder = await OrderDefault( user, @@ -66,12 +65,11 @@ describe('Exchange.sol', function () { }); it('should not cancel the order with invalid order hash', async function () { - const {ExchangeContractAsUser, user1, ERC20Contract} = await loadFixture( - deployFixtures - ); + const {ExchangeContractAsUser, user1, ERC20Contract, ERC721Contract} = + await loadFixture(deployFixtures); const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetETH(100); + const takerAsset = await AssetERC721(ERC721Contract, 100); const leftOrder = await OrderDefault( user1, @@ -91,12 +89,11 @@ describe('Exchange.sol', function () { }); it('should cancel an order and update fills mapping', async function () { - const {ExchangeContractAsUser, user, ERC20Contract} = await loadFixture( - deployFixtures - ); + const {ExchangeContractAsUser, user, ERC20Contract, ERC721Contract} = + await loadFixture(deployFixtures); const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetETH(100); + const takerAsset = await AssetERC721(ERC721Contract, 100); const leftOrder = await OrderDefault( user, diff --git a/packages/marketplace/test/exchange/OrderValidator.test.ts b/packages/marketplace/test/exchange/OrderValidator.test.ts index d45ebb19ba..77553582c2 100644 --- a/packages/marketplace/test/exchange/OrderValidator.test.ts +++ b/packages/marketplace/test/exchange/OrderValidator.test.ts @@ -1,7 +1,7 @@ import {deployFixtures} from '../fixtures'; import {loadFixture} from '@nomicfoundation/hardhat-network-helpers'; import {expect} from 'chai'; -import {AssetERC20, AssetETH} from '../utils/assets.ts'; +import {AssetERC20, AssetERC721} from '../utils/assets.ts'; import {OrderDefault} from '../utils/order.ts'; import {ZeroAddress} from 'ethers'; @@ -19,11 +19,10 @@ const ERC20Role = describe('OrderValidator.sol', function () { it('should validate when assetClass is not ETH_ASSET_CLASS', async function () { - const {OrderValidatorAsUser, ERC20Contract, user1} = await loadFixture( - deployFixtures - ); + const {OrderValidatorAsUser, ERC20Contract, ERC721Contract, user1} = + await loadFixture(deployFixtures); const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetETH(100); + const takerAsset = await AssetERC721(ERC721Contract, 100); const order = await OrderDefault( user1, makerAsset, @@ -39,10 +38,10 @@ describe('OrderValidator.sol', function () { .to.not.be.reverted; }); - it('should revert validate when assetClass is ETH_ASSET_CLASS, salt is zero and Order maker is not sender', async function () { - const {OrderValidatorAsUser, ERC20Contract, user1, user2} = + it('should revert validate when salt is zero and Order maker is not sender', async function () { + const {OrderValidatorAsUser, ERC20Contract, ERC721Contract, user1, user2} = await loadFixture(deployFixtures); - const makerAsset = await AssetETH(100); + const makerAsset = await AssetERC721(ERC721Contract, 100); const takerAsset = await AssetERC20(ERC20Contract, 100); const order = await OrderDefault( user1, @@ -60,11 +59,10 @@ describe('OrderValidator.sol', function () { ).to.be.revertedWith('maker is not tx sender'); }); - it('should validate when assetClass is ETH_ASSET_CLASS, salt is zero and Order maker is sender', async function () { - const {OrderValidatorAsUser, ERC20Contract, user1} = await loadFixture( - deployFixtures - ); - const makerAsset = await AssetETH(100); + it('should validate when salt is zero and Order maker is sender', async function () { + const {OrderValidatorAsUser, ERC20Contract, ERC721Contract, user1} = + await loadFixture(deployFixtures); + const makerAsset = await AssetERC721(ERC721Contract, 100); const takerAsset = await AssetERC20(ERC20Contract, 100); const order = await OrderDefault( user1, @@ -82,9 +80,9 @@ describe('OrderValidator.sol', function () { }); it('should not validate when sender and signature signer is not Order maker', async function () { - const {OrderValidatorAsUser, ERC20Contract, user1, user2} = + const {OrderValidatorAsUser, ERC20Contract, ERC721Contract, user1, user2} = await loadFixture(deployFixtures); - const makerAsset = await AssetETH(100); + const makerAsset = await AssetERC721(ERC721Contract, 100); const takerAsset = await AssetERC20(ERC20Contract, 100); const order = await OrderDefault( user1, @@ -103,9 +101,9 @@ describe('OrderValidator.sol', function () { }); it('should validate when sender is not Order maker but signature signer is Order maker', async function () { - const {OrderValidatorAsUser, ERC20Contract, user1, user2} = + const {OrderValidatorAsUser, ERC20Contract, ERC721Contract, user1, user2} = await loadFixture(deployFixtures); - const makerAsset = await AssetETH(100); + const makerAsset = await AssetERC721(ERC721Contract, 100); const takerAsset = await AssetERC20(ERC20Contract, 100); const order = await OrderDefault( user1, diff --git a/packages/marketplace/test/utils/assets.ts b/packages/marketplace/test/utils/assets.ts index fd42487d78..66d912f268 100644 --- a/packages/marketplace/test/utils/assets.ts +++ b/packages/marketplace/test/utils/assets.ts @@ -2,36 +2,26 @@ // "20 eth" == AssetETH(20), "11 Sand" == AssetERC20(SandTokenAddress, 11) // some NFT" == AssetERC721(nftContractAddress, tokenId), etc // SEE: LibAsset.sol -import { - AbiCoder, - Numeric, - BytesLike, - Contract, - keccak256, - solidityPackedKeccak256, -} from 'ethers'; -import {bytes4Keccak, HashSignature} from './signature'; +import {AbiCoder, BytesLike, Contract, keccak256, Numeric} from 'ethers'; -export const ETH_ASSET_CLASS = bytes4Keccak('ETH'); -export const ERC20_ASSET_CLASS = bytes4Keccak('ERC20'); -export const ERC721_ASSET_CLASS = bytes4Keccak('ERC721'); -export const ERC1155_ASSET_CLASS = bytes4Keccak('ERC1155'); -// export const ERC721_TSB_CLASS = bytes4Keccak('ERC721_TSB'); -// export const ERC1155_TSB_CLASS = bytes4Keccak('ERC1155_TSB'); -export const BUNDLE_ASSET_CLASS = bytes4Keccak('BUNDLE'); +export enum AssetClassType { + INVALID_ASSET_CLASS = '0x0', + ERC20_ASSET_CLASS = '0x1', + ERC721_ASSET_CLASS = '0x2', + ERC1155_ASSET_CLASS = '0x3', +} -// TODO: export const ERC721_LAZY_ASSET_CLASS = bytes4Keccak('ERC721_LAZY'); export const ASSET_TYPE_TYPEHASH = keccak256( - Buffer.from('AssetType(bytes4 assetClass,bytes data)') + Buffer.from('AssetType(uint256 assetClass,bytes data)') ); export const ASSET_TYPEHASH = keccak256( Buffer.from( - 'Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)' + 'Asset(AssetType assetType,uint256 value)AssetType(uint256 assetClass,bytes data)' ) ); export type AssetType = { - assetClass: HashSignature; + assetClass: AssetClassType; data: BytesLike; }; @@ -40,20 +30,12 @@ export type Asset = { value: Numeric; }; -export const AssetETH = (value: Numeric): Asset => ({ - assetType: { - assetClass: ETH_ASSET_CLASS, - data: '0x', - }, - value, -}); - export const AssetERC20 = async ( tokenContract: Contract, value: Numeric ): Promise => ({ assetType: { - assetClass: ERC20_ASSET_CLASS, + assetClass: AssetClassType.ERC20_ASSET_CLASS, data: AbiCoder.defaultAbiCoder().encode( ['address'], [await tokenContract.getAddress()] @@ -67,7 +49,7 @@ export const AssetERC721 = async ( tokenId: Numeric ): Promise => ({ assetType: { - assetClass: ERC721_ASSET_CLASS, + assetClass: AssetClassType.ERC721_ASSET_CLASS, data: AbiCoder.defaultAbiCoder().encode( ['address', 'uint256'], [await tokenContract.getAddress(), tokenId] @@ -83,7 +65,7 @@ export const AssetERC1155 = async ( value: Numeric ): Promise => ({ assetType: { - assetClass: ERC1155_ASSET_CLASS, + assetClass: AssetClassType.ERC1155_ASSET_CLASS, data: AbiCoder.defaultAbiCoder().encode( ['address', 'uint256'], [await tokenContract.getAddress(), tokenId] @@ -92,66 +74,23 @@ export const AssetERC1155 = async ( value: value, }); -export const AssetBundle = async ( - erc20: {token: Contract; value: Numeric}[], - erc721: { - token: Contract; - tokenId: Numeric; - }[], - erc1155: { - token: Contract; - tokenId: Numeric; - value: Numeric; - }[] -): Promise => { - const erc20Details = []; - for (const x of erc20) { - erc20Details.push([await x.token.getAddress(), x.value]); - } - const erc721Details = []; - for (const x of erc721) { - erc721Details.push([ - await x.token.getAddress(), - x.tokenId, - // TODO: Test value !=1 - 1, - ]); - } - const erc1155Details = []; - for (const x of erc1155) { - erc1155Details.push([await x.token.getAddress(), x.tokenId, x.value]); - } - return { - assetType: { - assetClass: BUNDLE_ASSET_CLASS, - data: AbiCoder.defaultAbiCoder().encode( - [ - 'tuple(address, uint256)[]', - 'tuple(address, uint256, uint256)[]', - 'tuple(address, uint256, uint256)[]', - ], - [erc20Details, erc721Details, erc1155Details] - ), - }, - // TODO: It make sense tho have multipler bundles >1 ???? - value: 1, - }; -}; - export function hashAssetType(a: AssetType) { - if (a.assetClass.length !== 10) { + if (a.assetClass === AssetClassType.INVALID_ASSET_CLASS) { throw new Error('Invalid assetClass' + a.assetClass); } - // There is aproblem with solidityPackedKeccak256 and byte4 => a.assetClass + '0'.repeat(56) - return solidityPackedKeccak256( - ['bytes32', 'bytes32', 'bytes32'], - [ASSET_TYPE_TYPEHASH, a.assetClass + '0'.repeat(56), keccak256(a.data)] + return keccak256( + AbiCoder.defaultAbiCoder().encode( + ['bytes32', 'uint256', 'bytes32'], + [ASSET_TYPE_TYPEHASH, a.assetClass, keccak256(a.data)] + ) ); } export function hashAsset(a: Asset) { - return solidityPackedKeccak256( - ['bytes32', 'bytes32', 'uint256'], - [ASSET_TYPEHASH, hashAssetType(a.assetType), a.value] + return keccak256( + AbiCoder.defaultAbiCoder().encode( + ['bytes32', 'bytes32', 'uint256'], + [ASSET_TYPEHASH, hashAssetType(a.assetType), a.value] + ) ); } diff --git a/packages/marketplace/test/utils/order.ts b/packages/marketplace/test/utils/order.ts index 9f315b53e5..b8d8e726e2 100644 --- a/packages/marketplace/test/utils/order.ts +++ b/packages/marketplace/test/utils/order.ts @@ -7,7 +7,7 @@ import {BytesLike} from 'ethers/src.ts/utils/index'; export const ORDER_TYPEHASH = keccak256( Buffer.from( - 'Order(address maker,Asset makeAsset,address taker,Asset takeAsset,uint256 salt,uint256 start,uint256 end,bytes4 dataType,bytes data)Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)' + 'Order(address maker,Asset makeAsset,address taker,Asset takeAsset,uint256 salt,uint256 start,uint256 end,bytes4 dataType,bytes data)Asset(AssetType assetType,uint256 value)AssetType(uint256 assetClass,bytes data)' ) ); diff --git a/packages/marketplace/test/utils/signature.ts b/packages/marketplace/test/utils/signature.ts index 934af54c0f..8d8d5dc249 100644 --- a/packages/marketplace/test/utils/signature.ts +++ b/packages/marketplace/test/utils/signature.ts @@ -1,5 +1,5 @@ import {Contract, keccak256, Signer} from 'ethers'; -import {Order, OrderBack} from './order'; +import {Order} from './order'; export type HashSignature = string; @@ -22,7 +22,7 @@ export async function signOrder( }, { AssetType: [ - {name: 'assetClass', type: 'bytes4'}, + {name: 'assetClass', type: 'uint256'}, {name: 'data', type: 'bytes'}, ], Asset: [ @@ -44,42 +44,3 @@ export async function signOrder( order ); } - -export async function signOrderBack( - order: OrderBack, - account: Signer, - verifyingContract: Contract -) { - const network = await verifyingContract.runner?.provider?.getNetwork(); - return account.signTypedData( - { - name: 'Exchange', - version: '1', - chainId: network.chainId, - verifyingContract: await verifyingContract.getAddress(), - }, - { - AssetType: [ - {name: 'assetClass', type: 'bytes4'}, - {name: 'data', type: 'bytes'}, - ], - Asset: [ - {name: 'assetType', type: 'AssetType'}, - {name: 'value', type: 'uint256'}, - ], - OrderBack: [ - {name: 'buyer', type: 'address'}, - {name: 'maker', type: 'address'}, - {name: 'makeAsset', type: 'Asset'}, - {name: 'taker', type: 'address'}, - {name: 'takeAsset', type: 'Asset'}, - {name: 'salt', type: 'uint256'}, - {name: 'start', type: 'uint256'}, - {name: 'end', type: 'uint256'}, - {name: 'dataType', type: 'bytes4'}, - {name: 'data', type: 'bytes'}, - ], - }, - order - ); -}