From 3169ea46932ef44114a215a60d1d91ef022b416d Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 31 Jan 2024 18:25:10 +0400 Subject: [PATCH 1/5] add pausable --- contracts/LimitOrderProtocol.sol | 20 +++++++++++++++++++- contracts/OrderMixin.sol | 6 ++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/contracts/LimitOrderProtocol.sol b/contracts/LimitOrderProtocol.sol index 31379675..dd6afecc 100644 --- a/contracts/LimitOrderProtocol.sol +++ b/contracts/LimitOrderProtocol.sol @@ -3,6 +3,8 @@ pragma solidity 0.8.23; import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/Pausable.sol"; import "./OrderMixin.sol"; /** @@ -28,14 +30,30 @@ import "./OrderMixin.sol"; */ contract LimitOrderProtocol is EIP712("1inch Limit Order Protocol", "4"), + Ownable, + Pausable, OrderMixin { // solhint-disable-next-line no-empty-blocks - constructor(IWETH _weth) OrderMixin(_weth) {} + constructor(IWETH _weth) OrderMixin(_weth) Ownable(msg.sender) {} /// @dev Returns the domain separator for the current chain (EIP-712) // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns(bytes32) { return _domainSeparatorV4(); } + + /** + * @notice Pauses all the trading functionality in the contract. + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice Unpauses all the trading functionality in the contract. + */ + function unpause() external onlyOwner { + _unpause(); + } } diff --git a/contracts/OrderMixin.sol b/contracts/OrderMixin.sol index 3cd12bd9..4ea578e0 100644 --- a/contracts/OrderMixin.sol +++ b/contracts/OrderMixin.sol @@ -5,6 +5,8 @@ pragma solidity 0.8.23; import "@openzeppelin/contracts/utils/math/Math.sol"; import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/utils/Pausable.sol"; + import "@1inch/solidity-utils/contracts/interfaces/IWETH.sol"; import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; import "@1inch/solidity-utils/contracts/OnlyWethReceiver.sol"; @@ -23,7 +25,7 @@ import "./libraries/RemainingInvalidatorLib.sol"; import "./OrderLib.sol"; /// @title Limit Order mixin -abstract contract OrderMixin is IOrderMixin, EIP712, OnlyWethReceiver, PredicateHelper, SeriesEpochManager, PermitAndCall { +abstract contract OrderMixin is IOrderMixin, EIP712, PredicateHelper, SeriesEpochManager, Pausable, OnlyWethReceiver, PermitAndCall { using SafeERC20 for IERC20; using SafeERC20 for IWETH; using OrderLib for IOrderMixin.Order; @@ -267,7 +269,7 @@ abstract contract OrderMixin is IOrderMixin, EIP712, OnlyWethReceiver, Predicate address target, bytes calldata extension, bytes calldata interaction - ) private returns(uint256 makingAmount, uint256 takingAmount) { + ) private whenNotPaused() returns(uint256 makingAmount, uint256 takingAmount) { // Validate order { (bool valid, bytes4 validationResult) = order.isValidExtension(extension); From 316b9edea1342f76c3649d2cdbd97832a01811ff Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 31 Jan 2024 18:49:10 +0400 Subject: [PATCH 2/5] add test --- test/LimitOrderProtocol.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/LimitOrderProtocol.js b/test/LimitOrderProtocol.js index bc4b52fb..12126716 100644 --- a/test/LimitOrderProtocol.js +++ b/test/LimitOrderProtocol.js @@ -2018,4 +2018,24 @@ describe('LimitOrderProtocol', function () { )).to.be.revertedWithCustomError(swap, 'ETHTransferFailed'); }); }); + + describe('Pause', function() { + it('Paused contract should not work', async function () { + const { tokens: { dai, weth }, contracts: { swap }, chainId } = await loadFixture(deployContractsAndInit); + + await swap.pause(); + + const order = buildOrder({ + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: 1, + takingAmount: 1, + maker: addr1.address, + makerTraits: buildMakerTraits(), + }); + + const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await swap.getAddress(), addr1)); + await expect(swap.fillOrder(order, r, vs, 1, fillWithMakingAmount(1))).to.be.revertedWithCustomError(swap, 'EnforcedPause') + }); + }); }); From ca101554861c15a83d1c32f0d902ef5039f92a3e Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 31 Jan 2024 19:41:27 +0400 Subject: [PATCH 3/5] linter --- test/LimitOrderProtocol.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/LimitOrderProtocol.js b/test/LimitOrderProtocol.js index 12126716..a26fb06e 100644 --- a/test/LimitOrderProtocol.js +++ b/test/LimitOrderProtocol.js @@ -2019,7 +2019,7 @@ describe('LimitOrderProtocol', function () { }); }); - describe('Pause', function() { + describe('Pause', function () { it('Paused contract should not work', async function () { const { tokens: { dai, weth }, contracts: { swap }, chainId } = await loadFixture(deployContractsAndInit); @@ -2035,7 +2035,7 @@ describe('LimitOrderProtocol', function () { }); const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await swap.getAddress(), addr1)); - await expect(swap.fillOrder(order, r, vs, 1, fillWithMakingAmount(1))).to.be.revertedWithCustomError(swap, 'EnforcedPause') + await expect(swap.fillOrder(order, r, vs, 1, fillWithMakingAmount(1))).to.be.revertedWithCustomError(swap, 'EnforcedPause'); }); }); }); From 5acbd12d080aa7dd4c62e81219c314f5ab8611d8 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 31 Jan 2024 20:11:41 +0400 Subject: [PATCH 4/5] add more tests --- test/LimitOrderProtocol.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/LimitOrderProtocol.js b/test/LimitOrderProtocol.js index a26fb06e..5ef91a7e 100644 --- a/test/LimitOrderProtocol.js +++ b/test/LimitOrderProtocol.js @@ -2037,5 +2037,18 @@ describe('LimitOrderProtocol', function () { const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await swap.getAddress(), addr1)); await expect(swap.fillOrder(order, r, vs, 1, fillWithMakingAmount(1))).to.be.revertedWithCustomError(swap, 'EnforcedPause'); }); + + it('pause and unpause can only be called by owner', async function () { + const { contracts: { swap } } = await loadFixture(deployContractsAndInit); + await expect(swap.connect(addr2).pause()).to.be.revertedWithCustomError(swap, 'OwnableUnauthorizedAccount', addr2.address); + await expect(swap.connect(addr2).unpause()).to.be.revertedWithCustomError(swap, 'OwnableUnauthorizedAccount', addr2.address); + }); + + it('unpause should work', async function () { + const { contracts: { swap } } = await loadFixture(deployContractsAndInit); + await swap.pause(); + await swap.unpause(); + expect(await swap.paused()).to.be.false; + }); }); }); From 1642577e93c12fc37785a79642ed82c4874015c5 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 31 Jan 2024 20:49:00 +0400 Subject: [PATCH 5/5] fix --- test/LimitOrderProtocol.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/LimitOrderProtocol.js b/test/LimitOrderProtocol.js index 5ef91a7e..c4cc301b 100644 --- a/test/LimitOrderProtocol.js +++ b/test/LimitOrderProtocol.js @@ -252,7 +252,7 @@ describe('LimitOrderProtocol', function () { if (hre.__SOLIDITY_COVERAGE_RUNNING === undefined) { const trace = findTrace(tracer, 'CALL', await swap.getAddress()); const opcodes = trace.children.map(item => item.opcode); - expect(countAllItems(opcodes)).to.deep.equal({ STATICCALL: 1, CALL: 2, SLOAD: 1, SSTORE: 1, LOG1: 1, MSTORE: 29, MLOAD: 10, SHA3: 5 }); + expect(countAllItems(opcodes)).to.deep.equal({ STATICCALL: 1, CALL: 2, SLOAD: 2, SSTORE: 1, LOG1: 1, MSTORE: 29, MLOAD: 10, SHA3: 5 }); } } }); @@ -278,7 +278,7 @@ describe('LimitOrderProtocol', function () { if (hre.__SOLIDITY_COVERAGE_RUNNING === undefined) { const trace = findTrace(tracer, 'CALL', await swap.getAddress()); const opcodes = trace.children.map(item => item.opcode); - expect(countAllItems(opcodes)).to.deep.equal({ STATICCALL: 1, CALL: 2, SLOAD: 1, SSTORE: 1, LOG1: 1, MSTORE: 31, MLOAD: 10, SHA3: 6 }); + expect(countAllItems(opcodes)).to.deep.equal({ STATICCALL: 1, CALL: 2, SLOAD: 2, SSTORE: 1, LOG1: 1, MSTORE: 31, MLOAD: 10, SHA3: 6 }); } });