diff --git a/packages/marketplace/contracts/exchange/Exchange.sol b/packages/marketplace/contracts/exchange/Exchange.sol index d25f897985..96c6faee2d 100644 --- a/packages/marketplace/contracts/exchange/Exchange.sol +++ b/packages/marketplace/contracts/exchange/Exchange.sol @@ -59,7 +59,8 @@ contract Exchange is uint256 newProtocolFeeSecondary, address newDefaultFeeReceiver, IRoyaltiesProvider newRoyaltiesProvider, - IOrderValidator orderValidatorAddress + IOrderValidator orderValidatorAddress, + uint256 maxTransfer ) external initializer { __ERC2771Handler_init_unchained(newTrustedForwarder); __AccessControlEnumerable_init_unchained(); @@ -70,7 +71,7 @@ contract Exchange is newDefaultFeeReceiver, newRoyaltiesProvider ); - __ExchangeCoreInitialize(orderValidatorAddress); + __ExchangeCoreInitialize(orderValidatorAddress, maxTransfer); _grantRole(DEFAULT_ADMIN_ROLE, admin); } @@ -113,6 +114,12 @@ contract Exchange is _setOrderValidatorContract(contractAddress); } + /// @notice setter for max transfer value + /// @param maxTransfer new vaue of max transfers + function setMaxTransferValue(uint256 maxTransfer) external onlyRole(EXCHANGE_ADMIN_ROLE) { + _setMaxTransferValue(maxTransfer); + } + /// @notice Change the address of the trusted forwarder for meta-transactions /// @param newTrustedForwarder The new trustedForwarder function setTrustedForwarder(address newTrustedForwarder) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { diff --git a/packages/marketplace/contracts/exchange/ExchangeCore.sol b/packages/marketplace/contracts/exchange/ExchangeCore.sol index ff5a32eada..44581da8ac 100644 --- a/packages/marketplace/contracts/exchange/ExchangeCore.sol +++ b/packages/marketplace/contracts/exchange/ExchangeCore.sol @@ -28,6 +28,7 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana IOrderValidator public orderValidator; uint256 private constant UINT256_MAX = type(uint256).max; + uint256 private TRANSFER_MAX; /// @notice stores the fills for orders /// @return order fill @@ -58,12 +59,18 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana ); event OrderValidatorSet(IOrderValidator indexed contractAddress); + /// @notice event for setting new maximum for transfers + /// @param newMaxTransferValue new maximum + event MaxTransferSet(uint256 newMaxTransferValue); + /// @notice initializer for ExchangeCore /// @param newOrderValidatorAddress new OrderValidator contract address /// @dev initialize permissions for native token exchange // solhint-disable-next-line func-name-mixedcase - function __ExchangeCoreInitialize(IOrderValidator newOrderValidatorAddress) internal onlyInitializing { + function __ExchangeCoreInitialize(IOrderValidator newOrderValidatorAddress, uint256 maxTransfer) internal onlyInitializing { _setOrderValidatorContract(newOrderValidatorAddress); + TRANSFER_MAX = maxTransfer; + } /// @notice set OrderValidator address @@ -74,6 +81,13 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana emit OrderValidatorSet(contractAddress); } + /// @notice setter for max transfer value + /// @param maxTransfer new vaue of max transfers + function _setMaxTransferValue(uint256 maxTransfer) internal { + MAX_TRANSFER = maxTransfer; + emit MaxTransferSet(MAX_TRANSFER); + } + /// @notice cancel order /// @param order to be canceled /// @param orderKeyHash used as a checksum to avoid mistakes in the values of order @@ -92,7 +106,7 @@ abstract contract ExchangeCore is Initializable, TransferExecutor, ITransferMana /// @dev validate orders through validateOrders before matchAndTransfer function _matchOrders(address sender, ExchangeMatch[] calldata matchedOrders) internal { uint256 len = matchedOrders.length; - require(len > 0, "invalid exchange match"); + require(len > 0 && len < TRANSFER_MAX, "invalid exchange match quantities"); for (uint256 i; i < len; i++) { ExchangeMatch calldata m = matchedOrders[i]; _validateOrders(sender, m.orderLeft, m.signatureLeft, m.orderRight, m.signatureRight); diff --git a/packages/marketplace/test/exchange/Exchange.test.ts b/packages/marketplace/test/exchange/Exchange.test.ts index 8aa7a2e3c8..7401b06f87 100644 --- a/packages/marketplace/test/exchange/Exchange.test.ts +++ b/packages/marketplace/test/exchange/Exchange.test.ts @@ -1957,7 +1957,7 @@ describe('Exchange.sol', function () { } }); - it('should be able to buy 150 tokens from different orders in one txs', async function () { + it('should not be able to buy 150 tokens from different orders in one txs, max transfers = 150', async function () { const { ExchangeContractAsUser, OrderValidatorAsAdmin, @@ -2019,19 +2019,11 @@ describe('Exchange.sol', function () { rightOrders.push(rightorder); } - const tx = await ExchangeContractAsUser.matchOrders(rightOrders); - - const receipt = await tx.wait(); - console.log('Gas used for 150 tokens: ' + receipt.gasUsed); - - expect(await ERC20Contract.balanceOf(taker)).to.be.equal(0); + await expect( + ExchangeContractAsUser.matchOrders(rightOrders) + + ).to.be.revertedWith('invalid exchange match quantities'); - expect(await ERC20Contract.balanceOf(maker)).to.be.equal( - totalPayment - 2 * 150 - ); - for (let i = 0; i < 150; i++) { - expect(await ERC721Contract.ownerOf(i)).to.be.equal(taker.address); - } }); }); diff --git a/packages/marketplace/test/exchange/ExchangeSettings.test.ts b/packages/marketplace/test/exchange/ExchangeSettings.test.ts index e3e44714a5..4699763c22 100644 --- a/packages/marketplace/test/exchange/ExchangeSettings.test.ts +++ b/packages/marketplace/test/exchange/ExchangeSettings.test.ts @@ -160,6 +160,29 @@ describe('Exchange.sol settings', function () { .to.emit(ExchangeContractAsAdmin, 'DefaultFeeReceiverSet') .withArgs(user.address); }); + it('should not set setMaxTransferValue if caller is not in the role', async function () { + const {EXCHANGE_ADMIN_ROLE, ExchangeContractAsUser, user} = + await loadFixture(deployFixtures); + const newMaxTransfer = 200; + await expect( + ExchangeContractAsUser.setMaxTransferValue(newMaxTransfer) + ).to.be.revertedWith( + `AccessControl: account ${user.address.toLowerCase()} is missing role ${EXCHANGE_ADMIN_ROLE}` + ); + }); + it('should be able to set setMaxTransferValue', async function () { + const { + EXCHANGE_ADMIN_ROLE, + ExchangeContractAsAdmin, + ExchangeContractAsUser, + user, + } = await loadFixture(deployFixtures); + const newMaxTransfer = 200; + await ExchangeContractAsAdmin.grantRole(EXCHANGE_ADMIN_ROLE, user); + await expect(ExchangeContractAsUser.setMaxTransferValue(newMaxTransfer)) + .to.emit(ExchangeContractAsAdmin, 'MaxTransferSet') + .withArgs(newMaxTransfer); + }); }); shouldNotBeAbleToSetAsZero( diff --git a/packages/marketplace/test/fixtures.ts b/packages/marketplace/test/fixtures.ts index 605cce9468..c4f0cb991e 100644 --- a/packages/marketplace/test/fixtures.ts +++ b/packages/marketplace/test/fixtures.ts @@ -38,6 +38,7 @@ async function deploy() { const OrderValidatorAsAdmin = await OrderValidatorAsDeployer.connect(admin); const protocolFeePrimary = 123; const protocolFeeSecondary = 250; + const maxTransfers = 150; const ExchangeFactory = await ethers.getContractFactory('Exchange'); const ExchangeContractAsDeployer = await upgrades.deployProxy( ExchangeFactory, @@ -49,6 +50,7 @@ async function deploy() { defaultFeeReceiver.address, await RoyaltiesRegistryAsDeployer.getAddress(), await OrderValidatorAsAdmin.getAddress(), + maxTransfers ], { initializer: '__Exchange_init',