22
33pragma solidity 0.8.23 ;
44
5- import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol " ;
6- import "@1inch/solidity-utils/contracts/mixins/OnlyWethReceiver.sol " ;
7- import "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
5+ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol " ;
6+ import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol " ;
7+ import { OnlyWethReceiver } from "@1inch/solidity-utils/contracts/mixins/OnlyWethReceiver.sol " ;
8+ import { IWETH } from "@1inch/solidity-utils/contracts/interfaces/IWETH.sol " ;
9+ import { Address, AddressLib } from "@1inch/solidity-utils/contracts/libraries/AddressLib.sol " ;
810
911import { EIP712Alien } from "contracts/mocks/EIP712Alien.sol " ;
10- import "../interfaces/IPostInteraction.sol " ;
11- import "../OrderLib.sol " ;
12+ import { IPostInteraction } from "../interfaces/IPostInteraction.sol " ;
13+ import { OrderLib, IOrderMixin } from "../OrderLib.sol " ;
14+ import { MakerTraits, MakerTraitsLib } from "../libraries/MakerTraitsLib.sol " ;
15+ import { ExtensionLib } from "../libraries/ExtensionLib.sol " ;
1216
1317/// @title Extension that will allow to create limit order that sell ETH. ETH must be deposited into the contract.
14- contract ETHOrders is IPostInteraction , OnlyWethReceiver , EIP712Alien {
18+ contract ETHOrders is ERC20 , IPostInteraction , OnlyWethReceiver , EIP712Alien {
1519 using SafeERC20 for IWETH;
20+ using SafeERC20 for IERC20 ;
1621 using OrderLib for IOrderMixin.Order;
1722 using MakerTraitsLib for MakerTraits;
1823 using ExtensionLib for bytes ;
1924 using AddressLib for Address;
2025
21- error AccessDenied ();
22- error InvalidOrder ();
23- error NotEnoughBalance ();
24- error ExistingOrder ();
25- error OrderNotExpired ();
26- error RewardIsTooBig ();
27- error CancelOrderByResolverIsForbidden ();
28-
2926 event ETHDeposited (address maker , bytes32 orderHash , uint256 amount );
3027 event ETHOrderCancelled (bytes32 orderHash , uint256 amount );
3128 event ETHOrderCancelledByThirdParty (bytes32 orderHash , uint256 amount , uint256 reward );
29+ event ETHCancelled (address maker , bytes32 orderHash , uint256 amount );
3230
33- error OrderShouldHavePostInteractionCallFlag (MakerTraits makerTraits );
31+ error AccessDenied (address caller );
32+ error OrderShouldNotExist (address maker , bytes32 orderHash , uint256 balance );
33+ error OrderShouldRequirePostInteractionCall (MakerTraits makerTraits );
3434 error OrderMakerShouldBeThisContract (address maker , address thisContract );
3535 error OrderReceiverShouldNotBeThis (address receiver , address self );
3636 error OrderMakingAmountShouldBeEqualToMsgValue (uint256 makingAmount , uint256 msgValue );
3737 error OrderPostInteractionTargetShouldBeThisContract (address target , address thisContract );
38- error OrderNotExpiredYet (uint256 currentTimestamp , uint256 expirationTimestamp );
38+ error OrderExtensionInvalid (bytes4 exception );
39+ error OrderShouldBeExpired (uint256 currentTimestamp , uint256 expirationTimestamp );
40+ error CanNotCancelForZeroBalance ();
41+ error RescueFundsTooMuch (uint256 requested , uint256 available );
42+ error PostInteractionExtraDataShouldMatchMaker (address orderMaker , address maker );
3943
40- /// @notice Deposit information for an order
44+ /// @notice Bond information for an order
4145 /// @param balance The amount of ETH deposited for the order
42- /// @param maxGasCostsBump The maximum gas costs bump in basis points (1_500 = +50%)
46+ /// @param auctionMaxGasBump The maximum gas costs bump in basis points (1_500 = +50%)
4347 /// @param auctionDuration The duration of the auction in seconds
44- struct Deposit {
48+ struct Bond {
4549 uint208 balance;
46- uint16 maxGasCostsBump ; // in basis points (1_000)
50+ uint16 auctionMaxGasBump ; // in basis points (1_000)
4751 uint32 auctionDuration;
4852 }
4953
@@ -54,10 +58,10 @@ contract ETHOrders is IPostInteraction, OnlyWethReceiver, EIP712Alien {
5458 IWETH private immutable _WETH;
5559 IERC20 private immutable _ACCESS_TOKEN;
5660
57- mapping (address maker = > mapping (bytes32 orderHash = > Deposit data )) public deposits ;
61+ mapping (address maker = > mapping (bytes32 orderHash = > Bond )) public bonds ;
5862
5963 modifier onlyLimitOrderProtocolOrOneOfTails (bytes calldata allowedMsgSenders ) {
60- if (! _onlyLimitOrderProtocolOrOneOfTails (allowedMsgSenders)) revert AccessDenied ();
64+ if (! _onlyLimitOrderProtocolOrOneOfTails (allowedMsgSenders)) revert AccessDenied (msg . sender );
6165 _;
6266 }
6367
@@ -72,11 +76,18 @@ contract ETHOrders is IPostInteraction, OnlyWethReceiver, EIP712Alien {
7276 }
7377
7478 modifier onlyResolver {
75- if (_ACCESS_TOKEN.balanceOf (msg .sender ) == 0 ) revert AccessDenied ();
79+ if (_ACCESS_TOKEN.balanceOf (msg .sender ) == 0 ) revert AccessDenied (msg . sender );
7680 _;
7781 }
7882
79- constructor (IWETH weth , address limitOrderProtocol , IERC20 accessToken )
83+ constructor (
84+ IWETH weth ,
85+ address limitOrderProtocol ,
86+ IERC20 accessToken ,
87+ string memory name ,
88+ string memory symbol
89+ )
90+ ERC20 (name, symbol)
8091 OnlyWethReceiver (address (weth))
8192 EIP712Alien (_LIMIT_ORDER_PROTOCOL, "1inch Limit Order Protocol " , "4 " )
8293 {
@@ -86,97 +97,116 @@ contract ETHOrders is IPostInteraction, OnlyWethReceiver, EIP712Alien {
8697 _WETH.forceApprove (limitOrderProtocol, type (uint256 ).max);
8798 }
8899
89- function depositForOrder (
100+ function validateOrder (
101+ address maker ,
102+ uint256 msgValue ,
90103 IOrderMixin.Order calldata order ,
91- bytes calldata extension ,
92- uint16 maxGasCostsBump ,
93- uint32 auctionDuration
94- ) external payable {
104+ bytes calldata extension
105+ ) external view returns (bool ) {
95106 // Validate main order parameters
96107 if (order.maker.get () != address (this )) revert OrderMakerShouldBeThisContract (order.maker.get (), address (this ));
97108 if (order.getReceiver () == address (this )) revert OrderReceiverShouldNotBeThis (order.getReceiver (), address (this ));
98- if (order.makingAmount != msg . value ) revert OrderMakingAmountShouldBeEqualToMsgValue (order.makingAmount, msg . value );
109+ if (order.makingAmount != msgValue ) revert OrderMakingAmountShouldBeEqualToMsgValue (order.makingAmount, msgValue );
99110
100111 // Validate post interaction flag and target
101- if (! order.makerTraits.needPostInteractionCall ()) revert OrderShouldHavePostInteractionCallFlag (order.makerTraits);
112+ if (! order.makerTraits.needPostInteractionCall ()) revert OrderShouldRequirePostInteractionCall (order.makerTraits);
102113 (bool isExtensionValid , bytes4 validationResult ) = order.isValidExtension (extension);
103- if (! isExtensionValid) {
104- // solhint-disable-next-line no-inline-assembly
105- assembly ("memory-safe" ) {
106- mstore (0 , validationResult)
107- revert (0 , 4 )
108- }
109- }
114+ if (! isExtensionValid) revert OrderExtensionInvalid (validationResult);
115+
116+ // Validate post-interaction target and data
110117 bytes calldata interaction = extension.postInteractionTargetAndData ();
111118 if (interaction.length != 20 || address (bytes20 (interaction)) != address (this )) revert OrderPostInteractionTargetShouldBeThisContract (address (bytes20 (interaction)), address (this ));
119+ address orderMaker = address (bytes20 (interaction[20 :40 ]));
120+ if (orderMaker != maker) revert PostInteractionExtraDataShouldMatchMaker (orderMaker, maker);
112121
113122 // EIP712Alien._domainSeparatorV4() is cheaper than call IOrderMixin(_LIMIT_ORDER_PROTOCOL).hashOrder(order);
114123 bytes32 orderHash = order.hash (_domainSeparatorV4 ());
115- depositForOrderHash (orderHash, maxGasCostsBump, auctionDuration);
124+ if (bonds[maker][orderHash].balance != 0 ) revert OrderShouldNotExist (maker, orderHash, bonds[maker][orderHash].balance);
125+ return true ;
116126 }
117127
118- function depositForOrderHash (
128+ function deposit (
119129 bytes32 orderHash ,
120- uint16 maxGasCostsBump ,
130+ uint16 auctionMaxGasBump ,
121131 uint32 auctionDuration
122132 ) public payable {
123- if (deposits [msg .sender ][orderHash].balance != 0 ) revert ExistingOrder ( );
133+ if (bonds [msg .sender ][orderHash].balance != 0 ) revert OrderShouldNotExist ( msg . sender , orderHash, bonds[ msg . sender ][orderHash].balance );
124134
125- deposits[msg .sender ][orderHash] = Deposit ({
135+ _mint (msg .sender , msg .value );
136+ bonds[msg .sender ][orderHash] = Bond ({
126137 balance: uint208 (msg .value ),
127- maxGasCostsBump: maxGasCostsBump ,
138+ auctionMaxGasBump: auctionMaxGasBump ,
128139 auctionDuration: auctionDuration
129140 });
130141 _WETH.safeDeposit (msg .value );
131142 emit ETHDeposited (msg .sender , orderHash, msg .value );
132143 }
133144
134145 function isValidSignature (bytes32 orderHash , bytes calldata signature ) external view returns (bytes4 ) {
135- address maker = address (uint160 ( bytes20 (signature) ));
136- uint256 balance = deposits [maker][orderHash].balance;
146+ address maker = address (bytes20 (signature));
147+ uint256 balance = bonds [maker][orderHash].balance;
137148 return (balance > 0 ) ? this .isValidSignature.selector : bytes4 (0 );
138149 }
139150
140151 function cancelOrder (bytes32 orderHash ) external {
141- uint256 balance = deposits[msg .sender ][orderHash].balance;
142- deposits[msg .sender ][orderHash].balance = 0 ;
143- _WETH.safeWithdrawTo (balance, msg .sender );
152+ return _cancelOrder (msg .sender , orderHash, address (0 ), 0 );
144153 }
145154
146155 function cancelExpiredOrderByResolver (address maker , IOrderMixin.Order calldata order ) external onlyResolver {
147156 uint256 orderExpirationTime = order.makerTraits.getExpirationTime ();
148- if (orderExpirationTime > 0 && block .timestamp > orderExpirationTime) revert OrderNotExpiredYet (block .timestamp , order.makerTraits.getExpirationTime ());
157+ if (orderExpirationTime > 0 && block .timestamp > orderExpirationTime) revert OrderShouldBeExpired (block .timestamp , order.makerTraits.getExpirationTime ());
158+
159+ // Using EIP712Alien._domainSeparatorV4() is cheaper than call IOrderMixin(_LIMIT_ORDER_PROTOCOL).hashOrder(order);
160+ bytes32 orderHash = order.hash (_domainSeparatorV4 ());
161+ Bond storage bond = bonds[maker][orderHash];
162+ uint256 resolverReward = getResolverReward (bond.auctionMaxGasBump, orderExpirationTime, bond.auctionDuration);
163+ _cancelOrder (maker, orderHash, msg .sender , resolverReward);
164+ }
149165
150- bytes32 orderHash = order.hash (_domainSeparatorV4 ()); // IOrderMixin(_LIMIT_ORDER_PROTOCOL).hashOrder(order);
151- Deposit storage deposit = deposits[maker][orderHash];
152- uint256 balance = deposit.balance;
153- deposit.balance = 0 ;
166+ function _cancelOrder (address maker , bytes32 orderHash , address resolver , uint256 resolverReward ) internal {
167+ uint256 balance = bonds[maker][orderHash].balance;
168+ if (balance == 0 ) revert CanNotCancelForZeroBalance ();
169+ _burn (maker, balance);
170+ bonds[maker][orderHash].balance = 0 ;
154171
155- uint256 resolverReward = getResolverReward (deposit.maxGasCostsBump, orderExpirationTime, deposit.auctionDuration);
156172 if (resolverReward > 0 ) {
157173 balance -= resolverReward;
158- _WETH.safeWithdrawTo (resolverReward, msg . sender );
174+ _WETH.safeWithdrawTo (resolverReward, resolver );
159175 }
160176 if (balance > 0 ) {
161177 _WETH.safeWithdrawTo (balance, maker);
162178 }
179+ emit ETHCancelled (maker, orderHash, balance);
163180 }
164181
165182 function postInteraction (
166- IOrderMixin.Order calldata order ,
183+ IOrderMixin.Order calldata /* order */ ,
167184 bytes calldata /* extension */ ,
168185 bytes32 orderHash ,
169186 address /* taker */ ,
170187 uint256 makingAmount ,
171188 uint256 /* takingAmount */ ,
172189 uint256 /* remainingMakingAmount */ ,
173190 bytes calldata extraData
174- ) external onlyLimitOrderProtocolOrOneOfTails (extraData) {
175- deposits[order.maker.get ()][orderHash].balance -= uint208 (makingAmount);
191+ ) external onlyLimitOrderProtocolOrOneOfTails (extraData[20 :]) {
192+ address maker = address (bytes20 (extraData[:20 ]));
193+ bonds[maker][orderHash].balance -= uint208 (makingAmount);
194+ }
195+
196+ function rescueFunds (address token , address to , uint256 amount ) external onlyResolver {
197+ if (token == address (0 )) {
198+ payable (to).transfer (amount);
199+ } else if (IWETH (token) == _WETH) {
200+ uint256 available = _WETH.balanceOf (address (this )) - totalSupply ();
201+ if (amount > available) revert RescueFundsTooMuch (amount, available);
202+ _WETH.safeWithdrawTo (amount, to);
203+ } else {
204+ IERC20 (token).safeTransfer (to, amount);
205+ }
176206 }
177207
178- function getResolverReward (uint256 maxGasCostsBump , uint256 orderExpirationTime , uint256 auctionDuration ) public view returns (uint256 ) {
179- uint256 gasCostsBump = getCurrentAuctionValue (maxGasCostsBump , orderExpirationTime, auctionDuration);
208+ function getResolverReward (uint256 auctionMaxGasBump , uint256 orderExpirationTime , uint256 auctionDuration ) public view returns (uint256 ) {
209+ uint256 gasCostsBump = getCurrentAuctionValue (auctionMaxGasBump , orderExpirationTime, auctionDuration);
180210 return _CANCEL_GAS_LOWER_BOUND * block .basefee * (_BUMP_BASE + gasCostsBump) / _BUMP_BASE;
181211 }
182212
0 commit comments