Skip to content

Commit 09b075e

Browse files
committed
Refactor ETHOrders
1 parent c100474 commit 09b075e

File tree

2 files changed

+119
-89
lines changed

2 files changed

+119
-89
lines changed

contracts/extensions/ETHOrders.sol

Lines changed: 91 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,52 @@
22

33
pragma 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

911
import { 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

Comments
 (0)