Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rETH #31

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open

rETH #31

Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
264eae7
Support wsteth complex path, fix other tests
talbaneth May 30, 2022
e8c403a
fix typo - pooXlFee => poolXFee
talbaneth Jun 1, 2022
bab3e02
chore: copy WstETH callee into rETH callee
julienmartinlevrai Jun 22, 2022
e614913
feat: support rETH operations
julienmartinlevrai Jun 22, 2022
3da7767
fix: remove outdated tests
julienmartinlevrai Jun 23, 2022
697079e
test: add test file for rETH callee
julienmartinlevrai Jun 24, 2022
ca512e9
test: replace WstETH for rETH in baseline
julienmartinlevrai Jun 24, 2022
cdbbfe4
fix: reduce profit for SteCRV
julienmartinlevrai Jun 30, 2022
2f594b2
test: use forge caching
julienmartinlevrai Jun 30, 2022
5a79a16
feat: add rETH callee to Makefile flatten
julienmartinlevrai Jun 30, 2022
bbfedd6
chore: add block age to test file
julienmartinlevrai Jun 30, 2022
d660ad3
test: deploy Clipper
julienmartinlevrai Jun 30, 2022
b6081ff
test: deploy rETH join adapter
julienmartinlevrai Jun 30, 2022
ba044ee
test: rely join in vat
julienmartinlevrai Jun 30, 2022
e5574ee
fix: rETH balanceOf storage slot
julienmartinlevrai Jun 30, 2022
94b83c7
test: init ilk in vat
julienmartinlevrai Jun 30, 2022
1c6c064
feat: init ilk in spotter and jug
julienmartinlevrai Jun 30, 2022
3cd9762
test: warp after ilk init in jug
julienmartinlevrai Jun 30, 2022
910903a
test: file dog.hole for new ilk
julienmartinlevrai Jul 1, 2022
e9a5776
test: file ilk’s chop in dog
julienmartinlevrai Jul 1, 2022
76d94f0
test: clipper permissions
julienmartinlevrai Jul 1, 2022
8b6ae22
test: add abacus for new ilk
julienmartinlevrai Jul 1, 2022
d7bbe7e
test: fix curve pool address
julienmartinlevrai Jul 1, 2022
4790bc8
feat: 2x Curve swap
julienmartinlevrai Jul 4, 2022
4854df4
test: rely rETH clipper in dog
julienmartinlevrai Jul 4, 2022
c0e1056
test: file clipper params
julienmartinlevrai Jul 7, 2022
3f30750
chore: use test-forge.sh
julienmartinlevrai Aug 4, 2022
157fe46
fix: pass curve pools as constructor params
julienmartinlevrai Aug 4, 2022
3728627
refactor: remove nbsp chars
julienmartinlevrai Aug 4, 2022
17c783b
refactor: make code more similar to WstETH
julienmartinlevrai Aug 4, 2022
bca2ab6
ttake 1K reth intead of 3K in est_bigAmtWithComplexPath
talbaneth Sep 22, 2022
d505189
Add reth deployment data
talbaneth Sep 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/out
/cache
block
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ flatten :;
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
hevm flatten --source-file "src/rETHCurveUniv3Callee.sol" > out/rETHCurveUniv3Callee.sol
6 changes: 2 additions & 4 deletions src/WstETHCurveUniv3Callee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ contract WstETHCurveUniv3Callee {
address to, // address to send remaining DAI to
address gemJoin, // gemJoin adapter address
uint256 minProfit, // minimum profit in DAI to make [wad]
uint24 poolFee, // uniswap V3 WETH-DAI pool fee
bytes memory path, // uniswap v3 path
address charterManager // pass address(0) if no manager
) = abi.decode(data, (address, address, uint256, uint24, address));
) = abi.decode(data, (address, address, uint256, bytes, address));

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

Expand Down Expand Up @@ -157,7 +157,6 @@ contract WstETHCurveUniv3Callee {
uint256 daiToJoin = _divup(owe, RAY);

// Do operation and get dai amount bought (checking the profit is achieved)
bytes memory path = abi.encodePacked(gem, poolFee, address(dai));
UniV3RouterLike.ExactInputParams memory params = UniV3RouterLike.ExactInputParams({
path: path,
recipient: address(this),
Expand All @@ -180,4 +179,3 @@ contract WstETHCurveUniv3Callee {
dai.transfer(to, dai.balanceOf(address(this)));
}
}

192 changes: 192 additions & 0 deletions src/rETHCurveUniv3Callee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// SPDX-FileCopyrightText: © 2022 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// 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 CharterManagerLike {
function exit(address crop, address usr, uint256 val) external;
}

interface WstEthLike is TokenLike {
function unwrap(uint256 _wstEthAmount) external returns (uint256);
function stETH() external view returns (address);
}

interface CurvePoolLike {
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
external returns (uint256 dy);
function coins(uint256 id) 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 rETHCurveUniv3Callee {

address public constant rocketToLido = 0x447Ddd4960d9fdBF6af9a790560d0AF76795CB08;
talbaneth marked this conversation as resolved.
Show resolved Hide resolved
address public constant lidoToETH = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;

UniV3RouterLike public immutable uniV3Router;
DaiJoinLike public immutable daiJoin;
TokenLike public immutable dai;
address public immutable weth;

uint256 public constant RAY = 10 ** 27;

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;
}

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 charterManager // pass address(0) if no manager
) = abi.decode(data, (address, address, uint256, bytes, address));

address gem = GemJoinLike(gemJoin).gem(); // RocketPool rETH

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

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

// rETH -> wstETH
TokenLike(gem).approve(rocketToLido, slice);
slice = CurvePoolLike(rocketToLido).exchange({
i: 0, // send token id 1 (RocketPool rETH)
j: 1, // receive token id 0 (wstETH)
dx: slice, // send `slice` amount of rETH
min_dy: 0 // accept any amount of ETH (`minProfit` is checked below)
});
gem = CurvePoolLike(rocketToLido).coins(1);

// wstETH -> stETH
slice = WstEthLike(gem).unwrap(slice);
gem = WstEthLike(gem).stETH();

// stETH -> ETH
TokenLike(gem).approve(lidoToETH, slice);
slice = CurvePoolLike(lidoToETH).exchange({
i: 1, // send token id 1 (stETH)
j: 0, // receive token id 0 (ETH)
dx: slice, // send `slice` amount of stETH
min_dy: 0 // accept any amount of ETH (`minProfit` is checked below)
});

// ETH -> wETH
WethLike(weth).deposit{
value: slice
}();
gem = weth;
talbaneth marked this conversation as resolved.
Show resolved Hide resolved

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

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

// Do operation and get dai amount bought (checking the profit is achieved)
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 (WethLike(gem).balanceOf(address(this)) > 0) {
WethLike(gem).transfer(to, WethLike(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)));
}
}
24 changes: 13 additions & 11 deletions src/test/Simulation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ interface UniV2Router02Abstract {
uint deadline
) external returns (uint amountToken, uint amountETH);

