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 |
129×
129×
129×
2×
2×
2×
1×
18×
17×
10×
7×
7×
12×
11×
10×
10×
10×
7×
17×
17×
15×
15×
13×
13×
13×
13×
10×
10×
12×
7×
7×
9×
| // SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "../modules/Exec.sol";
import "../modules/Markets.sol";
import "../modules/DToken.sol";
import "../Interfaces.sol";
import "../Utils.sol";
contract FlashLoan is IERC3156FlashLender, IDeferredLiquidityCheck {
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
address immutable eulerAddress;
Exec immutable exec;
Markets immutable markets;
bool internal _isDeferredLiquidityCheck;
constructor(address euler_, address exec_, address markets_) {
eulerAddress = euler_;
exec = Exec(exec_);
markets = Markets(markets_);
}
function maxFlashLoan(address token) override external view returns (uint) {
address eTokenAddress = markets.underlyingToEToken(token);
return eTokenAddress == address(0) ? 0 : IERC20(token).balanceOf(eulerAddress);
}
function flashFee(address token, uint) override external view returns (uint) {
require(markets.underlyingToEToken(token) != address(0), "e/flash-loan/unsupported-token");
return 0;
}
function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data) override external returns (bool) {
require(markets.underlyingToEToken(token) != address(0), "e/flash-loan/unsupported-token");
if(!_isDeferredLiquidityCheck) {
exec.deferLiquidityCheck(address(this), abi.encode(receiver, token, amount, data, msg.sender));
_isDeferredLiquidityCheck = false;
} else {
_loan(receiver, token, amount, data, msg.sender);
}
return true;
}
function onDeferredLiquidityCheck(bytes memory encodedData) override external {
require(msg.sender == eulerAddress, "e/flash-loan/on-deferred-caller");
(IERC3156FlashBorrower receiver, address token, uint amount, bytes memory data, address msgSender) =
abi.decode(encodedData, (IERC3156FlashBorrower, address, uint, bytes, address));
_isDeferredLiquidityCheck = true;
_loan(receiver, token, amount, data, msgSender);
_exitAllMarkets();
}
function _loan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes memory data, address msgSender) internal {
DToken dToken = DToken(markets.underlyingToDToken(token));
dToken.borrow(0, amount);
Utils.safeTransfer(token, address(receiver), amount);
require(
receiver.onFlashLoan(msgSender, token, amount, 0, data) == CALLBACK_SUCCESS,
"e/flash-loan/callback"
);
Utils.safeTransferFrom(token, address(receiver), address(this), amount);
Erequire(IERC20(token).balanceOf(address(this)) >= amount, 'e/flash-loan/pull-amount');
uint allowance = IERC20(token).allowance(address(this), eulerAddress);
if(allowance < amount) {
(bool success,) = token.call(abi.encodeWithSelector(IERC20(token).approve.selector, eulerAddress, type(uint).max));
require(success, "e/flash-loan/approve");
}
dToken.repay(0, amount);
}
function _exitAllMarkets() internal {
address[] memory enteredMarkets = markets.getEnteredMarkets(address(this));
for (uint i = 0; i < enteredMarkets.length; ++i) {
markets.exitMarket(0, enteredMarkets[i]);
}
}
}
|