Skip to content

Commit

Permalink
Merge pull request #21 from makerdao/steCRV
Browse files Browse the repository at this point in the history
SteCRV support
  • Loading branch information
talbaneth authored Mar 14, 2022
2 parents d45aff2 + 016cba1 commit 090e029
Show file tree
Hide file tree
Showing 6 changed files with 508 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
[submodule "lib/proxy-manager-clipper"]
path = lib/proxy-manager-clipper
url = https://github.com/makerdao/proxy-manager-clipper
[submodule "lib/dss-crop-join"]
path = lib/dss-crop-join
url = https://github.com/makerdao/dss-crop-join
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ flatten :;
hevm flatten --source-file "src/UniswapV2LpTokenCallee.sol" > out/UniswapV2LpTokenCalleeDai.sol
hevm flatten --source-file "src/UniswapV3Callee.sol" > out/UniswapV3Callee.sol
hevm flatten --source-file "src/WstETHCurveUniv3Callee.sol" > out/WstETHCurveUniv3Callee.sol
hevm flatten --source-file "src/CurveLpTokenUniv3Callee.sol" > out/CurveLpTokenUniv3Callee.sol
1 change: 1 addition & 0 deletions lib/dss-crop-join
Submodule dss-crop-join added at 546fcb
6 changes: 6 additions & 0 deletions scripts/deploy-mainnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ WstETHCurveUniv3Callee=$(dapp create WstETHCurveUniv3Callee \
0x9759A6Ac90977b93B58547b4A71c78317f391A28 \
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)

CurveLpTokenUniv3Callee=$(dapp create CurveLpTokenUniv3Callee \
0xE592427A0AEce92De3Edee1F18E0157C05861564 \
0x9759A6Ac90977b93B58547b4A71c78317f391A28 \
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)

echo "UniswapV2CalleeDai: ${UniswapV2CalleeDai}"
echo "UniswapV2LpTokenCalleeDai: ${UniswapV2LpTokenCalleeDai}"
echo "UniswapV3Callee: ${UniswapV3Callee}"
echo "WstETHCurveUniv3Callee: ${WstETHCurveUniv3Callee}"
echo "CurveLpTokenUniv3Callee: ${CurveLpTokenUniv3Callee}"
180 changes: 180 additions & 0 deletions src/CurveLpTokenUniv3Callee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2022 Dai Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

interface GemJoinLike {
function dec() external view returns (uint256);
function gem() external view returns (address);
function exit(address, uint256) external;
}

interface DaiJoinLike {
function dai() external view returns (TokenLike);
function join(address, uint256) external;
}

interface TokenLike {
function approve(address, uint256) external;
function transfer(address, uint256) external;
function balanceOf(address) external view returns (uint256);
function symbol() external view returns (string memory);
}

interface ManagerLike {
function exit(address crop, address usr, uint256 val) external;
}

interface CurvePoolLike {
function remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 _min_amount)
external returns (uint256);
function coins(uint256) external view returns (address);
}

interface WethLike is TokenLike {
function deposit() external payable;
}

interface UniV3RouterLike {

struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}

function exactInput(UniV3RouterLike.ExactInputParams calldata params)
external payable returns (uint256 amountOut);
}

contract CurveLpTokenUniv3Callee {
UniV3RouterLike public immutable uniV3Router;
DaiJoinLike public immutable daiJoin;
TokenLike public immutable dai;
address public immutable weth;

uint256 public constant RAY = 10 ** 27;
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

function _add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function _sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function _divup(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(x, _sub(y, 1)) / y;
}

struct CurveData {
address pool;
uint256 coinIndex;
}

constructor(
address uniV3Router_,
address daiJoin_,
address weth_
) public {
uniV3Router = UniV3RouterLike(uniV3Router_);
daiJoin = DaiJoinLike(daiJoin_);
TokenLike dai_ = DaiJoinLike(daiJoin_).dai();
dai = dai_;
weth = weth_;

dai_.approve(daiJoin_, type(uint256).max);
}

receive() external payable {}

function _fromWad(address gemJoin, uint256 wad) internal view returns (uint256 amt) {
amt = wad / 10 ** (_sub(18, GemJoinLike(gemJoin).dec()));
}

function clipperCall(
address sender, // Clipper caller, pays back the loan
uint256 owe, // Dai amount to pay back [rad]
uint256 slice, // Gem amount received [wad]
bytes calldata data // Extra data, see below
) external {
(
address to, // address to send remaining DAI to
address gemJoin, // gemJoin adapter address
uint256 minProfit, // minimum profit in DAI to make [wad]
bytes memory path, // uniswap v3 path
address manager, // pass address(0) if no manager
CurveData memory curveData // curve pool data
) = abi.decode(data, (address, address, uint256, bytes, address, CurveData));

address gem = GemJoinLike(gemJoin).gem();

// Convert slice to token precision
slice = _fromWad(gemJoin, slice);

// Exit gem to token
if(manager != address(0)) {
ManagerLike(manager).exit(gemJoin, address(this), slice);
} else {
GemJoinLike(gemJoin).exit(address(this), slice);
}

// curveData used explicitly to avoid stack too deep
TokenLike(gem).approve(curveData.pool, slice);
slice = CurvePoolLike(curveData.pool).remove_liquidity_one_coin({
_token_amount: slice,
i: int128(curveData.coinIndex),
_min_amount: 0 // minProfit is checked below
});

gem = CurvePoolLike(curveData.pool).coins(curveData.coinIndex);
if (gem == ETH) {
gem = weth;
WethLike(gem).deposit{
value: slice
}();
}

// Approve uniV3 to take gem
TokenLike(gem).approve(address(uniV3Router), slice);

// Calculate amount of DAI to Join (as erc20 WAD value)
uint256 daiToJoin = _divup(owe, RAY);

UniV3RouterLike.ExactInputParams memory params = UniV3RouterLike.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: slice,
amountOutMinimum: _add(daiToJoin, minProfit)
});
uniV3Router.exactInput(params);

// Although Uniswap will accept all gems, this check is a sanity check, just in case
// Transfer any lingering gem to specified address
if (TokenLike(gem).balanceOf(address(this)) > 0) {
TokenLike(gem).transfer(to, TokenLike(gem).balanceOf(address(this)));
}

// Convert DAI bought to internal vat value of the msg.sender of Clipper.take
daiJoin.join(sender, daiToJoin);

// Transfer remaining DAI to specified address
dai.transfer(to, dai.balanceOf(address(this)));
}
}
Loading

0 comments on commit 090e029

Please sign in to comment.