-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathCowSwapDex.sol
186 lines (165 loc) · 7.36 KB
/
CowSwapDex.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
// External
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Libs
import { ImmutableModule } from "../../shared/ImmutableModule.sol";
import { ICowSettlement } from "../../peripheral/Cowswap/ICowSettlement.sol";
import { DexSwapData, IDexAsyncSwap } from "../../interfaces/IDexSwap.sol";
/**
* @title CowSwapDex allows to swap tokens via CowSwap.
* @author mStable
* @notice
* @dev VERSION: 1.0
* DATE: 2022-06-17
*/
contract CowSwapDex is ImmutableModule, IDexAsyncSwap {
using SafeERC20 for IERC20;
/// @notice Contract GPv2VaultRelayer to give allowance to perform swaps
address public immutable RELAYER;
/// @notice GPv2Settlement contract
ICowSettlement public immutable SETTLEMENT;
/// @notice Event emitted when a order is cancelled.
event SwapCancelled(bytes indexed orderUid);
/**
* @param _nexus Address of the Nexus contract that resolves protocol modules and roles.
* @param _relayer Address of the GPv2VaultRelayer contract to set allowance to perform swaps
* @param _settlement Address of the GPv2Settlement contract that pre-signs orders.
*/
constructor(
address _nexus,
address _relayer,
address _settlement
) ImmutableModule(_nexus) {
RELAYER = _relayer;
SETTLEMENT = ICowSettlement(_settlement);
}
/**
* @dev Modifier to allow function calls only from the Liquidator or the Keeper EOA.
*/
modifier onlyKeeperOrLiquidator() {
_keeperOrLiquidator();
_;
}
function _keeperOrLiquidator() internal view {
require(
msg.sender == _keeper() || msg.sender == _liquidatorV2(),
"Only keeper or liquidator"
);
}
/***************************************
Core
****************************************/
/**
* @notice Initialises a cow swap order by approving the order on-chain.
* @dev This function is used in order to be compliant with IDexSwap interface.
* @param swapData The data of the swap {fromAsset, toAsset, fromAssetAmount, fromAssetFeeAmount, data}.
*/
function _initiateSwap(DexSwapData memory swapData) internal {
// unpack the CowSwap specific params from the generic swap.data field
(bytes memory orderUid, bool transfer) = abi.decode(swapData.data, (bytes, bool));
if (transfer) {
// transfer in the fromAsset
require(
IERC20(swapData.fromAsset).balanceOf(msg.sender) >= swapData.fromAssetAmount,
"not enough from assets"
);
// Transfer rewards from the liquidator
IERC20(swapData.fromAsset).safeTransferFrom(
msg.sender,
address(this),
swapData.fromAssetAmount
);
}
// sign the order on-chain so the order will happen
SETTLEMENT.setPreSignature(orderUid, true);
}
/**
* @notice Initialises a cow swap order.
* @dev Orders must be created off-chain.
* In case that an order fails, a new order uid is created there is no need to transfer "fromAsset".
* @param swapData The data of the swap {fromAsset, toAsset, fromAssetAmount, fromAssetFeeAmount, data}.
* The `data` field is ABI encoded data specific for CowSwap and includes:
* orderUid - The CowSwap unique identifier of the order. eg 0x6ac90cbb70839ca7829daef9f3d46f30ba4b043682e22a91604bbf9ab14940658e9a9a122f402cd98727128baf3dccaf05189b67634fc052
* transfer - A flag to indicate if reward tokens should be transferred from the sender to this CowSwapDex contract.
* If a CowSwap order expired, then a new order can be initiated with `transfer` set to `false` as the reward tokens
* that are to be sold are already in this CowSwapDex contract.
*/
function initiateSwap(DexSwapData calldata swapData) external override onlyKeeperOrLiquidator {
_initiateSwap(swapData);
}
/**
* @notice Initiate cow swap orders in bulk.
* @dev Orders must be created off-chain.
* @param swapsData Array of swap data {fromAsset, toAsset, fromAssetAmount, fromAssetFeeAmount, data}.
*/
function initiateSwaps(DexSwapData[] calldata swapsData) external onlyKeeperOrLiquidator {
uint256 len = swapsData.length;
for (uint256 i; i < len; ) {
_initiateSwap(swapsData[i]);
// Increment index with low gas consumption, no need to check for overflow.
unchecked {
i += 1;
}
}
}
/**
* @notice It reverts as cowswap allows to provide a "receiver" while creating an order.
* That is, CowSwap will transfer the purchased token back to the Liquidator.
* @dev The method is kept to have compatibility with IDexAsyncSwap.
*/
function settleSwap(DexSwapData memory) external pure {
revert("!not supported");
}
/**
* @notice Allows to cancel a cowswap order perhaps if it took too long or was with invalid parameters
* @dev This function performs no checks, there's a high change it will revert if you send it with fluff parameters
* Emits the `SwapCancelled` event with the `orderUid`.
* @param orderUid The order uid of the swap.
*/
function cancelSwap(bytes calldata orderUid) external override onlyKeeperOrLiquidator {
SETTLEMENT.setPreSignature(orderUid, false);
}
/**
* @notice Cancels cow swap orders in bulk.
* @dev It invokes the `cancelSwap` function for each order in the array.
* For each order uid it emits the `SwapCancelled` event with the `orderUid`.
* @param orderUids Array of swaps order uids
*/
function cancelSwaps(bytes[] calldata orderUids) external onlyKeeperOrLiquidator {
uint256 len = orderUids.length;
for (uint256 i; i < len; ) {
SETTLEMENT.setPreSignature(orderUids[i], false);
// Increment index with low gas consumption, no need to check for overflow.
unchecked {
i += 1;
}
}
}
/**
* @notice Approves a token to be sold using cow swap.
* @dev this approves the cow swap router to transfer the specified token from this contract.
* @param token Address of the token that is to be sold.
*/
function approveToken(address token) external onlyGovernor {
IERC20(token).safeApprove(RELAYER, type(uint256).max);
}
/**
* @notice Revokes cow swap from selling a token.
* @dev this removes the allowance for the cow swap router to transfer the specified token from this contract.
* @param token Address of the token that is to no longer be sold.
*/
function revokeToken(address token) external onlyGovernor {
IERC20(token).safeApprove(RELAYER, 0);
}
/**
* @notice Rescues tokens from the contract in case of a cancellation or failure and sends it to governor.
* @dev only governor can invoke.
* Even if a swap fails, the order can be created again and keep trying, rescueToken must be the last resource,
* ie, cowswap is not availabler for N hours.
*/
function rescueToken(address _erc20, uint256 amount) external onlyGovernor {
IERC20(_erc20).safeTransfer(_governor(), amount);
}
}