Skip to content

Commit 84bc515

Browse files
committed
Introduce ETHOrderClonable
1 parent 09b075e commit 84bc515

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity 0.8.23;
4+
5+
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
6+
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7+
import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
8+
import { OnlyWethReceiver } from "@1inch/solidity-utils/contracts/mixins/OnlyWethReceiver.sol";
9+
import { IWETH } from "@1inch/solidity-utils/contracts/interfaces/IWETH.sol";
10+
import { Address, AddressLib } from "@1inch/solidity-utils/contracts/libraries/AddressLib.sol";
11+
12+
import { EIP712Alien } from "contracts/mocks/EIP712Alien.sol";
13+
import { IPostInteraction } from "../interfaces/IPostInteraction.sol";
14+
import { OrderLib, IOrderMixin } from "../OrderLib.sol";
15+
import { MakerTraits, MakerTraitsLib } from "../libraries/MakerTraitsLib.sol";
16+
import { ExtensionLib } from "../libraries/ExtensionLib.sol";
17+
18+
/// @title Extension that will allow to create Fusion+ order that sell ETH. ETH will be deposited into the clone.
19+
contract ETHOrderClonable is OnlyWethReceiver, EIP712Alien {
20+
using Clones for address;
21+
using AddressLib for Address;
22+
using SafeERC20 for IWETH;
23+
using SafeERC20 for IERC20;
24+
using OrderLib for IOrderMixin.Order;
25+
using MakerTraitsLib for MakerTraits;
26+
27+
error AccessDenied(address caller, address expected);
28+
error IsValidSignatureFactoryDerivationMismatch(address expected, address actual);
29+
error OrderMakerShouldBeThisContract(address maker, address thisContract);
30+
error OrderReceiverShouldNotBeThis(address receiver, address self);
31+
error WethBalanceShouldNotBeAboveMakingAmount(uint256 makingAmount, uint256 wethBalance);
32+
error OrderMakingAmountShouldBeEqualToWethBalance(uint256 makingAmount, uint256 wethBalance);
33+
error CanNotCancelForZeroBalance();
34+
error ResolverAccessTokenMissing(address resolver, address accessToken);
35+
error ExpectedCloneAddressMismatch(address actual, address expected);
36+
error OnlyFactoryViolation(address caller, address factory);
37+
error OnlyCloneViolation(address caller, address clone);
38+
error OrderShouldBeExpired(uint256 currentTime, uint256 orderExpirationTime);
39+
40+
event EscrowDeployed(address maker, bytes32 orderHash, address clone, uint256 value);
41+
event EscrowCancelled(address maker, bytes32 orderHash, uint256 value);
42+
43+
address private immutable _FACTORY = address(this);
44+
address private immutable _LIMIT_ORDER_PROTOCOL;
45+
IWETH private immutable _WETH;
46+
IERC20 private immutable _ACCESS_TOKEN;
47+
48+
modifier onlySender(address expected) {
49+
if (msg.sender != expected) revert AccessDenied(msg.sender, expected);
50+
_;
51+
}
52+
53+
modifier onlyResolver {
54+
if (_ACCESS_TOKEN.balanceOf(msg.sender) == 0) revert ResolverAccessTokenMissing(msg.sender, address(_ACCESS_TOKEN));
55+
_;
56+
}
57+
58+
modifier onlyFactory {
59+
if (address(this) != _FACTORY) revert OnlyFactoryViolation(msg.sender, _FACTORY);
60+
_;
61+
}
62+
63+
modifier onlyClone {
64+
if (address(this) == _FACTORY) revert OnlyCloneViolation(msg.sender, address(this));
65+
_;
66+
}
67+
68+
constructor(
69+
IWETH weth,
70+
address limitOrderProtocol,
71+
IERC20 accessToken
72+
)
73+
OnlyWethReceiver(address(weth))
74+
EIP712Alien(_LIMIT_ORDER_PROTOCOL, "1inch Limit Order Protocol", "4")
75+
{
76+
_WETH = weth;
77+
_LIMIT_ORDER_PROTOCOL = limitOrderProtocol;
78+
_ACCESS_TOKEN = accessToken;
79+
_WETH.forceApprove(limitOrderProtocol, type(uint256).max);
80+
}
81+
82+
function validateOrder(
83+
address maker,
84+
IOrderMixin.Order calldata order,
85+
address expectedClone,
86+
bool isPartiallyFilled
87+
) external view onlyFactory returns(bool) {
88+
// Validate main order parameters
89+
if (order.maker.get() != expectedClone) revert OrderMakerShouldBeThisContract(order.maker.get(), address(this));
90+
if (order.getReceiver() == address(this) || order.getReceiver() == expectedClone) revert OrderReceiverShouldNotBeThis(order.getReceiver(), address(this));
91+
uint256 balance = _WETH.safeBalanceOf(address(this));
92+
if (isPartiallyFilled) {
93+
if (balance <= order.makingAmount) revert WethBalanceShouldNotBeAboveMakingAmount(order.makingAmount, balance);
94+
} else {
95+
if (balance != order.makingAmount) revert OrderMakingAmountShouldBeEqualToWethBalance(order.makingAmount, balance);
96+
}
97+
98+
// EIP712Alien._domainSeparatorV4() is cheaper than call IOrderMixin(_LIMIT_ORDER_PROTOCOL).hashOrder(order);
99+
bytes32 orderHash = order.hash(_domainSeparatorV4());
100+
address clone = addressOf(maker, orderHash);
101+
if (clone != expectedClone) revert ExpectedCloneAddressMismatch(clone, expectedClone);
102+
return true;
103+
}
104+
105+
function addressOf(address maker, bytes32 orderHash) public view returns (address) {
106+
bytes32 salt = keccak256(abi.encode(maker, orderHash));
107+
return _FACTORY.predictDeterministicAddress(salt, _FACTORY);
108+
}
109+
110+
function deposit(bytes32 orderHash) external payable onlyFactory returns (address clone) {
111+
clone = addressOf(msg.sender, orderHash);
112+
_WETH.safeDeposit(msg.value);
113+
_WETH.safeTransfer(clone, msg.value);
114+
emit EscrowDeployed(msg.sender, orderHash, clone, msg.value);
115+
}
116+
117+
function isValidSignature(bytes32 orderHash, bytes calldata signature) external view onlyClone returns(bytes4) {
118+
address maker = address(bytes20(signature[:20]));
119+
address clone = addressOf(maker, orderHash);
120+
if (clone != address(this)) revert IsValidSignatureFactoryDerivationMismatch(clone, address(this));
121+
return this.isValidSignature.selector;
122+
}
123+
124+
function cancelOrder(bytes32 orderHash) external onlyClone {
125+
bytes32 salt = keccak256(abi.encode(msg.sender, orderHash));
126+
address clone = _FACTORY.predictDeterministicAddress(salt, _FACTORY);
127+
if (clone != address(this)) revert IsValidSignatureFactoryDerivationMismatch(clone, address(this));
128+
129+
uint256 balance = _WETH.safeBalanceOf(address(this));
130+
if (balance == 0) revert CanNotCancelForZeroBalance();
131+
_WETH.safeWithdrawTo(balance, msg.sender);
132+
emit EscrowCancelled(msg.sender, orderHash, balance);
133+
}
134+
135+
function cancelExpiredOrderByResolver(address maker, IOrderMixin.Order calldata order) external onlyClone onlyResolver {
136+
uint256 orderExpirationTime = order.makerTraits.getExpirationTime();
137+
if (orderExpirationTime > 0 && block.timestamp > orderExpirationTime) revert OrderShouldBeExpired(block.timestamp, order.makerTraits.getExpirationTime());
138+
139+
// Using EIP712Alien._domainSeparatorV4() is cheaper than call IOrderMixin(_LIMIT_ORDER_PROTOCOL).hashOrder(order);
140+
bytes32 orderHash = order.hash(_domainSeparatorV4());
141+
_cancelOrder(maker, orderHash);
142+
}
143+
144+
function _cancelOrder(address maker, bytes32 orderHash) private {
145+
address clone = addressOf(maker, orderHash);
146+
if (clone != address(this)) revert IsValidSignatureFactoryDerivationMismatch(clone, address(this));
147+
148+
uint256 balance = _WETH.safeBalanceOf(address(this));
149+
if (balance == 0) revert CanNotCancelForZeroBalance();
150+
_WETH.safeWithdrawTo(balance, msg.sender);
151+
emit EscrowCancelled(msg.sender, orderHash, balance);
152+
}
153+
}

0 commit comments

Comments
 (0)