generated from PaulRBerg/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 3
/
UniswapV3Wrapper.sol
143 lines (118 loc) · 5.65 KB
/
UniswapV3Wrapper.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
// SPDX-License-Identifier: MIT
// Thanks to sunnyRK, yashnaman & ultrasecr.eth
pragma solidity ^0.8.19;
import { Registry } from "src/Registry.sol";
import { IUniswapV3FlashCallback } from "./interfaces/callback/IUniswapV3FlashCallback.sol";
import { IUniswapV3Pool } from "./interfaces/IUniswapV3Pool.sol";
import { PoolAddress } from "./interfaces/PoolAddress.sol";
import { BaseWrapper, IERC7399, IERC20 } from "../BaseWrapper.sol";
/// @dev Uniswap V3 Flash Lender that uses Uniswap V3 Pools as source of liquidity.
/// Uniswap V3 allows pushing repayments, so we override `_repayTo`.
contract UniswapV3Wrapper is BaseWrapper, IUniswapV3FlashCallback {
using PoolAddress for address;
using { canLoan, balance } for IUniswapV3Pool;
error UnknownPool();
error UnsupportedCurrency(address asset);
// CONSTANTS
address public immutable factory;
// DEFAULT ASSETS
address public immutable weth;
address public immutable usdc;
address public immutable usdt;
/// @param reg Registry storing constructor parameters
constructor(string memory name, Registry reg) {
// @param factory_ Uniswap v3 UniswapV3Factory address
// @param weth_ Weth contract used in Uniswap v3 Pairs
// @param usdc_ usdc contract used in Uniswap v3 Pairs
// @param usdt_ usdt contract used in Uniswap v3 Pairs
(factory, weth, usdc, usdt) = abi.decode(reg.getSafe(name), (address, address, address, address));
}
/**
* @dev Get the Uniswap Pool that will be used as the source of a loan. The opposite asset will be Weth, except for
* Weth that will be usdc.
* @param asset The loan currency.
* @param amount The amount of assets to borrow.
* @return pool The Uniswap V3 Pool that will be used as the source of the flash loan.
*/
function cheapestPool(address asset, uint256 amount) public view returns (IUniswapV3Pool pool) {
// Try a stable pair first
pool = _pool(asset, asset == usdc ? usdt : usdc, 0.0001e6);
if (address(pool) != address(0) && pool.canLoan(asset, amount)) return pool;
// Look for the cheapest fee otherwise
uint16[3] memory fees = [0.0005e6, 0.003e6, 0.01e6];
address assetOther = asset == weth ? usdc : weth;
for (uint256 i = 0; i < 3; i++) {
pool = _pool(asset, assetOther, fees[i]);
if (address(pool) != address(0) && pool.canLoan(asset, amount)) return pool;
}
pool = IUniswapV3Pool(address(0));
}
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) external view returns (uint256) {
return _maxFlashLoan(asset);
}
/// @inheritdoc IERC7399
function flashFee(address asset, uint256 amount) external view returns (uint256) {
uint256 max = _maxFlashLoan(asset);
require(max > 0, "Unsupported currency");
return amount >= max ? type(uint256).max : _flashFee(asset, amount);
}
/// @inheritdoc IUniswapV3FlashCallback
function uniswapV3FlashCallback(
uint256 fee0, // Fee on Asset0
uint256 fee1, // Fee on Asset1
bytes calldata params
)
external
override
{
(address asset0, address asset1, uint24 feeTier, uint256 amount, bytes memory data) =
abi.decode(params, (address, address, uint24, uint256, bytes));
if (msg.sender != address(_pool(asset0, asset1, feeTier))) revert UnknownPool();
(address asset, uint256 fee) = fee0 > 0 ? (asset0, fee0) : (asset1, fee1);
_bridgeToCallback(asset, amount, fee, data);
}
function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
IUniswapV3Pool pool = cheapestPool(asset, amount);
if (address(pool) == address(0)) revert UnsupportedCurrency(asset);
address asset0 = address(pool.token0());
address asset1 = address(pool.token1());
uint256 amount0 = asset == asset0 ? amount : 0;
uint256 amount1 = asset == asset1 ? amount : 0;
pool.flash(address(this), amount0, amount1, abi.encode(asset0, asset1, pool.fee(), amount, data));
}
function _repayTo() internal view override returns (address) {
return msg.sender;
}
function _pool(address asset, address other, uint24 fee) internal view returns (IUniswapV3Pool pool) {
PoolAddress.PoolKey memory poolKey = PoolAddress.getPoolKey(address(asset), address(other), fee);
pool = IUniswapV3Pool(factory.computeAddress(poolKey));
}
function _maxFlashLoan(address asset) internal view returns (uint256 max) {
// Try a stable pair first
IUniswapV3Pool pool = _pool(asset, asset == usdc ? usdt : usdc, 0.0001e6);
if (address(pool) != address(0)) {
max = pool.balance(asset);
}
uint16[3] memory fees = [0.0005e6, 0.003e6, 0.01e6];
address assetOther = asset == weth ? usdc : weth;
for (uint256 i = 0; i < 3; i++) {
pool = _pool(asset, assetOther, fees[i]);
uint256 _balance = pool.balance(asset);
if (address(pool) != address(0) && _balance > max) {
max = _balance;
}
}
}
function _flashFee(address asset, uint256 amount) internal view returns (uint256) {
IUniswapV3Pool pool = cheapestPool(asset, amount);
require(address(pool) != address(0), "Unsupported currency");
return amount * uint256(pool.fee()) / 1e6;
}
}
function canLoan(IUniswapV3Pool pool, address asset, uint256 amount) view returns (bool) {
return balance(pool, asset) >= amount;
}
function balance(IUniswapV3Pool pool, address asset) view returns (uint256) {
return IERC20(asset).balanceOf(address(pool));
}