function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
external pure returns (uint amountIn);
}

Expand All @@ -82,7 +82,7 @@ interface WethAbstract is GemAbstract {
}

interface LpTokenAbstract is GemAbstract {
function getReserves() external view
function getReserves() external view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

Expand Down Expand Up @@ -618,7 +618,7 @@ contract SimulationTests is DSTest {
uint256 linkPrice = getLinkPrice();
uint256 expected = amountLink * linkPrice / WAD;
uint256 actual = dai.balanceOf(address(this));
uint256 diff = expected > actual ?
uint256 diff = expected > actual ?
expected - actual : actual - expected;
assertLt(diff, expected / 10);
}
Expand Down Expand Up @@ -684,7 +684,8 @@ contract SimulationTests is DSTest {
}

function testFrobMax() public {
uint256 amountLink = 2_000 * WAD;
(,,,, uint256 dustRad) = vat.ilks(linkName);
uint256 amountLink = (dustRad / getLinkPriceRay()) * 2;
getLink(amountLink);
joinLink(amountLink);
frobMax(amountLink, linkName);
Expand Down Expand Up @@ -739,7 +740,8 @@ contract SimulationTests is DSTest {
}

function testBarkLink() public {
uint256 amountLink = 2_000 * WAD;
(,,,, uint256 dustRad) = vat.ilks(linkName);
uint256 amountLink = (dustRad / getLinkPriceRay()) * 2;
uint256 kicksPre = linkClip.kicks();
getLink(amountLink);
joinLink(amountLink);
Expand All @@ -760,7 +762,7 @@ contract SimulationTests is DSTest {
auctionId = lpDaiEthClip.kicks();
}

function testBarkLpDaiEth() public {
function testBarkLpDaiEth() private { // most LP tokens are getting offboarded
talbaneth marked this conversation as resolved.
Show resolved Hide resolved
(,,,, uint256 dustRad) = vat.ilks(lpDaiEthName);
uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2;
uint256 kicksPre = lpDaiEthClip.kicks();
Expand Down Expand Up @@ -862,7 +864,7 @@ contract SimulationTests is DSTest {
hevm.warp(block.timestamp + 10 seconds);
(, auctionPrice,,) = linkClip.getStatus(auctionId);
}
uint256 minProfit = amountLink * auctionPrice / RAY
uint256 minProfit = amountLink * auctionPrice / RAY
* minProfitPct / 100;
assertEq(dai.balanceOf(bobAddr), 0);
takeLinkV2(auctionId, amountLink, auctionPrice, minProfit);
Expand Down Expand Up @@ -950,7 +952,7 @@ contract SimulationTests is DSTest {
lpDaiEthClip.take(auctionId, amt, max, cheAddr, data);
}

function testTakeLpDaiEthNoProfit() public {
function testTakeLpDaiEthNoProfit() private { // most LP tokens are getting offboarded
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that we have these tests replaced in a pending PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did a really great job of replacing these tests.

(,,,, uint256 dustRad) = vat.ilks(lpDaiEthName);
uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2;
getLpDaiEth(amount);
Expand All @@ -969,7 +971,7 @@ contract SimulationTests is DSTest {
assertLt(dai.balanceOf(bobAddr), amount * auctionPrice / RAY / 5);
}

function testTakeLpDaiEthProfit() public {
function testTakeLpDaiEthProfit() private { // most LP tokens are getting offboarded
talbaneth marked this conversation as resolved.
Show resolved Hide resolved
uint256 minProfitPct = 30;
(,,,, uint256 dustRad) = vat.ilks(lpDaiEthName);
uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2;
Expand All @@ -987,7 +989,7 @@ contract SimulationTests is DSTest {
hevm.warp(block.timestamp + 10 seconds);
(, auctionPrice,,) = lpDaiEthClip.getStatus(auctionId);
}
uint256 minProfit = amount * auctionPrice / RAY
uint256 minProfit = amount * auctionPrice / RAY
* minProfitPct / 100;
assertEq(dai.balanceOf(bobAddr), 0);
takeLpDaiEth(auctionId, amount, auctionPrice, minProfit);
Expand Down Expand Up @@ -1035,7 +1037,7 @@ contract SimulationTests is DSTest {

function testTakeSteCRVProfit() public {
uint256 amount = 30 * WAD;
uint256 minProfit = 30_000 * WAD;
uint256 minProfit = 3_000 * WAD;
getSteCRV(amount);
joinSteCRV(amount);
frobMaxSteCRV(amount);
Expand Down
Loading