generated from PaulRBerg/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathSiloWrapper.sol
113 lines (91 loc) · 4.41 KB
/
SiloWrapper.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
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;
import { IFlashLoanRecipient } from "../balancer/interfaces/IFlashLoanRecipient.sol";
import { IFlashLoaner } from "../balancer/interfaces/IFlashLoaner.sol";
import { ISiloRepository } from "./interfaces/ISiloRepository.sol";
import { ISiloLens } from "./interfaces/ISiloLens.sol";
import { ISilo } from "./interfaces/ISilo.sol";
import { Arrays } from "../utils/Arrays.sol";
import { WAD } from "../utils/constants.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { BaseWrapper, IERC7399, IERC20 } from "../BaseWrapper.sol";
/// @dev Silo Flash Lender that uses Balancer Pools as source of X liquidity,
/// then deposits X on Silo to borrow whatever's necessary.
contract SiloWrapper is BaseWrapper, IFlashLoanRecipient {
using Arrays for uint256;
using Arrays for address;
using SafeERC20 for IERC20;
bool public constant COLLATERAL_ONLY = false;
// Silo sometimes rounds up the borrowed amount, so we need to account for that
uint256 public constant DUST_FEE = 1;
error NotBalancer();
error HashMismatch();
IFlashLoaner public immutable balancer;
ISiloRepository public immutable repository;
ISiloLens public immutable lens;
IERC20 public immutable intermediateToken;
bytes32 private flashLoanDataHash;
constructor(ISiloLens _lens, IFlashLoaner _balancer, IERC20 _intermediateToken) {
lens = _lens;
repository = _lens.siloRepository();
balancer = _balancer;
intermediateToken = _intermediateToken;
}
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) public view returns (uint256) {
// Optimistically assume that balancer has enough liquidity of the intermediate token
// Each Silo fork has a different oracle, so it'd be hard to get the exact amount that we can borrow
ISilo silo = repository.getSilo(IERC20(asset));
return address(silo) == address(0) ? 0 : lens.liquidity(silo, IERC20(asset));
}
/// @inheritdoc IERC7399
function flashFee(address asset, uint256 amount) external view returns (uint256) {
uint256 max = maxFlashLoan(asset);
require(max > 0, "Unsupported currency");
uint256 fee = Math.mulDiv(
amount, balancer.getProtocolFeesCollector().getFlashLoanFeePercentage(), WAD, Math.Rounding.Ceil
);
// If Balancer ever charges a fee, we can't repay it with the flash loan, so this wrapper becomes useless
return amount >= max || fee > 0 ? type(uint256).max : DUST_FEE;
}
function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
bytes memory metadata = abi.encode(asset, amount, data);
flashLoanDataHash = keccak256(metadata);
uint256 max = intermediateToken.balanceOf(address(balancer));
balancer.flashLoan(this, address(intermediateToken).toArray(), max.toArray(), metadata);
}
/// @inheritdoc IFlashLoanRecipient
function receiveFlashLoan(
address[] memory,
uint256[] memory amounts,
uint256[] memory,
bytes memory params
)
external
override
{
if (msg.sender != address(balancer)) revert NotBalancer();
if (keccak256(params) != flashLoanDataHash) revert HashMismatch();
delete flashLoanDataHash;
(IERC20 asset, uint256 amount, bytes memory data) = abi.decode(params, (IERC20, uint256, bytes));
uint256 intermediateAmount = amounts[0];
ISilo silo = _silo(asset);
silo.deposit(intermediateToken, intermediateAmount, COLLATERAL_ONLY);
silo.borrow(asset, amount);
_bridgeToCallback(address(asset), amount, DUST_FEE, data);
silo.repay(asset, type(uint256).max);
silo.withdraw(intermediateToken, type(uint256).max, COLLATERAL_ONLY);
intermediateToken.safeTransfer(address(balancer), intermediateAmount);
}
function _silo(IERC20 asset) internal returns (ISilo silo) {
silo = repository.getSilo(asset);
if (asset.allowance(address(this), address(silo)) == 0) {
asset.forceApprove(address(silo), type(uint256).max);
}
if (intermediateToken.allowance(address(this), address(silo)) == 0) {
intermediateToken.forceApprove(address(silo), type(uint256).max);
}
}
}