forked from perspectivefi/core-v2-hats
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Router.sol
291 lines (254 loc) · 9.91 KB
/
Router.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import {Math} from "openzeppelin-math/Math.sol";
import {IERC3156FlashBorrower} from "openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol";
import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessManagedUpgradeable} from "openzeppelin-contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {RayMath} from "../libraries/RayMath.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {Dispatcher} from "./Dispatcher.sol";
/**
* @title Router contract
* @author Spectra Finance
* @notice Handles executions of complex sequences of actions in the Spectra protocol.
*/
contract Router is
Dispatcher,
AccessManagedUpgradeable,
PausableUpgradeable,
IRouter,
IERC3156FlashBorrower
{
using SafeERC20 for IERC20;
using Math for uint256;
/** @dev Maximum amount of tokens for which balance can be tracked in _previewRate(). */
uint256 private constant MAX_INVOLVED_TOKENS = 30;
/** @dev Expected return value from borrowers onFlashLoan function. */
bytes32 private immutable ON_FLASH_LOAN = keccak256("ERC3156FlashBorrower.onFlashLoan");
/* Events
*********************************************************************************************************/
event RouterUtilChange(address indexed previousRouterUtil, address indexed newRouterUtil);
event KyberRouterChange(address indexed previousKyberRouter, address indexed newKyberRouter);
event PendleRouterChange(address indexed previousPendleRouter, address indexed newPendleRouter);
/* Modifiers
*********************************************************************************************************/
modifier checkDeadline(uint256 deadline) {
if (block.timestamp > deadline) revert TransactionDeadlinePassed();
_;
}
/* Constructor
*********************************************************************************************************/
constructor(address _registry) Dispatcher(_registry) {
_disableInitializers(); // using this so that the deployed logic contract later cannot be initialized.
}
/* Initializer
*********************************************************************************************************/
function initialize(
address _routerUtil,
address _kyberRouter,
address _pendleRouter,
address _initialAuthority
) external initializer {
__Dispatcher_init(_routerUtil, _kyberRouter, _pendleRouter);
__AccessManaged_init(_initialAuthority);
}
/* Setters
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function pause() external override restricted {
_pause();
}
/**
* @inheritdoc IRouter
*/
function unPause() external override restricted {
_unpause();
}
/**
* @inheritdoc IRouter
*/
function setRouterUtil(address _routerUtil) external override restricted {
if (_routerUtil == address(0)) {
revert AddressError();
}
emit RouterUtilChange(routerUtil, _routerUtil);
routerUtil = _routerUtil;
}
/**
* @inheritdoc IRouter
*/
function setKyberRouter(address _kyberRouter) external override restricted {
emit KyberRouterChange(kyberRouter, _kyberRouter);
kyberRouter = _kyberRouter;
}
/**
* @inheritdoc IRouter
*/
function setPendleRouter(address _pendleRouter) external override restricted {
emit PendleRouterChange(pendleRouter, _pendleRouter);
pendleRouter = _pendleRouter;
}
/* Getters
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function getRegistry() external view override returns (address) {
return registry;
}
/**
* @inheritdoc IRouter
*/
function getRouterUtil() external view override returns (address) {
return routerUtil;
}
/**
* @inheritdoc IRouter
*/
function getKyberRouter() external view override returns (address) {
return kyberRouter;
}
/**
* @inheritdoc IRouter
*/
function getPendleRouter() external view override returns (address) {
return pendleRouter;
}
/* Executions
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function execute(
bytes calldata _commands,
bytes[] calldata _inputs,
uint256 _deadline
) external payable override checkDeadline(_deadline) {
execute(_commands, _inputs);
}
/**
* @inheritdoc IRouter
*/
function execute(
bytes calldata _commands,
bytes[] calldata _inputs
) public payable override whenNotPaused {
uint256 numCommands = _commands.length;
if (_inputs.length != numCommands) {
revert LengthMismatch();
}
// Relying on msg.sender is problematic as it changes during a flash loan.
// Thus, it's necessary to track who initiated the original Router execution.
bool topLevel;
if (msgSender == address(0)) {
msgSender = msg.sender;
topLevel = true;
} else if (msg.sender != address(this)) {
revert UnauthorizedReentrantCall();
}
// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex; commandIndex < numCommands; ) {
bytes1 command = _commands[commandIndex];
bytes calldata input = _inputs[commandIndex];
_dispatch(command, input);
unchecked {
commandIndex++;
}
}
if (topLevel) {
// top-level reset
msgSender = address(0);
}
}
/* Previews
*********************************************************************************************************/
/**
* @dev Simulates the execution of a sequence of commands and returns the expected resulting rate
* @param _commands Encoded instructions passed to the dispatcher
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
* @param _spot If set to true, spot exchange rate is used for swaps. Additionally for all commands,
* input amounts are disregarded, and one unit of the token of interest is used instead.
* If set to false, the function includes price impact and curve pool fees for swaps.
* @return The preview rate value, which represents the amount of output token obtained at the end of execution
* for each wei of input token spent at the start of execution, multiplied by 1 ray unit.
*/
function _previewRate(
bytes calldata _commands,
bytes[] calldata _inputs,
bool _spot
) internal view whenNotPaused returns (uint256) {
uint256 numCommands = _commands.length;
if (_inputs.length != numCommands) {
revert LengthMismatch();
}
TokenBalance[] memory balances = new TokenBalance[](MAX_INVOLVED_TOKENS);
uint256 rate = RayMath.RAY_UNIT;
// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex; commandIndex < numCommands; ) {
bytes1 command = _commands[commandIndex];
bytes calldata input = _inputs[commandIndex];
uint256 commandRate = _dispatchPreviewRate(command, input, _spot, balances);
if (commandRate != RayMath.RAY_UNIT) {
rate = rate.mulDiv(commandRate, RayMath.RAY_UNIT);
}
unchecked {
commandIndex++;
}
}
return rate;
}
/**
* @inheritdoc IRouter
*/
function previewRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view override returns (uint256) {
return _previewRate(_commands, _inputs, false);
}
/**
* @inheritdoc IRouter
*/
function previewSpotRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view override returns (uint256) {
return _previewRate(_commands, _inputs, true);
}
/* Flashloans
*********************************************************************************************************/
/**
* @inheritdoc IERC3156FlashBorrower
*/
function onFlashLoan(
address /* initiator */,
address _token,
uint256 _amount,
uint256 _fee,
bytes calldata _data
) external returns (bytes32) {
if (msgSender == address(0)) {
revert DirectOnFlashloanCall();
}
if (msg.sender != flashloanLender) {
revert UnauthorizedOnFlashloanCaller();
}
(bytes memory commands, bytes[] memory inputs) = abi.decode(_data, (bytes, bytes[]));
this.execute(commands, inputs); // https://ethereum.stackexchange.com/questions/103437/converting-bytes-memory-to-bytes-calldata
uint256 repayAmount = _amount + _fee;
uint256 allowance = IERC20(_token).allowance(address(this), msg.sender);
if (allowance < repayAmount) {
// Approve the lender to pull the funds if needed
IERC20(_token).forceApprove(msg.sender, repayAmount);
}
uint256 balance = IERC20(_token).balanceOf(address(this));
if (balance < repayAmount) {
// Collect remaining debt from the original sender if needed
IERC20(_token).safeTransferFrom(msgSender, address(this), repayAmount - balance);
}
return ON_FLASH_LOAN;
}
}