diff --git a/packages/loopring_v3/contracts/aux/access/DelayedSelectorBasedAccessManager.sol b/packages/loopring_v3/contracts/aux/access/DelayedSelectorBasedAccessManager.sol new file mode 100644 index 000000000..5d1d5ee4a --- /dev/null +++ b/packages/loopring_v3/contracts/aux/access/DelayedSelectorBasedAccessManager.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2017 Loopring Technology Limited. +pragma solidity ^0.7.0; + +import "../../lib/Claimable.sol"; +import "../../thirdparty/BytesUtil.sol"; +import "./DelayedTransaction.sol"; + +/// @title DelayedSelectorBasedAccessManager +/// @author Break Xiong - +contract DelayedSelectorBasedAccessManager is DelayedTransaction, Claimable { + using BytesUtil for bytes; + + address public immutable target; + mapping(address => mapping(bytes4 => bool)) public permissions; + + event PermissionUpdate( + address indexed user, + bytes4 indexed selector, + bool allowed + ); + + modifier withAccess(bytes4 selector) { + require(hasAccessTo(msg.sender, selector), "PERMISSION_DENIED"); + _; + } + + constructor( + address _target, + uint _timeToLive + ) DelayedTransaction(_timeToLive) { + require(_target != address(0), "ZERO_ADDRESS"); + target = _target; + } + + receive() external payable {} + + fallback() external payable { + transact(msg.data); + } + + function grantAccess( + address user, + bytes4 selector, + bool granted + ) external onlyOwner { + require(permissions[user][selector] != granted, "INVALID_VALUE"); + permissions[user][selector] = granted; + emit PermissionUpdate(user, selector, granted); + } + + function transact( + bytes calldata data + ) public payable withAccess(data.toBytes4(0)) { + transactInternal(target, msg.value, data); + } + + function transact( + address, + bytes calldata + ) external payable override nonReentrant onlyAuthorized { + revert("Deprecated"); + } + + function hasAccessTo( + address user, + bytes4 selector + ) public view returns (bool) { + return user == owner || permissions[user][selector]; + } + + function isAuthorizedForTransactions( + address sender + ) internal view override returns (bool) { + return hasAccessTo(sender, msg.data.toBytes4(0)); + } + + function setFunctionDelay(bytes4 functionSelector, uint delay) internal { + setFunctionDelay(target, functionSelector, delay); + } +} diff --git a/packages/loopring_v3/contracts/aux/access/DelayedTransaction.sol b/packages/loopring_v3/contracts/aux/access/DelayedTransaction.sol index bd19dac61..e726f7677 100644 --- a/packages/loopring_v3/contracts/aux/access/DelayedTransaction.sol +++ b/packages/loopring_v3/contracts/aux/access/DelayedTransaction.sol @@ -49,9 +49,10 @@ abstract contract DelayedTransaction is IDelayedTransaction, ReentrancyGuard bytes calldata data ) external + payable + virtual override nonReentrant - payable onlyAuthorized { transactInternal(to, msg.value, data); diff --git a/packages/loopring_v3/contracts/aux/access/LoopringIOExchangeOwner.sol b/packages/loopring_v3/contracts/aux/access/LoopringIOExchangeOwner.sol index 8384a40fa..bade3fc92 100644 --- a/packages/loopring_v3/contracts/aux/access/LoopringIOExchangeOwner.sol +++ b/packages/loopring_v3/contracts/aux/access/LoopringIOExchangeOwner.sol @@ -12,11 +12,11 @@ import "../../lib/Drainable.sol"; import "../../lib/ERC1271.sol"; import "../../lib/MathUint.sol"; import "../../lib/SignatureUtil.sol"; -import "./SelectorBasedAccessManager.sol"; +import "./DelayedSelectorBasedAccessManager.sol"; import "./IBlockReceiver.sol"; -contract LoopringIOExchangeOwner is SelectorBasedAccessManager, ERC1271, Drainable +contract LoopringIOExchangeOwner is DelayedSelectorBasedAccessManager, ERC1271, Drainable { using AddressUtil for address; using AddressUtil for address payable; @@ -53,8 +53,10 @@ contract LoopringIOExchangeOwner is SelectorBasedAccessManager, ERC1271, Drainab constructor( address _exchange ) - SelectorBasedAccessManager(_exchange) + DelayedSelectorBasedAccessManager(_exchange, 3 days) { + setFunctionDelay(IExchangeV3.setLoopring.selector, 7 days); + setFunctionDelay(IExchangeV3.refreshBlockVerifier.selector, 7 days); } function openAccessToSubmitBlocks(bool _open) diff --git a/packages/loopring_v3/contracts/aux/access/LoopringV3Owner.sol b/packages/loopring_v3/contracts/aux/access/LoopringV3Owner.sol new file mode 100644 index 000000000..ff10d15cf --- /dev/null +++ b/packages/loopring_v3/contracts/aux/access/LoopringV3Owner.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2017 Loopring Technology Limited. +pragma solidity ^0.7.0; + +import "../../core/iface/ILoopringV3.sol"; +import "./DelayedOwner.sol"; + +/// @title LoopringV3Owner +/// @author Break Xiong - +contract LoopringV3Owner is DelayedOwner { + constructor( + ILoopringV3 loopringV3 + ) DelayedOwner(address(loopringV3), 3 days) { + setFunctionDelay(loopringV3.transferOwnership.selector, 7 days); + setFunctionDelay(loopringV3.updateSettings.selector, 7 days); + setFunctionDelay(loopringV3.updateProtocolFeeSettings.selector, 1 days); + } +} diff --git a/packages/loopring_v3/contracts/aux/access/ProxyOwner.sol b/packages/loopring_v3/contracts/aux/access/ProxyOwner.sol new file mode 100644 index 000000000..66e336011 --- /dev/null +++ b/packages/loopring_v3/contracts/aux/access/ProxyOwner.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2017 Loopring Technology Limited. +pragma solidity ^0.7.0; + +import "./DelayedOwner.sol"; + +/// @title IProxy +/// @author Break Xiong - +interface IProxy { + function transferProxyOwnership(address newOwner) external; + function upgradeTo(address implementation) external; + function upgradeToAndCall( + address implementation, + bytes memory data + ) external payable; +} + +/// @title ProxyOwner +/// @author Break Xiong - +contract ProxyOwner is DelayedOwner { + constructor(IProxy proxy) DelayedOwner(address(proxy), 3 days) { + setFunctionDelay(proxy.transferProxyOwnership.selector, 7 days); + setFunctionDelay(proxy.upgradeTo.selector, 7 days); + setFunctionDelay(proxy.upgradeToAndCall.selector, 7 days); + } +} diff --git a/packages/loopring_v3/contracts/core/iface/ExchangeData.sol b/packages/loopring_v3/contracts/core/iface/ExchangeData.sol index 8bc062c8a..3a0131b2b 100644 --- a/packages/loopring_v3/contracts/core/iface/ExchangeData.sol +++ b/packages/loopring_v3/contracts/core/iface/ExchangeData.sol @@ -124,6 +124,8 @@ library ExchangeData // This is the prime number that is used for the alt_bn128 elliptic curve, see EIP-196. uint public constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // 0.1% of pending deposited amount + uint public constant MIN_DEPOSIT_PERCENTAGE = 1; uint public constant MAX_OPEN_FORCED_REQUESTS = 4096; uint public constant MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE = 15 days; uint public constant TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS = 7 days; diff --git a/packages/loopring_v3/contracts/core/iface/IExchangeV3.sol b/packages/loopring_v3/contracts/core/iface/IExchangeV3.sol index 6371ef681..68ef72e91 100644 --- a/packages/loopring_v3/contracts/core/iface/IExchangeV3.sol +++ b/packages/loopring_v3/contracts/core/iface/IExchangeV3.sol @@ -131,6 +131,9 @@ abstract contract IExchangeV3 is Claimable virtual external; + function getLoopring() external view virtual returns (address); + function setLoopring(address loopringAddr) external virtual; + /// @dev Initialized the agent registry contract used by the exchange. /// Can only be called by the exchange owner once. /// @param agentRegistry The agent registry contract to be used diff --git a/packages/loopring_v3/contracts/core/impl/ExchangeV3.sol b/packages/loopring_v3/contracts/core/impl/ExchangeV3.sol index b6eb9705e..a05c8d2d7 100644 --- a/packages/loopring_v3/contracts/core/impl/ExchangeV3.sol +++ b/packages/loopring_v3/contracts/core/impl/ExchangeV3.sol @@ -102,6 +102,27 @@ contract ExchangeV3 is IExchangeV3, ReentrancyGuard, ERC1155Holder, ERC721Holder ); } + function setLoopring(address _loopringAddr) + external + override + nonReentrant + onlyOwner + { + require(_loopringAddr != address(0), "ZERO_ADDRESS"); + // set loopring + state.loopringAddr = _loopringAddr; + state.loopring = ILoopringV3(_loopringAddr); + } + + function getLoopring() + external + view + override + returns (address) + { + return state.loopringAddr; + } + function setAgentRegistry(address _agentRegistry) external override diff --git a/packages/loopring_v3/contracts/core/impl/LoopringV3.sol b/packages/loopring_v3/contracts/core/impl/LoopringV3.sol index 127015215..279ce3ba5 100644 --- a/packages/loopring_v3/contracts/core/impl/LoopringV3.sol +++ b/packages/loopring_v3/contracts/core/impl/LoopringV3.sol @@ -165,6 +165,10 @@ contract LoopringV3 is ILoopringV3, ReentrancyGuard { require(address(0) != _protocolFeeVault, "ZERO_ADDRESS"); require(address(0) != _blockVerifierAddress, "ZERO_ADDRESS"); + require( + _forcedWithdrawalFee <= 0.5 ether, + "FORCED_WITHDRAWAL_FEE_TOO_HIGH" + ); protocolFeeVault = _protocolFeeVault; blockVerifierAddress = _blockVerifierAddress; diff --git a/packages/loopring_v3/contracts/core/impl/libexchange/ExchangeDeposits.sol b/packages/loopring_v3/contracts/core/impl/libexchange/ExchangeDeposits.sol index ff121c0d4..cad63df25 100644 --- a/packages/loopring_v3/contracts/core/impl/libexchange/ExchangeDeposits.sol +++ b/packages/loopring_v3/contracts/core/impl/libexchange/ExchangeDeposits.sol @@ -57,9 +57,16 @@ library ExchangeDeposits // Allow depositing with amount == 0 to allow updating the deposit timestamp uint16 tokenID = S.getTokenID(tokenAddress); + ExchangeData.Deposit memory _deposit = S.pendingDeposits[to][tokenID]; + // prevent from attackers to deposit too little tokens + require( + amount * 1000 >= + _deposit.amount * ExchangeData.MIN_DEPOSIT_PERCENTAGE, + "DEPOSIT_TOO_LITTLE" + ); if (tokenID == 0 && amount == 0) { - require(msg.value == 0), "INVALID_ETH_DEPOSIT"); + require(msg.value == 0, "INVALID_ETH_DEPOSIT"); } // Transfer the tokens to this contract @@ -71,7 +78,6 @@ library ExchangeDeposits ); // Add the amount to the deposit request and reset the time the operator has to process it - ExchangeData.Deposit memory _deposit = S.pendingDeposits[to][tokenID]; _deposit.timestamp = uint64(block.timestamp); _deposit.amount = _deposit.amount.add(amountDeposited); S.pendingDeposits[to][tokenID] = _deposit;