-
Notifications
You must be signed in to change notification settings - Fork 157
Simple native orders #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Simple native orders #391
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ import { Errors } from "../libraries/Errors.sol"; | |
| import { EIP712Alien } from "../mocks/EIP712Alien.sol"; | ||
| import { OrderLib, IOrderMixin } from "../OrderLib.sol"; | ||
|
|
||
| // This contract is owning makers' funds for native orders | ||
| contract NativeOrderImpl is IERC1271, EIP712Alien, OnlyWethReceiver { | ||
| using Clones for address; | ||
| using AddressLib for Address; | ||
|
|
@@ -22,40 +23,45 @@ contract NativeOrderImpl is IERC1271, EIP712Alien, OnlyWethReceiver { | |
| using OrderLib for IOrderMixin.Order; | ||
| using MakerTraitsLib for MakerTraits; | ||
|
|
||
| event NativeOrderCancelled(bytes32 orderHash, uint256 balance); | ||
| event NativeOrderCancelledByResolver(bytes32 orderHash, uint256 balance, uint256 resolverReward); | ||
| event NativeOrderCancelled(uint256 balance); | ||
| event NativeOrderCancelledByResolver(uint256 balance, uint256 resolverReward); | ||
|
|
||
| error OnlyLimitOrderProtocolViolation(address sender, address limitOrderProtocol); | ||
| error OnlyFactoryViolation(address sender, address factory); | ||
| error OnlyMakerViolation(address sender, address maker); | ||
| error OnlyMakerViolation(address sender, uint80 makerTail); | ||
| error ResolverAccessTokenMissing(address resolver, address accessToken); | ||
| error OrderIsIncorrect(address expected, address actual); | ||
| error OrderShouldBeExpired(uint256 currentTime, uint256 expirationTime); | ||
| error CanNotCancelForZeroBalance(); | ||
| error RescueFundsTooMuch(uint256 requested, uint256 available); | ||
| error CancellationDelayViolation(uint256 timePassedSinceExpiration, uint256 requiredDelay); | ||
| error WrongMakerArgument(address maker, uint80 expectedTail); | ||
|
|
||
| uint256 private constant _CANCEL_GAS_LOWER_BOUND = 70_000; | ||
|
|
||
| IWETH private immutable _WETH; | ||
| address private immutable _LOP; | ||
| address private immutable _IMPLEMENTATION = address(this); | ||
| address private immutable _FACTORY; | ||
| IERC20 private immutable _ACCESS_TOKEN; | ||
| uint256 private immutable _CANCELLATION_DELAY; | ||
|
|
||
| struct Purpose { | ||
| uint80 orderHashTail; | ||
| uint80 makerTail; | ||
| uint40 expiration; | ||
| } | ||
|
|
||
| Purpose public purpose; | ||
|
|
||
| modifier onlyFactory { | ||
| if (msg.sender != _FACTORY) revert OnlyFactoryViolation(msg.sender, _FACTORY); | ||
| _; | ||
| } | ||
|
|
||
| modifier onlyResolver { | ||
| if (_ACCESS_TOKEN.balanceOf(msg.sender) == 0) revert ResolverAccessTokenMissing(msg.sender, address(_ACCESS_TOKEN)); | ||
| modifier onlyMaker { | ||
| if (uint80(uint160(msg.sender)) != purpose.makerTail) revert OnlyMakerViolation(msg.sender, purpose.makerTail); | ||
k06a marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| _; | ||
| } | ||
|
|
||
| modifier onlyMaker(address maker) { | ||
| if (msg.sender != maker) revert OnlyMakerViolation(msg.sender, maker); | ||
| modifier onlyResolver { | ||
| if (_ACCESS_TOKEN.balanceOf(msg.sender) == 0) revert ResolverAccessTokenMissing(msg.sender, address(_ACCESS_TOKEN)); | ||
| _; | ||
| } | ||
|
|
||
|
|
@@ -78,89 +84,63 @@ contract NativeOrderImpl is IERC1271, EIP712Alien, OnlyWethReceiver { | |
| _CANCELLATION_DELAY = cancellationDelay; | ||
| } | ||
|
|
||
| function depositAndApprove() external payable onlyFactory { | ||
| function deposit(address maker, bytes32 orderHash, uint40 expiration) external payable onlyFactory { | ||
| purpose = Purpose({ | ||
| orderHashTail: uint80(uint256(orderHash)), | ||
| makerTail: uint80(uint160(maker)), | ||
| expiration: expiration | ||
| }); | ||
| _WETH.safeDeposit(msg.value); | ||
| _WETH.forceApprove(_LOP, msg.value); | ||
| } | ||
|
|
||
| function isValidSignature(bytes32 hash, bytes calldata signature) external view returns(bytes4) { | ||
| // Extract order from signature via calldata type casting | ||
| IOrderMixin.Order calldata makerOrder; | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| makerOrder := signature.offset | ||
| function isValidSignature(bytes32 hash, bytes calldata /* signature */) external view returns(bytes4) { | ||
| uint80 orderHashTail = purpose.orderHashTail; | ||
| uint256 expiration = purpose.expiration; | ||
| if (uint80(uint256(hash)) != orderHashTail) { | ||
|
||
| return 0xffffffff; | ||
| } | ||
|
|
||
| // Check order args by CREATE2 salt validation | ||
| bytes32 makerOrderHash = makerOrder.hash(_domainSeparatorV4()); | ||
| address clone = _IMPLEMENTATION.predictDeterministicAddress(makerOrderHash, _FACTORY); | ||
| if (clone != address(this)) { | ||
| return bytes4(0); | ||
| } | ||
|
|
||
| // Check if patched order from signature matches LOP filling order | ||
| bytes32 orderHash = _patchOrderMakerAndHash(makerOrder); | ||
| if (orderHash != hash) { | ||
| return bytes4(0); | ||
| if (block.timestamp >= expiration) { | ||
| return 0xffffffff; | ||
| } | ||
|
|
||
| return this.isValidSignature.selector; | ||
| } | ||
|
|
||
| function cancelOrder(IOrderMixin.Order calldata makerOrder) external onlyMaker(makerOrder.maker.get()) { | ||
| uint256 balance = _cancelOrder(makerOrder, 0); | ||
| bytes32 orderHash = _patchOrderMakerAndHash(makerOrder); | ||
| emit NativeOrderCancelled(orderHash, balance); | ||
| function cancelOrder() external onlyMaker { | ||
| uint256 balance = _WETH.safeBalanceOf(address(this)); | ||
| _WETH.safeWithdrawTo(balance, msg.sender); | ||
| emit NativeOrderCancelled(balance); | ||
| } | ||
|
|
||
| function cancelExpiredOrderByResolver(IOrderMixin.Order calldata makerOrder, uint256 rewardLimit) external onlyResolver { | ||
| uint256 orderExpiration = makerOrder.makerTraits.getExpirationTime(); | ||
| if (!makerOrder.makerTraits.isExpired()) revert OrderShouldBeExpired(block.timestamp, orderExpiration); | ||
|
|
||
| uint256 resolverReward = 0; | ||
| if (rewardLimit > 0) { | ||
| if (block.timestamp - orderExpiration < _CANCELLATION_DELAY) revert CancellationDelayViolation(block.timestamp - orderExpiration, _CANCELLATION_DELAY); | ||
| resolverReward = Math.min(rewardLimit, block.basefee * _CANCEL_GAS_LOWER_BOUND * 1.1e18 / 1e18); | ||
| function rescueFunds(address token, address to, uint256 amount) external onlyMaker { | ||
| if (token == address(0)) { | ||
| (bool success, ) = to.call{ value: amount }(""); | ||
| if (!success) revert Errors.ETHTransferFailed(); | ||
| } else { | ||
| IERC20(token).safeTransfer(to, amount); | ||
| } | ||
| uint256 balance = _cancelOrder(makerOrder, resolverReward); | ||
| bytes32 orderHash = _patchOrderMakerAndHash(makerOrder); | ||
| emit NativeOrderCancelledByResolver(orderHash, balance, resolverReward); | ||
| } | ||
k06a marked this conversation as resolved.
Show resolved
Hide resolved
k06a marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| function _cancelOrder(IOrderMixin.Order calldata makerOrder, uint256 resolverReward) private returns(uint256 balance) { | ||
| bytes32 makerOrderHash = makerOrder.hash(_domainSeparatorV4()); | ||
| address clone = _IMPLEMENTATION.predictDeterministicAddress(makerOrderHash, _FACTORY); | ||
| if (clone != address(this)) revert OrderIsIncorrect(clone, address(this)); | ||
| function cancelExpiredOrderByResolver(address maker, uint256 rewardLimit) external onlyResolver { | ||
| uint80 makerTail = purpose.makerTail; | ||
| uint256 purposeExpiration = purpose.expiration; | ||
| if (uint80(uint160(maker)) != makerTail) revert WrongMakerArgument(maker, makerTail); | ||
k06a marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (block.timestamp < purposeExpiration) revert OrderShouldBeExpired(block.timestamp, purposeExpiration); | ||
|
|
||
| balance = _WETH.safeBalanceOf(address(this)); | ||
| uint256 balance = _WETH.safeBalanceOf(address(this)); | ||
k06a marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (balance == 0) revert CanNotCancelForZeroBalance(); | ||
|
|
||
| _WETH.safeWithdraw(balance); | ||
| if (resolverReward > 0) { | ||
| uint256 resolverReward; | ||
| if (rewardLimit > 0) { | ||
| if (block.timestamp - purposeExpiration < _CANCELLATION_DELAY) revert CancellationDelayViolation(block.timestamp - purposeExpiration, _CANCELLATION_DELAY); | ||
| resolverReward = Math.min(rewardLimit, block.basefee * _CANCEL_GAS_LOWER_BOUND * 1.1e18 / 1e18); // base fee + 10% | ||
k06a marked this conversation as resolved.
Show resolved
Hide resolved
k06a marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| balance -= resolverReward; | ||
| (bool success, ) = msg.sender.call{ value: resolverReward }(""); | ||
| if (!success) revert Errors.ETHTransferFailed(); | ||
| } | ||
| if (balance > 0) { | ||
| (bool success, ) = makerOrder.maker.get().call{ value: balance }(""); | ||
| (bool success, ) = maker.call{ value: balance }(""); | ||
| if (!success) revert Errors.ETHTransferFailed(); | ||
| } | ||
| } | ||
|
|
||
| function rescueFunds(address token, address to, uint256 amount) external onlyResolver { | ||
| if (token == address(0)) { | ||
| payable(to).transfer(amount); | ||
| } else if (IWETH(token) == _WETH) { | ||
| uint256 remainingOrderAmount = _WETH.allowance(address(this), _LOP); | ||
| uint256 extraAmount = _WETH.safeBalanceOf(address(this)) - remainingOrderAmount; | ||
| if (amount > extraAmount) revert RescueFundsTooMuch(amount, extraAmount); | ||
| _WETH.safeTransfer(to, amount); | ||
| } else { | ||
| IERC20(token).safeTransfer(to, amount); | ||
| } | ||
| } | ||
|
|
||
| function _patchOrderMakerAndHash(IOrderMixin.Order memory order) private view returns(bytes32) { | ||
| order.maker = Address.wrap(uint160(address(this))); | ||
| return order.hashMemory(_domainSeparatorV4()); | ||
| emit NativeOrderCancelledByResolver(balance, resolverReward); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.