diff --git a/packages/marketplace/contracts/exchange/ExchangeCore.sol b/packages/marketplace/contracts/exchange/ExchangeCore.sol index 30ba54b815..210f322163 100644 --- a/packages/marketplace/contracts/exchange/ExchangeCore.sol +++ b/packages/marketplace/contracts/exchange/ExchangeCore.sol @@ -6,8 +6,6 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {LibFill} from "./libraries/LibFill.sol"; import {IAssetMatcher} from "../interfaces/IAssetMatcher.sol"; import {TransferExecutor} from "../transfer-manager/TransferExecutor.sol"; -import {LibFeeSide} from "../transfer-manager/lib/LibFeeSide.sol"; -import {LibDeal} from "../transfer-manager/lib/LibDeal.sol"; import {LibAsset} from "../lib-asset/LibAsset.sol"; import {LibOrder} from "../lib-order/LibOrder.sol"; import {LibPart} from "../lib-part/LibPart.sol"; @@ -161,17 +159,17 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana LibFill.FillResult memory newFill = _parseOrdersSetFillEmitMatch(sender, orderLeft, orderRight); doTransfers( - LibDeal.DealSide({ + ITransferManager.DealSide({ asset: LibAsset.Asset({assetType: makeMatch, value: newFill.leftValue}), payouts: _payToMaker(orderLeft), from: orderLeft.maker }), - LibDeal.DealSide({ + ITransferManager.DealSide({ asset: LibAsset.Asset(takeMatch, newFill.rightValue), payouts: _payToMaker(orderRight), from: orderRight.maker }), - LibFeeSide.getFeeSide(makeMatch.assetClass, takeMatch.assetClass) + LibAsset.getFeeSide(makeMatch.assetClass, takeMatch.assetClass) ); } diff --git a/packages/marketplace/contracts/lib-asset/LibAsset.sol b/packages/marketplace/contracts/lib-asset/LibAsset.sol index 73412ff7e5..7866b2fc81 100644 --- a/packages/marketplace/contracts/lib-asset/LibAsset.sol +++ b/packages/marketplace/contracts/lib-asset/LibAsset.sol @@ -13,13 +13,15 @@ library LibAsset { 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(uint256 assetClass,bytes data)"); + enum FeeSide { + NONE, + LEFT, + RIGHT + } struct AssetType { - LibAsset.AssetClassType assetClass; + AssetClassType assetClass; bytes data; } @@ -28,16 +30,35 @@ library LibAsset { uint256 value; } - /// @notice calculate hash of asset type - /// @param assetType to be hashed - /// @return hash of assetType + bytes32 internal constant ASSET_TYPE_TYPEHASH = keccak256("AssetType(uint256 assetClass,bytes data)"); + + bytes32 internal constant ASSET_TYPEHASH = + keccak256("Asset(AssetType assetType,uint256 value)AssetType(uint256 assetClass,bytes data)"); + + /// @notice decides if the fees will be taken and from which side + /// @param leftClass left side asset class type + /// @param rightClass right side asset class type + /// @return side from which the fees will be taken or none + function getFeeSide(AssetClassType leftClass, AssetClassType rightClass) internal pure returns (FeeSide) { + if (leftClass == AssetClassType.ERC20_ASSET_CLASS && rightClass != AssetClassType.ERC20_ASSET_CLASS) { + return FeeSide.LEFT; + } + if (rightClass == AssetClassType.ERC20_ASSET_CLASS && leftClass != AssetClassType.ERC20_ASSET_CLASS) { + return FeeSide.RIGHT; + } + return FeeSide.NONE; + } + + /// @notice calculate hash of asset type + /// @param assetType to be hashed + /// @return hash of assetType function hash(AssetType memory assetType) internal pure returns (bytes32) { return keccak256(abi.encode(ASSET_TYPE_TYPEHASH, assetType.assetClass, keccak256(assetType.data))); } - /// @notice calculate hash of asset - /// @param asset to be hashed - /// @return hash of asset + /// @notice calculate hash of asset + /// @param asset to be hashed + /// @return hash of asset function hash(Asset memory asset) internal pure returns (bytes32) { return keccak256(abi.encode(ASSET_TYPEHASH, hash(asset.assetType), asset.value)); } diff --git a/packages/marketplace/contracts/mocks/LibFeeSideTest.sol b/packages/marketplace/contracts/mocks/LibFeeSideTest.sol index f4dda31ac0..61dc7d6b73 100644 --- a/packages/marketplace/contracts/mocks/LibFeeSideTest.sol +++ b/packages/marketplace/contracts/mocks/LibFeeSideTest.sol @@ -3,14 +3,13 @@ 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( LibAsset.AssetClassType maker, LibAsset.AssetClassType taker - ) external pure returns (LibFeeSide.FeeSide) { - return LibFeeSide.getFeeSide(maker, taker); + ) external pure returns (LibAsset.FeeSide) { + return LibAsset.getFeeSide(maker, taker); } } diff --git a/packages/marketplace/contracts/mocks/SimpleTransferManager.sol b/packages/marketplace/contracts/mocks/SimpleTransferManager.sol index 1e46d36005..45482e5ea9 100644 --- a/packages/marketplace/contracts/mocks/SimpleTransferManager.sol +++ b/packages/marketplace/contracts/mocks/SimpleTransferManager.sol @@ -2,13 +2,14 @@ pragma solidity 0.8.21; -import {ITransferManager, LibDeal, LibFeeSide} from "../transfer-manager/interfaces/ITransferManager.sol"; +import {ITransferManager} from "../transfer-manager/interfaces/ITransferManager.sol"; +import {LibAsset} from "../lib-asset/LibAsset.sol"; abstract contract SimpleTransferManager is ITransferManager { function doTransfers( - LibDeal.DealSide memory left, - LibDeal.DealSide memory right, - LibFeeSide.FeeSide /* feeSide */ + DealSide memory left, + DealSide memory right, + LibAsset.FeeSide /* feeSide */ ) internal override { transfer(left.asset, left.from, right.from); transfer(right.asset, right.from, left.from); diff --git a/packages/marketplace/contracts/transfer-manager/TransferManager.sol b/packages/marketplace/contracts/transfer-manager/TransferManager.sol index 7beeb26800..bb36972cb5 100644 --- a/packages/marketplace/contracts/transfer-manager/TransferManager.sol +++ b/packages/marketplace/contracts/transfer-manager/TransferManager.sol @@ -6,7 +6,7 @@ import {ERC165Upgradeable, IERC165Upgradeable} from "@openzeppelin/contracts-upg import {IRoyaltiesProvider} from "../interfaces/IRoyaltiesProvider.sol"; import {BpLibrary} from "../lib-bp/BpLibrary.sol"; import {IRoyaltyUGC} from "./interfaces/IRoyaltyUGC.sol"; -import {ITransferManager, LibDeal, LibFeeSide} from "./interfaces/ITransferManager.sol"; +import {ITransferManager} from "./interfaces/ITransferManager.sol"; import {LibAsset} from "../lib-asset/LibAsset.sol"; import {LibPart} from "../lib-part/LibPart.sol"; @@ -99,15 +99,11 @@ abstract contract TransferManager is ERC165Upgradeable, ITransferManager { /// @notice executes transfers for 2 matched orders /// @param left DealSide from the left order (see LibDeal.sol) /// @param right DealSide from the right order (see LibDeal.sol) - function doTransfers( - LibDeal.DealSide memory left, - LibDeal.DealSide memory right, - LibFeeSide.FeeSide feeSide - ) internal override { - if (feeSide == LibFeeSide.FeeSide.LEFT) { + function doTransfers(DealSide memory left, DealSide memory right, LibAsset.FeeSide feeSide) internal override { + if (feeSide == LibAsset.FeeSide.LEFT) { doTransfersWithFees(left, right); transferPayouts(right.asset.assetType, right.asset.value, right.from, left.payouts); - } else if (feeSide == LibFeeSide.FeeSide.RIGHT) { + } else if (feeSide == LibAsset.FeeSide.RIGHT) { doTransfersWithFees(right, left); transferPayouts(left.asset.assetType, left.asset.value, left.from, right.payouts); } else { @@ -119,7 +115,7 @@ abstract contract TransferManager is ERC165Upgradeable, ITransferManager { /// @notice executes the fee-side transfers (payment + fees) /// @param paymentSide DealSide of the fee-side order /// @param nftSide DealSide of the nft-side order - function doTransfersWithFees(LibDeal.DealSide memory paymentSide, LibDeal.DealSide memory nftSide) internal { + function doTransfersWithFees(DealSide memory paymentSide, DealSide memory nftSide) internal { uint256 rest = paymentSide.asset.value; if (_applyFees(paymentSide.from)) { diff --git a/packages/marketplace/contracts/transfer-manager/interfaces/ITransferManager.sol b/packages/marketplace/contracts/transfer-manager/interfaces/ITransferManager.sol index 5fad64c692..3a289e60b1 100644 --- a/packages/marketplace/contracts/transfer-manager/interfaces/ITransferManager.sol +++ b/packages/marketplace/contracts/transfer-manager/interfaces/ITransferManager.sol @@ -2,14 +2,16 @@ pragma solidity 0.8.21; -import {LibDeal} from "../lib/LibDeal.sol"; -import {LibFeeSide} from "../lib/LibFeeSide.sol"; import {ITransferExecutor} from "./ITransferExecutor.sol"; +import {LibAsset} from "../../lib-asset/LibAsset.sol"; +import {LibPart} from "../../lib-part/LibPart.sol"; abstract contract ITransferManager is ITransferExecutor { - function doTransfers( - LibDeal.DealSide memory left, - LibDeal.DealSide memory right, - LibFeeSide.FeeSide feeSide - ) internal virtual; + struct DealSide { + LibAsset.Asset asset; + LibPart.Part[] payouts; + address from; + } + + function doTransfers(DealSide memory left, DealSide memory right, LibAsset.FeeSide feeSide) internal virtual; } diff --git a/packages/marketplace/contracts/transfer-manager/lib/LibDeal.sol b/packages/marketplace/contracts/transfer-manager/lib/LibDeal.sol deleted file mode 100644 index 9cd7ef8305..0000000000 --- a/packages/marketplace/contracts/transfer-manager/lib/LibDeal.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {LibPart} from "../../lib-part/LibPart.sol"; -import {LibAsset} from "../../lib-asset/LibAsset.sol"; - -library LibDeal { - struct DealSide { - LibAsset.Asset asset; - LibPart.Part[] payouts; - address from; - } -} diff --git a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.md b/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.md deleted file mode 100644 index 5def6da084..0000000000 --- a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.md +++ /dev/null @@ -1,3 +0,0 @@ -#### Features - -`getFeeSide` function identifies side of the order which can be interpreted like money. So [RaribleTransferManager](./RaribleTransferManager.sol) is able to calculate and transfer different fees (royalties, protocol and origin fees etc). \ No newline at end of file diff --git a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol b/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol deleted file mode 100644 index e676a5abbe..0000000000 --- a/packages/marketplace/contracts/transfer-manager/lib/LibFeeSide.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.21; - -import {LibAsset} from "../../lib-asset/LibAsset.sol"; - -library LibFeeSide { - enum FeeSide { - NONE, - LEFT, - RIGHT - } - - 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.AssetClassType.ERC20_ASSET_CLASS) { - return FeeSide.RIGHT; - } - if (leftClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS) { - return FeeSide.LEFT; - } - if (rightClass == LibAsset.AssetClassType.ERC1155_ASSET_CLASS) { - return FeeSide.RIGHT; - } - return FeeSide.NONE; - } -} diff --git a/packages/marketplace/test/exchange/Exchange.test.ts b/packages/marketplace/test/exchange/Exchange.test.ts index cb32e48449..0944146f93 100644 --- a/packages/marketplace/test/exchange/Exchange.test.ts +++ b/packages/marketplace/test/exchange/Exchange.test.ts @@ -63,8 +63,8 @@ describe('Exchange.sol', function () { ERC721Contract, } = await loadFixture(deployFixtures); - const makerAsset = await AssetERC20(ERC20Contract, 100); - const takerAsset = await AssetERC721(ERC721Contract, 100); + const makerAsset = await AssetERC20(ERC20Contract, 123000000); + const takerAsset = await AssetERC721(ERC721Contract, 456000000); const leftOrder = await OrderDefault( user1, @@ -158,31 +158,29 @@ describe('Exchange.sol', function () { OrderValidatorAsAdmin, ERC20Contract, ERC20Contract2, - defaultFeeReceiver, - protocolFeeSecondary, user1: maker, user2: taker, } = await loadFixture(deployFixtures); - await ERC20Contract.mint(maker.address, 200); + await ERC20Contract.mint(maker.address, 123000000); await ERC20Contract.connect(maker).approve( await ExchangeContractAsUser.getAddress(), - 200 + 123000000 ); - await ERC20Contract2.mint(taker.address, 100); + await ERC20Contract2.mint(taker.address, 456000000); await ERC20Contract2.connect(taker).approve( await ExchangeContractAsUser.getAddress(), - 100 + 456000000 ); - expect(await ERC20Contract.balanceOf(maker)).to.be.equal(200); + expect(await ERC20Contract.balanceOf(maker)).to.be.equal(123000000); expect(await ERC20Contract.balanceOf(taker)).to.be.equal(0); expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(0); - expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(100); + expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(456000000); - const makerAsset = await AssetERC20(ERC20Contract, 200); - const takerAsset = await AssetERC20(ERC20Contract2, 100); + const makerAsset = await AssetERC20(ERC20Contract, 123000000); + const takerAsset = await AssetERC20(ERC20Contract2, 456000000); const orderLeft = await OrderDefault( maker, makerAsset, @@ -214,16 +212,9 @@ describe('Exchange.sol', function () { ]); expect(await ERC20Contract.balanceOf(maker)).to.be.equal(0); - expect(await ERC20Contract.balanceOf(taker)).to.be.equal(195); // 200 - protocolFee - expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(100); + expect(await ERC20Contract.balanceOf(taker)).to.be.equal(123000000); + expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(456000000); expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(0); - - // check protocol fee -> 250 * 200 / 10000 = 5 - expect( - await ERC20Contract.balanceOf(defaultFeeReceiver.address) - ).to.be.equal( - (Number(protocolFeeSecondary) * Number(makerAsset.value)) / 10000 - ); }); it('should execute a complete match order between ERC20 and ERC721 tokens', async function () { @@ -328,7 +319,7 @@ describe('Exchange.sol', function () { 10000000000 ); - await ERC1155Contract.mint(taker.address, 1, 10); + await ERC1155Contract.mint(taker.address, 1, 123000000); await ERC1155Contract.connect(taker).setApprovalForAll( await ExchangeContractAsUser.getAddress(), true @@ -337,10 +328,12 @@ describe('Exchange.sol', function () { expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal( 10000000000 ); - expect(await ERC1155Contract.balanceOf(taker.address, 1)).to.be.equal(10); + expect(await ERC1155Contract.balanceOf(taker.address, 1)).to.be.equal( + 123000000 + ); const makerAsset = await AssetERC20(ERC20Contract, 10000000000); - const takerAsset = await AssetERC1155(ERC1155Contract, 1, 10); + const takerAsset = await AssetERC1155(ERC1155Contract, 1, 123000000); const orderLeft = await OrderDefault( maker, @@ -377,12 +370,14 @@ describe('Exchange.sol', function () { }, ]); expect(await ExchangeContractAsUser.fills(hashKey(orderLeft))).to.be.equal( - 10 + 123000000 ); expect(await ExchangeContractAsUser.fills(hashKey(orderRight))).to.be.equal( 10000000000 ); - expect(await ERC1155Contract.balanceOf(maker.address, 1)).to.be.equal(10); + expect(await ERC1155Contract.balanceOf(maker.address, 1)).to.be.equal( + 123000000 + ); expect(await ERC1155Contract.balanceOf(taker.address, 1)).to.be.equal(0); expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal(0); expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal( @@ -687,7 +682,7 @@ describe('Exchange.sol', function () { it('should execute a complete match order with royalties 2981(type 3) transferred to royaltyReceiver', async function () { const { ExchangeContractAsUser, - OrderValidatorAsDeployer, + OrderValidatorAsAdmin, ERC20Contract, ERC721WithRoyalty, defaultFeeReceiver, @@ -745,16 +740,8 @@ describe('Exchange.sol', function () { 0, 0 ); - const makerSig = await signOrder( - orderLeft, - maker, - OrderValidatorAsDeployer - ); - const takerSig = await signOrder( - orderRight, - taker, - OrderValidatorAsDeployer - ); + const makerSig = await signOrder(orderLeft, maker, OrderValidatorAsAdmin); + const takerSig = await signOrder(orderRight, taker, OrderValidatorAsAdmin); expect(await ExchangeContractAsUser.fills(hashKey(orderLeft))).to.be.equal( 0 @@ -1020,16 +1007,16 @@ describe('Exchange.sol', function () { await ExchangeContractAsUser.getAddress(), 1 ); - await ERC20Contract.mint(taker.address, 100); + await ERC20Contract.mint(taker.address, 123000000); await ERC20Contract.connect(taker).approve( await ExchangeContractAsUser.getAddress(), - 100 + 123000000 ); expect(await ERC721Contract.ownerOf(1)).to.be.equal(maker.address); - expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal(100); + expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal(123000000); const makerAsset = await AssetERC721(ERC721Contract, 1); - const takerAsset = await AssetERC20(ERC20Contract, 100); + const takerAsset = await AssetERC20(ERC20Contract, 123000000); const orderLeft = await OrderDefault( maker, makerAsset, @@ -1067,13 +1054,15 @@ describe('Exchange.sol', function () { }, ]); expect(await ExchangeContractAsUser.fills(hashKey(orderLeft))).to.be.equal( - 100 + 123000000 ); expect(await ExchangeContractAsUser.fills(hashKey(orderRight))).to.be.equal( 1 ); expect(await ERC721Contract.ownerOf(1)).to.be.equal(taker.address); - expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal(98); + expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal( + 123000000 * (1 - 2.5 / 100) + ); }); it('should execute a complete match order between ERC1155 and ERC20 tokens', async function () { @@ -1086,23 +1075,25 @@ describe('Exchange.sol', function () { user2: taker, } = await loadFixture(deployFixtures); - await ERC1155Contract.mint(maker.address, 1, 10); + await ERC1155Contract.mint(maker.address, 1, 123000000); await ERC1155Contract.connect(maker).setApprovalForAll( await ExchangeContractAsUser.getAddress(), true ); - await ERC20Contract.mint(taker.address, 100); + await ERC20Contract.mint(taker.address, 456000000); await ERC20Contract.connect(taker).approve( await ExchangeContractAsUser.getAddress(), - 100 + 456000000 ); - expect(await ERC1155Contract.balanceOf(maker.address, 1)).to.be.equal(10); - expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal(100); + expect(await ERC1155Contract.balanceOf(maker.address, 1)).to.be.equal( + 123000000 + ); + expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal(456000000); - const makerAsset = await AssetERC1155(ERC1155Contract, 1, 10); - const takerAsset = await AssetERC20(ERC20Contract, 100); + const makerAsset = await AssetERC1155(ERC1155Contract, 1, 123000000); + const takerAsset = await AssetERC20(ERC20Contract, 456000000); const orderLeft = await OrderDefault( maker, @@ -1139,15 +1130,17 @@ describe('Exchange.sol', function () { }, ]); expect(await ExchangeContractAsUser.fills(hashKey(orderLeft))).to.be.equal( - 100 + 456000000 ); expect(await ExchangeContractAsUser.fills(hashKey(orderRight))).to.be.equal( - 10 + 123000000 ); expect(await ERC1155Contract.balanceOf(maker.address, 1)).to.be.equal(0); - expect(await ERC1155Contract.balanceOf(taker.address, 1)).to.be.equal(10); + expect(await ERC1155Contract.balanceOf(taker.address, 1)).to.be.equal( + 123000000 + ); expect(await ERC20Contract.balanceOf(taker.address)).to.be.equal(0); - expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal(98); + expect(await ERC20Contract.balanceOf(maker.address)).to.be.equal(444600000); }); it('should partially fill orders using matchOrders between ERC20 tokens', async function () { @@ -1224,7 +1217,7 @@ describe('Exchange.sol', function () { 50 ); expect(await ERC20Contract.balanceOf(maker)).to.be.equal(50); - expect(await ERC20Contract.balanceOf(taker)).to.be.equal(49); + expect(await ERC20Contract.balanceOf(taker)).to.be.equal(50); expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(100); expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(100); }); @@ -1382,7 +1375,7 @@ describe('Exchange.sol', function () { await ExchangeContractAsUser.fills(hashKey(rightOrderForFirstMatch)) ).to.be.equal(50); expect(await ERC20Contract.balanceOf(maker)).to.be.equal(50); - expect(await ERC20Contract.balanceOf(taker)).to.be.equal(49); + expect(await ERC20Contract.balanceOf(taker)).to.be.equal(50); expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(100); expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(100); @@ -1415,7 +1408,7 @@ describe('Exchange.sol', function () { await ExchangeContractAsUser.fills(hashKey(rightOrderForSecondMatch)) ).to.be.equal(50); expect(await ERC20Contract.balanceOf(maker)).to.be.equal(0); - expect(await ERC20Contract.balanceOf(taker)).to.be.equal(98); + expect(await ERC20Contract.balanceOf(taker)).to.be.equal(100); expect(await ERC20Contract2.balanceOf(maker)).to.be.equal(200); expect(await ERC20Contract2.balanceOf(taker)).to.be.equal(0); });