From 264eae7ce816815d17e828e43b488fddf19810e5 Mon Sep 17 00:00:00 2001 From: Tal Date: Mon, 30 May 2022 18:46:42 +0300 Subject: [PATCH 01/32] Support wsteth complex path, fix other tests --- src/WstETHCurveUniv3Callee.sol | 6 ++-- src/test/Simulation.t.sol | 6 ++-- src/test/WstETHCurveUniv3Callee.t.sol | 41 ++++++++++++++++----------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/WstETHCurveUniv3Callee.sol b/src/WstETHCurveUniv3Callee.sol index 150014d..d1fd289 100644 --- a/src/WstETHCurveUniv3Callee.sol +++ b/src/WstETHCurveUniv3Callee.sol @@ -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(); @@ -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), @@ -180,4 +179,3 @@ contract WstETHCurveUniv3Callee { dai.transfer(to, dai.balanceOf(address(this))); } } - diff --git a/src/test/Simulation.t.sol b/src/test/Simulation.t.sol index aed9a91..f85c76e 100644 --- a/src/test/Simulation.t.sol +++ b/src/test/Simulation.t.sol @@ -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); @@ -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); diff --git a/src/test/WstETHCurveUniv3Callee.t.sol b/src/test/WstETHCurveUniv3Callee.t.sol index 3f36a03..4696305 100644 --- a/src/test/WstETHCurveUniv3Callee.t.sol +++ b/src/test/WstETHCurveUniv3Callee.t.sol @@ -91,11 +91,16 @@ contract CurveCalleeTest is DSTest { WstETHCurveUniv3Callee callee; uint256 tail; address vat; + address weth; + address dai; + address usdc; function setUp() public { clipper = Chainlog(chainlog).getAddress("MCD_CLIP_WSTETH_A"); address daiJoin = Chainlog(chainlog).getAddress("MCD_JOIN_DAI"); - address weth = Chainlog(chainlog).getAddress("ETH"); + weth = Chainlog(chainlog).getAddress("ETH"); + dai = Chainlog(chainlog).getAddress("MCD_DAI"); + usdc = Chainlog(chainlog).getAddress("USDC"); callee = new WstETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); vat = Chainlog(chainlog).getAddress("MCD_VAT"); Vat(vat).hope(clipper); @@ -138,11 +143,12 @@ contract CurveCalleeTest is DSTest { function test_baseline() public { uint256 amt = 50 * WAD; newAuction(amt); + uint24 poolFee = 3000; bytes memory data = abi.encode( address(123), address(gemJoin), uint256(0), - uint24(3000), + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); @@ -153,28 +159,29 @@ contract CurveCalleeTest is DSTest { who: address(callee), data: data }); - address dai = Chainlog(chainlog).getAddress("MCD_DAI"); assertEq(Token(dai).balanceOf(address(this)), 0); assertEq(Token(wstEth).balanceOf(address(this)), 0); } - function test_bigAmt() public { + function test_bigAmtWithComplexPath() public { uint256 amt = 3000 * WAD; newAuction(amt); + uint24 pooAlFee = 500; + uint24 pooBlFee = 100; bytes memory data = abi.encode( address(this), address(gemJoin), uint256(0), - uint24(3000), + abi.encodePacked(weth, pooAlFee, usdc, pooBlFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); Clipper(clipper).take({ - id: id, - amt: amt, - max: type(uint256).max, - who: address(callee), - data: data + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data }); } @@ -182,11 +189,12 @@ contract CurveCalleeTest is DSTest { uint256 minProfit = 10_000 * WAD; uint256 amt = 50 * WAD; newAuction(amt); + uint24 poolFee = 3000; bytes memory data = abi.encode( address(123), address(gemJoin), uint256(minProfit), - uint24(3000), + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); @@ -197,7 +205,6 @@ contract CurveCalleeTest is DSTest { who: address(callee), data: data }); - address dai = Chainlog(chainlog).getAddress("MCD_DAI"); assertGe(Token(dai).balanceOf(address(123)), minProfit); } @@ -209,7 +216,7 @@ contract CurveCalleeTest is DSTest { address(this), address(gemJoin), uint256(0), - poolFee, + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); @@ -230,7 +237,7 @@ contract CurveCalleeTest is DSTest { address(this), address(gemJoin), uint256(0), - poolFee, + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); @@ -246,11 +253,12 @@ contract CurveCalleeTest is DSTest { function test_maxPrice() public { uint256 amt = 50 * WAD; newAuction(amt); + uint24 poolFee = 3000; bytes memory data = abi.encode( address(this), address(gemJoin), uint256(0), - uint24(3000), + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); @@ -274,11 +282,12 @@ contract CurveCalleeTest is DSTest { function testFail_maxPrice() public { uint256 amt = 50 * WAD; newAuction(amt); + uint24 poolFee = 3000; bytes memory data = abi.encode( address(this), address(gemJoin), uint256(0), - uint24(3000), + abi.encodePacked(weth, poolFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 5); From e8c403a3da8fe8add2a0259ed8f18d224f8c1c48 Mon Sep 17 00:00:00 2001 From: talbaneth Date: Wed, 1 Jun 2022 17:45:40 +0300 Subject: [PATCH 02/32] fix typo - pooXlFee => poolXFee Co-authored-by: Julien <80432849+julienmartinlevrai@users.noreply.github.com> --- src/test/WstETHCurveUniv3Callee.t.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/WstETHCurveUniv3Callee.t.sol b/src/test/WstETHCurveUniv3Callee.t.sol index 4696305..943844b 100644 --- a/src/test/WstETHCurveUniv3Callee.t.sol +++ b/src/test/WstETHCurveUniv3Callee.t.sol @@ -166,13 +166,13 @@ contract CurveCalleeTest is DSTest { function test_bigAmtWithComplexPath() public { uint256 amt = 3000 * WAD; newAuction(amt); - uint24 pooAlFee = 500; - uint24 pooBlFee = 100; + uint24 poolAFee = 500; + uint24 poolBFee = 100; bytes memory data = abi.encode( address(this), address(gemJoin), uint256(0), - abi.encodePacked(weth, pooAlFee, usdc, pooBlFee, dai), + abi.encodePacked(weth, poolAFee, usdc, poolBFee, dai), address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); From bab3e028b5641676801a1f39477f7f14554fdfa0 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Wed, 22 Jun 2022 16:27:42 -0400 Subject: [PATCH 03/32] chore: copy WstETH callee into rETH callee --- src/rETHCurveUniv3Callee.sol | 181 +++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/rETHCurveUniv3Callee.sol diff --git a/src/rETHCurveUniv3Callee.sol b/src/rETHCurveUniv3Callee.sol new file mode 100644 index 0000000..d1fd289 --- /dev/null +++ b/src/rETHCurveUniv3Callee.sol @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// Copyright (C) 2021 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 . + +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); +} + +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 WstETHCurveUniv3Callee { + CurvePoolLike public immutable curvePool; + 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 curvePool_, + address uniV3Router_, + address daiJoin_, + address weth_ + ) public { + curvePool = CurvePoolLike(curvePool_); + 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(); + + // 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); + } + + slice = WstEthLike(gem).unwrap(slice); + gem = WstEthLike(gem).stETH(); + + TokenLike(gem).approve(address(curvePool), slice); + slice = curvePool.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) + }); + + gem = weth; + WethLike(gem).deposit{ + value: slice + }(); + + // 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))); + } +} From e6149138b0cf8bfe3065ec004df77f4d27c46be7 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Wed, 22 Jun 2022 16:53:33 -0400 Subject: [PATCH 04/32] =?UTF-8?q?feat:=C2=A0support=20rETH=20operations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rETHCurveUniv3Callee.sol | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/rETHCurveUniv3Callee.sol b/src/rETHCurveUniv3Callee.sol index d1fd289..3e3d5c5 100644 --- a/src/rETHCurveUniv3Callee.sol +++ b/src/rETHCurveUniv3Callee.sol @@ -1,5 +1,5 @@ +// SPDX-FileCopyrightText: © 2022 Dai Foundation // SPDX-License-Identifier: AGPL-3.0-or-later -// Copyright (C) 2021 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 @@ -39,11 +39,6 @@ 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); @@ -67,7 +62,7 @@ interface UniV3RouterLike { external payable returns (uint256 amountOut); } -contract WstETHCurveUniv3Callee { +contract rETHCurveUniv3Callee { CurvePoolLike public immutable curvePool; UniV3RouterLike public immutable uniV3Router; DaiJoinLike public immutable daiJoin; @@ -134,14 +129,11 @@ contract WstETHCurveUniv3Callee { GemJoinLike(gemJoin).exit(address(this), slice); } - slice = WstEthLike(gem).unwrap(slice); - gem = WstEthLike(gem).stETH(); - TokenLike(gem).approve(address(curvePool), slice); slice = curvePool.exchange({ - i: 1, // send token id 1 (stETH) + i: 1, // send token id 1 (rETH) j: 0, // receive token id 0 (ETH) - dx: slice, // send `slice` amount of stETH + dx: slice, // send `slice` amount of rETH min_dy: 0 // accept any amount of ETH (`minProfit` is checked below) }); From 3da77670ca704066c5222b42eb0542da4f009405 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Wed, 22 Jun 2022 22:44:09 -0400 Subject: [PATCH 05/32] =?UTF-8?q?fix:=C2=A0remove=20outdated=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/Simulation.t.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/Simulation.t.sol b/src/test/Simulation.t.sol index f85c76e..5e0c110 100644 --- a/src/test/Simulation.t.sol +++ b/src/test/Simulation.t.sol @@ -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); } @@ -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); } @@ -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); } @@ -762,7 +762,7 @@ contract SimulationTests is DSTest { auctionId = lpDaiEthClip.kicks(); } - function testBarkLpDaiEth() public { + function testBarkLpDaiEth() private { // most LP tokens are getting offboarded (,,,, uint256 dustRad) = vat.ilks(lpDaiEthName); uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2; uint256 kicksPre = lpDaiEthClip.kicks(); @@ -864,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); @@ -952,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 (,,,, uint256 dustRad) = vat.ilks(lpDaiEthName); uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2; getLpDaiEth(amount); @@ -971,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 uint256 minProfitPct = 30; (,,,, uint256 dustRad) = vat.ilks(lpDaiEthName); uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2; @@ -989,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); From 697079ea6b222bbb76ec6809f5dfaa39cd5b286a Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 24 Jun 2022 15:46:22 -0400 Subject: [PATCH 06/32] test: add test file for rETH callee --- src/test/rETHCurveUniv3Callee.t.sol | 310 ++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 src/test/rETHCurveUniv3Callee.t.sol diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol new file mode 100644 index 0000000..43adc9d --- /dev/null +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -0,0 +1,310 @@ +// SPDX-FileCopyrightText: © 2022 Dai Foundation +// 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 . + +pragma solidity ^0.6.12; + +import "ds-test/test.sol"; +import { WstETHCurveUniv3Callee } from "../WstETHCurveUniv3Callee.sol"; + +interface Hevm { + function store(address c, bytes32 loc, bytes32 val) external; + function warp(uint256) external; +} + +interface Chainlog { + function getAddress(bytes32) external view returns (address); +} + +interface Token { + function approve(address, uint256) external; + function balanceOf(address) external view returns (uint256); +} + +interface Join { + function join(address, uint256) external; +} + +interface Vat { + function ilks(bytes32) + external view returns (uint256, uint256, uint256, uint256, uint256); + function file(bytes32, bytes32, uint256) external; + function frob( + bytes32 i, + address u, + address v, + address w, + int dink, + int dart + ) external; + function hope(address) external; +} + +interface Jug { + function drip(bytes32) external; +} + +interface Dog { + function bark(bytes32, address, address) external returns (uint256); +} + +interface Clipper { + function tail() external view returns (uint256); + function take( + uint256 id, + uint256 amt, + uint256 max, + address who, + bytes calldata data + ) external; +} + +interface Osm { + function read() external view returns (bytes32); + function kiss(address) external; +} + +contract CurveCalleeTest is DSTest { + + address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; + address constant wstEth = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; + address constant curve = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; + address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + uint256 constant WAD = 1e18; + + address gemJoin; + uint256 id; + address clipper; + WstETHCurveUniv3Callee callee; + uint256 tail; + address vat; + address weth; + address dai; + address usdc; + + function setUp() public { + clipper = Chainlog(chainlog).getAddress("MCD_CLIP_WSTETH_A"); + address daiJoin = Chainlog(chainlog).getAddress("MCD_JOIN_DAI"); + weth = Chainlog(chainlog).getAddress("ETH"); + dai = Chainlog(chainlog).getAddress("MCD_DAI"); + usdc = Chainlog(chainlog).getAddress("USDC"); + callee = new WstETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); + vat = Chainlog(chainlog).getAddress("MCD_VAT"); + Vat(vat).hope(clipper); + tail = Clipper(clipper).tail(); + } + + function newAuction(uint256 amt) internal { + // wstEth._balances[address(this)] = amt; + Hevm(hevm).store({ + c: wstEth, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(amt) + }); + gemJoin = Chainlog(chainlog).getAddress("MCD_JOIN_WSTETH_A"); + Token(wstEth).approve(gemJoin, amt); + Join(gemJoin).join(address(this), amt); + (, uint256 rate, uint256 spot,,) = Vat(vat).ilks("WSTETH-A"); + uint256 maxArt = amt * spot / rate; + // vat.wards[address(this)] = 1; + Hevm(hevm).store({ + c: vat, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + Vat(vat).file("WSTETH-A", "line", type(uint256).max); + Vat(vat).frob({ + i: "WSTETH-A", + u: address(this), + v: address(this), + w: address(this), + dink: int(amt), + dart: int(maxArt) + }); + address jug = Chainlog(chainlog).getAddress("MCD_JUG"); + Jug(jug).drip("WSTETH-A"); + address dog = Chainlog(chainlog).getAddress("MCD_DOG"); + id = Dog(dog).bark("WSTETH-A", address(this), address(this)); + } + + function test_baseline() public { + uint256 amt = 50 * WAD; + newAuction(amt); + uint24 poolFee = 3000; + bytes memory data = abi.encode( + address(123), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + Clipper(clipper).take({ + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data + }); + assertEq(Token(dai).balanceOf(address(this)), 0); + assertEq(Token(wstEth).balanceOf(address(this)), 0); + } + + function test_bigAmtWithComplexPath() public { + uint256 amt = 3000 * WAD; + newAuction(amt); + uint24 poolAFee = 500; + uint24 poolBFee = 100; + bytes memory data = abi.encode( + address(this), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolAFee, usdc, poolBFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + Clipper(clipper).take({ + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data + }); + } + + function test_profit() public { + uint256 minProfit = 10_000 * WAD; + uint256 amt = 50 * WAD; + newAuction(amt); + uint24 poolFee = 3000; + bytes memory data = abi.encode( + address(123), + address(gemJoin), + uint256(minProfit), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + Clipper(clipper).take({ + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data + }); + assertGe(Token(dai).balanceOf(address(123)), minProfit); + } + + function test_poolFee() public { + uint24 poolFee = 500; + uint256 amt = 50 * WAD; + newAuction(amt); + bytes memory data = abi.encode( + address(this), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + Clipper(clipper).take({ + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data + }); + } + + function testFail_badPoolFee() public { + uint24 poolFee = 5000; + uint256 amt = 50 * WAD; + newAuction(amt); + bytes memory data = abi.encode( + address(this), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + Clipper(clipper).take({ + id: id, + amt: amt, + max: type(uint256).max, + who: address(callee), + data: data + }); + } + + function test_maxPrice() public { + uint256 amt = 50 * WAD; + newAuction(amt); + uint24 poolFee = 3000; + bytes memory data = abi.encode( + address(this), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 2); + address osm = Chainlog(chainlog).getAddress("PIP_WSTETH"); + Hevm(hevm).store({ + c: osm, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + Osm(osm).kiss(address(this)); + uint256 max = uint256(Osm(osm).read()) * 1e9; // WAD * 1e9 = RAY + Clipper(clipper).take({ + id: id, + amt: amt, + max: max, + who: address(callee), + data: data + }); + } + + function testFail_maxPrice() public { + uint256 amt = 50 * WAD; + newAuction(amt); + uint24 poolFee = 3000; + bytes memory data = abi.encode( + address(this), + address(gemJoin), + uint256(0), + abi.encodePacked(weth, poolFee, dai), + address(0) + ); + Hevm(hevm).warp(block.timestamp + tail / 5); + address osm = Chainlog(chainlog).getAddress("PIP_WSTETH"); + Hevm(hevm).store({ + c: osm, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + Osm(osm).kiss(address(this)); + uint256 max = uint256(Osm(osm).read()) * 1e9; // WAD * 1e9 = RAY + Clipper(clipper).take({ + id: id, + amt: amt, + max: max, + who: address(callee), + data: data + }); + } +} From ca512e99f95c371b0c2369de4e49150498df6938 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 24 Jun 2022 16:10:42 -0400 Subject: [PATCH 07/32] =?UTF-8?q?test:=C2=A0replace=20WstETH=20for=20rETH?= =?UTF-8?q?=20in=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 43adc9d..fde0004 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -17,7 +17,7 @@ pragma solidity ^0.6.12; import "ds-test/test.sol"; -import { WstETHCurveUniv3Callee } from "../WstETHCurveUniv3Callee.sol"; +import { rETHCurveUniv3Callee } from "../rETHCurveUniv3Callee.sol"; interface Hevm { function store(address c, bytes32 loc, bytes32 val) external; @@ -79,7 +79,7 @@ interface Osm { contract CurveCalleeTest is DSTest { address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - address constant wstEth = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + address constant rEth = 0xae78736Cd615f374D3085123A210448E74Fc6393; address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; address constant curve = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; @@ -88,7 +88,7 @@ contract CurveCalleeTest is DSTest { address gemJoin; uint256 id; address clipper; - WstETHCurveUniv3Callee callee; + rETHCurveUniv3Callee callee; uint256 tail; address vat; address weth; @@ -96,28 +96,28 @@ contract CurveCalleeTest is DSTest { address usdc; function setUp() public { - clipper = Chainlog(chainlog).getAddress("MCD_CLIP_WSTETH_A"); + clipper = Chainlog(chainlog).getAddress("MCD_CLIP_RETH_A"); address daiJoin = Chainlog(chainlog).getAddress("MCD_JOIN_DAI"); weth = Chainlog(chainlog).getAddress("ETH"); dai = Chainlog(chainlog).getAddress("MCD_DAI"); usdc = Chainlog(chainlog).getAddress("USDC"); - callee = new WstETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); + callee = new rETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); vat = Chainlog(chainlog).getAddress("MCD_VAT"); Vat(vat).hope(clipper); tail = Clipper(clipper).tail(); } function newAuction(uint256 amt) internal { - // wstEth._balances[address(this)] = amt; + // rEth._balances[address(this)] = amt; Hevm(hevm).store({ - c: wstEth, - loc: keccak256(abi.encode(address(this), uint256(0))), + c: rEth, + loc: keccak256(abi.encode(address(this), uint256(2))), val: bytes32(amt) }); - gemJoin = Chainlog(chainlog).getAddress("MCD_JOIN_WSTETH_A"); - Token(wstEth).approve(gemJoin, amt); + gemJoin = Chainlog(chainlog).getAddress("MCD_JOIN_RETH_A"); + Token(rEth).approve(gemJoin, amt); Join(gemJoin).join(address(this), amt); - (, uint256 rate, uint256 spot,,) = Vat(vat).ilks("WSTETH-A"); + (, uint256 rate, uint256 spot,,) = Vat(vat).ilks("RETH-A"); uint256 maxArt = amt * spot / rate; // vat.wards[address(this)] = 1; Hevm(hevm).store({ @@ -125,9 +125,9 @@ contract CurveCalleeTest is DSTest { loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - Vat(vat).file("WSTETH-A", "line", type(uint256).max); + Vat(vat).file("RETH-A", "line", type(uint256).max); Vat(vat).frob({ - i: "WSTETH-A", + i: "RETH-A", u: address(this), v: address(this), w: address(this), @@ -135,9 +135,9 @@ contract CurveCalleeTest is DSTest { dart: int(maxArt) }); address jug = Chainlog(chainlog).getAddress("MCD_JUG"); - Jug(jug).drip("WSTETH-A"); + Jug(jug).drip("RETH-A"); address dog = Chainlog(chainlog).getAddress("MCD_DOG"); - id = Dog(dog).bark("WSTETH-A", address(this), address(this)); + id = Dog(dog).bark("RETH-A", address(this), address(this)); } function test_baseline() public { @@ -160,7 +160,7 @@ contract CurveCalleeTest is DSTest { data: data }); assertEq(Token(dai).balanceOf(address(this)), 0); - assertEq(Token(wstEth).balanceOf(address(this)), 0); + assertEq(Token(rEth).balanceOf(address(this)), 0); } function test_bigAmtWithComplexPath() public { From cdbbfe483aee12f5bf9eb98911b38f8f495747a1 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 11:33:46 +0200 Subject: [PATCH 08/32] =?UTF-8?q?fix:=C2=A0reduce=20profit=20for=20SteCRV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/Simulation.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/Simulation.t.sol b/src/test/Simulation.t.sol index 5e0c110..b0d8102 100644 --- a/src/test/Simulation.t.sol +++ b/src/test/Simulation.t.sol @@ -1037,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); From 2f594b260be08b4ac212dfc10c43c7336f3f8897 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 12:13:46 +0200 Subject: [PATCH 09/32] =?UTF-8?q?test:=C2=A0use=20forge=20caching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ test.sh | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e2e7327..a282919 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /out +/cache +block diff --git a/test.sh b/test.sh index 5be365a..5d9cc4a 100755 --- a/test.sh +++ b/test.sh @@ -1,7 +1,16 @@ #! /bin/bash +if test -f block; then + BLOCK=$(cat block) + echo "using cached block ${BLOCK}, delete ./block to refresh" +else + LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) + BLOCK=$(($LATEST_BLOCK-6)) + echo "using fresh block ${BLOCK}" +fi if [[ $# -eq 0 ]] ; then - dapp --use solc:0.6.12 test --rpc + forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK else - dapp --use solc:0.6.12 test --rpc --verbosity 3 -m ${1} + forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK --match ${1} -vvv fi +echo $BLOCK > block From 5a79a169af616bdffcc63dfe4e9187212d16242f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 12:15:47 +0200 Subject: [PATCH 10/32] feat: add rETH callee to Makefile flatten --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f775ffc..5a1109b 100644 --- a/Makefile +++ b/Makefile @@ -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 From bbfedd625a8c68877527ba7ca0456277c6ceb0a9 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 12:22:12 +0200 Subject: [PATCH 11/32] =?UTF-8?q?chore:=C2=A0add=20block=20age=20to=20test?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index 5d9cc4a..073291b 100755 --- a/test.sh +++ b/test.sh @@ -1,10 +1,11 @@ #! /bin/bash +LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) if test -f block; then BLOCK=$(cat block) - echo "using cached block ${BLOCK}, delete ./block to refresh" + AGE=$((LATEST_BLOCK - BLOCK)) + echo "using cached block ${BLOCK} (${AGE} blocks ago), delete ./block to refresh" else - LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) BLOCK=$(($LATEST_BLOCK-6)) echo "using fresh block ${BLOCK}" fi From d660ad3f977554ea1d9ddc548e4484ccd4130a5c Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 13:36:50 +0200 Subject: [PATCH 12/32] test: deploy Clipper --- src/test/rETHCurveUniv3Callee.t.sol | 42 ++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index fde0004..c1cc2ad 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -19,6 +19,9 @@ pragma solidity ^0.6.12; import "ds-test/test.sol"; import { rETHCurveUniv3Callee } from "../rETHCurveUniv3Callee.sol"; +// FIXME: remove this import once rETH has been onboarded +import { Clipper } from "dss/clip.sol"; + interface Hevm { function store(address c, bytes32 loc, bytes32 val) external; function warp(uint256) external; @@ -60,7 +63,7 @@ interface Dog { function bark(bytes32, address, address) external returns (uint256); } -interface Clipper { +interface ClipperLike { function tail() external view returns (uint256); function take( uint256 id, @@ -76,6 +79,11 @@ interface Osm { function kiss(address) external; } +// FIXME: delete this interface once rETH has been onboarded +interface ChainlogTemp { + function setAddress(bytes32, address) external; +} + contract CurveCalleeTest is DSTest { address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; @@ -95,7 +103,23 @@ contract CurveCalleeTest is DSTest { address dai; address usdc; + // FIXME: delete this function once rETH has been onboarded + function onboardRETH() private { + vat = Chainlog(chainlog).getAddress("MCD_VAT"); + address spotter = Chainlog(chainlog).getAddress("MCD_SPOT"); + address dog = Chainlog(chainlog).getAddress("MCD_DOG"); + bytes32 ilk = "RETH-A"; + Clipper newClipper = new Clipper(vat, spotter, dog, ilk); + Hevm(hevm).store({ + c: chainlog, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(newClipper)); + } + function setUp() public { + onboardRETH(); // FIXME: delete this line once rETH has been onboarded clipper = Chainlog(chainlog).getAddress("MCD_CLIP_RETH_A"); address daiJoin = Chainlog(chainlog).getAddress("MCD_JOIN_DAI"); weth = Chainlog(chainlog).getAddress("ETH"); @@ -104,7 +128,7 @@ contract CurveCalleeTest is DSTest { callee = new rETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); vat = Chainlog(chainlog).getAddress("MCD_VAT"); Vat(vat).hope(clipper); - tail = Clipper(clipper).tail(); + tail = ClipperLike(clipper).tail(); } function newAuction(uint256 amt) internal { @@ -152,7 +176,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: type(uint256).max, @@ -176,7 +200,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: type(uint256).max, @@ -198,7 +222,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: type(uint256).max, @@ -220,7 +244,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: type(uint256).max, @@ -241,7 +265,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: type(uint256).max, @@ -270,7 +294,7 @@ contract CurveCalleeTest is DSTest { }); Osm(osm).kiss(address(this)); uint256 max = uint256(Osm(osm).read()) * 1e9; // WAD * 1e9 = RAY - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: max, @@ -299,7 +323,7 @@ contract CurveCalleeTest is DSTest { }); Osm(osm).kiss(address(this)); uint256 max = uint256(Osm(osm).read()) * 1e9; // WAD * 1e9 = RAY - Clipper(clipper).take({ + ClipperLike(clipper).take({ id: id, amt: amt, max: max, From b6081ffd9d43d2290848c961a2edf99b9d2c1048 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 14:00:46 +0200 Subject: [PATCH 13/32] =?UTF-8?q?test:=C2=A0deploy=20rETH=20join=20adapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index c1cc2ad..84f11d2 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -19,8 +19,9 @@ pragma solidity ^0.6.12; import "ds-test/test.sol"; import { rETHCurveUniv3Callee } from "../rETHCurveUniv3Callee.sol"; -// FIXME: remove this import once rETH has been onboarded +// FIXME: remove these imports once rETH has been onboarded import { Clipper } from "dss/clip.sol"; +import { GemJoin } from "dss/join.sol"; interface Hevm { function store(address c, bytes32 loc, bytes32 val) external; @@ -109,13 +110,15 @@ contract CurveCalleeTest is DSTest { address spotter = Chainlog(chainlog).getAddress("MCD_SPOT"); address dog = Chainlog(chainlog).getAddress("MCD_DOG"); bytes32 ilk = "RETH-A"; - Clipper newClipper = new Clipper(vat, spotter, dog, ilk); + Clipper rETHClipper = new Clipper(vat, spotter, dog, ilk); + GemJoin rETHJoin = new GemJoin(vat, ilk, rEth); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(newClipper)); + ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); + ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } function setUp() public { From ba044ee0138ea4a607c9ba2bdf0d7b10fd50153f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 14:25:29 +0200 Subject: [PATCH 14/32] =?UTF-8?q?test:=C2=A0rely=20join=20in=20vat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 84f11d2..d99bed4 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -80,15 +80,18 @@ interface Osm { function kiss(address) external; } -// FIXME: delete this interface once rETH has been onboarded +// FIXME: delete these interfaces once rETH has been onboarded interface ChainlogTemp { function setAddress(bytes32, address) external; } +interface VatTemp { + function rely(address) external; +} contract CurveCalleeTest is DSTest { address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - address constant rEth = 0xae78736Cd615f374D3085123A210448E74Fc6393; + address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; address constant curve = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; @@ -111,7 +114,13 @@ contract CurveCalleeTest is DSTest { address dog = Chainlog(chainlog).getAddress("MCD_DOG"); bytes32 ilk = "RETH-A"; Clipper rETHClipper = new Clipper(vat, spotter, dog, ilk); - GemJoin rETHJoin = new GemJoin(vat, ilk, rEth); + GemJoin rETHJoin = new GemJoin(vat, ilk, rETH); + Hevm(hevm).store({ + c: vat, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + VatTemp(vat).rely(address(rETHJoin)); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), @@ -135,14 +144,14 @@ contract CurveCalleeTest is DSTest { } function newAuction(uint256 amt) internal { - // rEth._balances[address(this)] = amt; + // rETH._balances[address(this)] = amt; Hevm(hevm).store({ - c: rEth, + c: rETH, loc: keccak256(abi.encode(address(this), uint256(2))), val: bytes32(amt) }); gemJoin = Chainlog(chainlog).getAddress("MCD_JOIN_RETH_A"); - Token(rEth).approve(gemJoin, amt); + Token(rETH).approve(gemJoin, amt); Join(gemJoin).join(address(this), amt); (, uint256 rate, uint256 spot,,) = Vat(vat).ilks("RETH-A"); uint256 maxArt = amt * spot / rate; @@ -187,7 +196,7 @@ contract CurveCalleeTest is DSTest { data: data }); assertEq(Token(dai).balanceOf(address(this)), 0); - assertEq(Token(rEth).balanceOf(address(this)), 0); + assertEq(Token(rETH).balanceOf(address(this)), 0); } function test_bigAmtWithComplexPath() public { From e5574eea2e801f02a5987bd29d089e5366582a2d Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 14:36:38 +0200 Subject: [PATCH 15/32] =?UTF-8?q?fix:=C2=A0rETH=20balanceOf=20storage=20sl?= =?UTF-8?q?ot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index d99bed4..89ab3ed 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -147,7 +147,7 @@ contract CurveCalleeTest is DSTest { // rETH._balances[address(this)] = amt; Hevm(hevm).store({ c: rETH, - loc: keccak256(abi.encode(address(this), uint256(2))), + loc: keccak256(abi.encode(address(this), uint256(1))), val: bytes32(amt) }); gemJoin = Chainlog(chainlog).getAddress("MCD_JOIN_RETH_A"); From 94b83c7d643b0d63c4db644967409c23633f1c0f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 16:52:32 +0200 Subject: [PATCH 16/32] test: init ilk in vat --- src/test/rETHCurveUniv3Callee.t.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 89ab3ed..a061c3e 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -86,6 +86,7 @@ interface ChainlogTemp { } interface VatTemp { function rely(address) external; + function init(bytes32) external; } contract CurveCalleeTest is DSTest { @@ -121,6 +122,7 @@ contract CurveCalleeTest is DSTest { val: bytes32(uint256(1)) }); VatTemp(vat).rely(address(rETHJoin)); + VatTemp(vat).init(ilk); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), From 1c6c06476a5d4c1c4c00ed6495d70744279f7f4f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 17:35:09 +0200 Subject: [PATCH 17/32] =?UTF-8?q?feat:=C2=A0init=20ilk=20in=20spotter=20an?= =?UTF-8?q?d=20jug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index a061c3e..f8ef58d 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -88,6 +88,15 @@ interface VatTemp { function rely(address) external; function init(bytes32) external; } +interface JugTemp { + function init(bytes32) external; + function file(bytes32, bytes32, uint256) external; +} +interface SpotterTemp { + function file(bytes32, bytes32, address) external; + function file(bytes32, bytes32, uint256) external; + function poke(bytes32) external; +} contract CurveCalleeTest is DSTest { @@ -113,6 +122,7 @@ contract CurveCalleeTest is DSTest { vat = Chainlog(chainlog).getAddress("MCD_VAT"); address spotter = Chainlog(chainlog).getAddress("MCD_SPOT"); address dog = Chainlog(chainlog).getAddress("MCD_DOG"); + address jug = Chainlog(chainlog).getAddress("MCD_JUG"); bytes32 ilk = "RETH-A"; Clipper rETHClipper = new Clipper(vat, spotter, dog, ilk); GemJoin rETHJoin = new GemJoin(vat, ilk, rETH); @@ -123,6 +133,21 @@ contract CurveCalleeTest is DSTest { }); VatTemp(vat).rely(address(rETHJoin)); VatTemp(vat).init(ilk); + Hevm(hevm).store({ + c: jug, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + JugTemp(jug).init(ilk); + JugTemp(jug).file(ilk, "duty", 1000000000705562181084137268); + Hevm(hevm).store({ + c: spotter, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); + SpotterTemp(spotter).file(ilk, "pip", 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763); + SpotterTemp(spotter).file(ilk, "mat", 1450000000000000000000000000); + SpotterTemp(spotter).poke(ilk); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), From 3cd9762af84d5147bdd8b5e89be949e64a6a39c0 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 30 Jun 2022 18:38:42 +0200 Subject: [PATCH 18/32] =?UTF-8?q?test:=C2=A0warp=20after=20ilk=20init=20in?= =?UTF-8?q?=20jug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index f8ef58d..9002f2e 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -140,6 +140,7 @@ contract CurveCalleeTest is DSTest { }); JugTemp(jug).init(ilk); JugTemp(jug).file(ilk, "duty", 1000000000705562181084137268); + Hevm(hevm).warp(block.timestamp + 1); Hevm(hevm).store({ c: spotter, loc: keccak256(abi.encode(address(this), uint256(0))), From 910903a840af904a22553580e2af66b62014f97b Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 1 Jul 2022 13:13:34 +0200 Subject: [PATCH 19/32] =?UTF-8?q?test:=C2=A0file=20dog.hole=20for=20new=20?= =?UTF-8?q?ilk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 9002f2e..118cf3a 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -90,12 +90,14 @@ interface VatTemp { } interface JugTemp { function init(bytes32) external; - function file(bytes32, bytes32, uint256) external; + function ilks(bytes32) external view returns (uint256,uint256); } interface SpotterTemp { + function poke(bytes32) external; +} +interface Fileable { function file(bytes32, bytes32, address) external; function file(bytes32, bytes32, uint256) external; - function poke(bytes32) external; } contract CurveCalleeTest is DSTest { @@ -131,29 +133,35 @@ contract CurveCalleeTest is DSTest { loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - VatTemp(vat).rely(address(rETHJoin)); - VatTemp(vat).init(ilk); Hevm(hevm).store({ c: jug, loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - JugTemp(jug).init(ilk); - JugTemp(jug).file(ilk, "duty", 1000000000705562181084137268); - Hevm(hevm).warp(block.timestamp + 1); Hevm(hevm).store({ c: spotter, loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - SpotterTemp(spotter).file(ilk, "pip", 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763); - SpotterTemp(spotter).file(ilk, "mat", 1450000000000000000000000000); - SpotterTemp(spotter).poke(ilk); + Hevm(hevm).store({ + c: dog, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); + VatTemp(vat).rely(address(rETHJoin)); + VatTemp(vat).init(ilk); + JugTemp(jug).init(ilk); + Fileable(jug).file(ilk, "duty", 1000000000705562181084137268); + Hevm(hevm).warp(block.timestamp + 1); + Fileable(spotter).file(ilk, "pip", 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763); + Fileable(spotter).file(ilk, "mat", 1450000000000000000000000000); + SpotterTemp(spotter).poke(ilk); + Fileable(dog).file(ilk, "hole", type(uint256).max); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } From e9a5776d26699fd3a8b2fcf56d83e6d375ef4774 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 1 Jul 2022 13:22:27 +0200 Subject: [PATCH 20/32] =?UTF-8?q?test:=20file=20ilk=E2=80=99s=20chop=20in?= =?UTF-8?q?=20dog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 118cf3a..1e27949 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -162,6 +162,7 @@ contract CurveCalleeTest is DSTest { Fileable(spotter).file(ilk, "mat", 1450000000000000000000000000); SpotterTemp(spotter).poke(ilk); Fileable(dog).file(ilk, "hole", type(uint256).max); + Fileable(dog).file(ilk, "chop", 11 * WAD / 10); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } From 76d94f0ed219e4a9d09013fef3a4c8c70cc4d242 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 1 Jul 2022 13:43:43 +0200 Subject: [PATCH 21/32] =?UTF-8?q?test:=C2=A0clipper=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 1e27949..2da94b4 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -95,6 +95,9 @@ interface JugTemp { interface SpotterTemp { function poke(bytes32) external; } +interface PipTemp { + function kiss(address) external; +} interface Fileable { function file(bytes32, bytes32, address) external; function file(bytes32, bytes32, uint256) external; @@ -125,6 +128,7 @@ contract CurveCalleeTest is DSTest { address spotter = Chainlog(chainlog).getAddress("MCD_SPOT"); address dog = Chainlog(chainlog).getAddress("MCD_DOG"); address jug = Chainlog(chainlog).getAddress("MCD_JUG"); + address pipEth = Chainlog(chainlog).getAddress("PIP_ETH"); bytes32 ilk = "RETH-A"; Clipper rETHClipper = new Clipper(vat, spotter, dog, ilk); GemJoin rETHJoin = new GemJoin(vat, ilk, rETH); @@ -148,6 +152,11 @@ contract CurveCalleeTest is DSTest { loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); + Hevm(hevm).store({ + c: pipEth, + loc: keccak256(abi.encode(address(this), uint256(0))), + val: bytes32(uint256(1)) + }); Hevm(hevm).store({ c: chainlog, loc: keccak256(abi.encode(address(this), uint256(0))), @@ -158,11 +167,14 @@ contract CurveCalleeTest is DSTest { JugTemp(jug).init(ilk); Fileable(jug).file(ilk, "duty", 1000000000705562181084137268); Hevm(hevm).warp(block.timestamp + 1); - Fileable(spotter).file(ilk, "pip", 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763); + Fileable(spotter).file(ilk, "pip", pipEth); Fileable(spotter).file(ilk, "mat", 1450000000000000000000000000); SpotterTemp(spotter).poke(ilk); Fileable(dog).file(ilk, "hole", type(uint256).max); Fileable(dog).file(ilk, "chop", 11 * WAD / 10); + Fileable(dog).file(ilk, "clip", address(rETHClipper)); + rETHClipper.rely(dog); + PipTemp(pipEth).kiss(address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } From 8b6ae22d59df2e330dacec03e267a5ca01d0c955 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 1 Jul 2022 14:57:01 +0200 Subject: [PATCH 22/32] =?UTF-8?q?test:=C2=A0add=20abacus=20for=20new=20ilk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 2da94b4..6f62b4c 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -22,6 +22,7 @@ import { rETHCurveUniv3Callee } from "../rETHCurveUniv3Callee.sol"; // FIXME: remove these imports once rETH has been onboarded import { Clipper } from "dss/clip.sol"; import { GemJoin } from "dss/join.sol"; +import { StairstepExponentialDecrease } from "dss/abaci.sol"; interface Hevm { function store(address c, bytes32 loc, bytes32 val) external; @@ -110,7 +111,9 @@ contract CurveCalleeTest is DSTest { address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; address constant curve = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; - uint256 constant WAD = 1e18; + + uint256 constant WAD = 1e18; + uint256 constant RAY = 1e27; address gemJoin; uint256 id; @@ -175,6 +178,10 @@ contract CurveCalleeTest is DSTest { Fileable(dog).file(ilk, "clip", address(rETHClipper)); rETHClipper.rely(dog); PipTemp(pipEth).kiss(address(rETHClipper)); + StairstepExponentialDecrease rETHCalc = new StairstepExponentialDecrease(); + rETHCalc.file("cut", 99 * RAY / 100); + rETHCalc.file("step", 90); + rETHClipper.file("calc", address(rETHCalc)); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } From d7bbe7ebd03a3d62f826daf7211e45b4e96ae19c Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 1 Jul 2022 15:33:41 +0200 Subject: [PATCH 23/32] =?UTF-8?q?test:=C2=A0fix=20curve=20pool=20address?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 6f62b4c..20ad49b 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -109,7 +109,7 @@ contract CurveCalleeTest is DSTest { address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; - address constant curve = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; + address constant curve = 0xF9440930043eb3997fc70e1339dBb11F341de7A8; address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; uint256 constant WAD = 1e18; From 4790bc813b73f048549dc159e30335b588b66639 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Mon, 4 Jul 2022 15:43:20 +0200 Subject: [PATCH 24/32] =?UTF-8?q?feat:=C2=A02x=20Curve=20swap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rETHCurveUniv3Callee.sol | 51 +++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/rETHCurveUniv3Callee.sol b/src/rETHCurveUniv3Callee.sol index 3e3d5c5..df60d49 100644 --- a/src/rETHCurveUniv3Callee.sol +++ b/src/rETHCurveUniv3Callee.sol @@ -39,9 +39,15 @@ 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 { @@ -49,7 +55,7 @@ interface WethLike is TokenLike { } interface UniV3RouterLike { - + struct ExactInputParams { bytes path; address recipient; @@ -63,7 +69,10 @@ interface UniV3RouterLike { } contract rETHCurveUniv3Callee { - CurvePoolLike public immutable curvePool; + + address public constant rocketToLido = 0x447Ddd4960d9fdBF6af9a790560d0AF76795CB08; + address public constant lidoToETH = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; + UniV3RouterLike public immutable uniV3Router; DaiJoinLike public immutable daiJoin; TokenLike public immutable dai; @@ -81,13 +90,7 @@ contract rETHCurveUniv3Callee { z = _add(x, _sub(y, 1)) / y; } - constructor( - address curvePool_, - address uniV3Router_, - address daiJoin_, - address weth_ - ) public { - curvePool = CurvePoolLike(curvePool_); + constructor(address uniV3Router_, address daiJoin_, address weth_) public { uniV3Router = UniV3RouterLike(uniV3Router_); daiJoin = DaiJoinLike(daiJoin_); TokenLike dai_ = DaiJoinLike(daiJoin_).dai(); @@ -117,7 +120,7 @@ contract rETHCurveUniv3Callee { address charterManager // pass address(0) if no manager ) = abi.decode(data, (address, address, uint256, bytes, address)); - address gem = GemJoinLike(gemJoin).gem(); + address gem = GemJoinLike(gemJoin).gem(); // RocketPool rETH // Convert slice to token precision slice = _fromWad(gemJoin, slice); @@ -129,18 +132,34 @@ contract rETHCurveUniv3Callee { GemJoinLike(gemJoin).exit(address(this), slice); } - TokenLike(gem).approve(address(curvePool), slice); - slice = curvePool.exchange({ - i: 1, // send token id 1 (rETH) - j: 0, // receive token id 0 (ETH) + // 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); - gem = weth; - WethLike(gem).deposit{ + // 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; // Approve uniV3 to take gem WethLike(gem).approve(address(uniV3Router), slice); From 4854df4ac3f3c28a4257a4e2c36f85078ad958a0 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Mon, 4 Jul 2022 15:44:10 +0200 Subject: [PATCH 25/32] =?UTF-8?q?test:=C2=A0rely=20rETH=20clipper=20in=20d?= =?UTF-8?q?og?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 12 +++++++----- test.sh | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 20ad49b..ee9f728 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -81,12 +81,11 @@ interface Osm { function kiss(address) external; } -// FIXME: delete these interfaces once rETH has been onboarded +// FIXME: delete all the following interfaces once rETH has been onboarded interface ChainlogTemp { function setAddress(bytes32, address) external; } interface VatTemp { - function rely(address) external; function init(bytes32) external; } interface JugTemp { @@ -103,13 +102,15 @@ interface Fileable { function file(bytes32, bytes32, address) external; function file(bytes32, bytes32, uint256) external; } +interface Authable { + function rely(address) external; +} contract CurveCalleeTest is DSTest { address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; - address constant curve = 0xF9440930043eb3997fc70e1339dBb11F341de7A8; address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; uint256 constant WAD = 1e18; @@ -165,7 +166,7 @@ contract CurveCalleeTest is DSTest { loc: keccak256(abi.encode(address(this), uint256(0))), val: bytes32(uint256(1)) }); - VatTemp(vat).rely(address(rETHJoin)); + Authable(vat).rely(address(rETHJoin)); VatTemp(vat).init(ilk); JugTemp(jug).init(ilk); Fileable(jug).file(ilk, "duty", 1000000000705562181084137268); @@ -182,6 +183,7 @@ contract CurveCalleeTest is DSTest { rETHCalc.file("cut", 99 * RAY / 100); rETHCalc.file("step", 90); rETHClipper.file("calc", address(rETHCalc)); + Authable(dog).rely(address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } @@ -193,7 +195,7 @@ contract CurveCalleeTest is DSTest { weth = Chainlog(chainlog).getAddress("ETH"); dai = Chainlog(chainlog).getAddress("MCD_DAI"); usdc = Chainlog(chainlog).getAddress("USDC"); - callee = new rETHCurveUniv3Callee(curve, uniV3, daiJoin, weth); + callee = new rETHCurveUniv3Callee(uniV3, daiJoin, weth); vat = Chainlog(chainlog).getAddress("MCD_VAT"); Vat(vat).hope(clipper); tail = ClipperLike(clipper).tail(); diff --git a/test.sh b/test.sh index 073291b..42179ff 100755 --- a/test.sh +++ b/test.sh @@ -1,5 +1,7 @@ #! /bin/bash +[[ "$(cast chain --rpc-url="$ETH_RPC_URL")" == "ethlive" ]] || { echo "Please set a mainnet ETH_RPC_URL"; exit 1; } + LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) if test -f block; then BLOCK=$(cat block) From c0e10564f84ed3196b3f65d3a009401ef9ddac4f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 7 Jul 2022 14:03:27 +0200 Subject: [PATCH 26/32] =?UTF-8?q?test:=C2=A0file=20clipper=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/rETHCurveUniv3Callee.t.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index ee9f728..b3a1a21 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -184,6 +184,8 @@ contract CurveCalleeTest is DSTest { rETHCalc.file("step", 90); rETHClipper.file("calc", address(rETHCalc)); Authable(dog).rely(address(rETHClipper)); + rETHClipper.file("buf", 12 * RAY / 10); + rETHClipper.file("tail", 8400); ChainlogTemp(chainlog).setAddress("MCD_CLIP_RETH_A", address(rETHClipper)); ChainlogTemp(chainlog).setAddress("MCD_JOIN_RETH_A", address(rETHJoin)); } @@ -356,7 +358,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 2); - address osm = Chainlog(chainlog).getAddress("PIP_WSTETH"); + address osm = Chainlog(chainlog).getAddress("PIP_ETH"); Hevm(hevm).store({ c: osm, loc: keccak256(abi.encode(address(this), uint256(0))), @@ -385,7 +387,7 @@ contract CurveCalleeTest is DSTest { address(0) ); Hevm(hevm).warp(block.timestamp + tail / 5); - address osm = Chainlog(chainlog).getAddress("PIP_WSTETH"); + address osm = Chainlog(chainlog).getAddress("PIP_ETH"); Hevm(hevm).store({ c: osm, loc: keccak256(abi.encode(address(this), uint256(0))), From 3f30750915b4f38b86c00b2b5882085b03db8b46 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 4 Aug 2022 13:24:47 +0200 Subject: [PATCH 27/32] =?UTF-8?q?chore:=C2=A0use=20test-forge.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + test-forge.sh | 19 +++++++++++++++++++ test.sh | 16 ++-------------- 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100755 test-forge.sh diff --git a/Makefile b/Makefile index 5a1109b..64c26d4 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ all :; DAPP_BUILD_OPTIMIZE=1 DAPP_BUILD_OPTIMIZE_RUNS=200 dapp --use clean :; dapp clean # usage: make test match=Burn test :; ./test.sh ${match} +test-forge :; ./test-forge.sh ${match} deploy :; echo "use deploy-goerli or deploy-mainnet" deploy-goerli :; make && ./scripts/deploy-goerli.sh deploy-mainnet :; make && ./scripts/deploy-mainnet.sh diff --git a/test-forge.sh b/test-forge.sh new file mode 100755 index 0000000..42179ff --- /dev/null +++ b/test-forge.sh @@ -0,0 +1,19 @@ +#! /bin/bash + +[[ "$(cast chain --rpc-url="$ETH_RPC_URL")" == "ethlive" ]] || { echo "Please set a mainnet ETH_RPC_URL"; exit 1; } + +LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) +if test -f block; then + BLOCK=$(cat block) + AGE=$((LATEST_BLOCK - BLOCK)) + echo "using cached block ${BLOCK} (${AGE} blocks ago), delete ./block to refresh" +else + BLOCK=$(($LATEST_BLOCK-6)) + echo "using fresh block ${BLOCK}" +fi +if [[ $# -eq 0 ]] ; then + forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK +else + forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK --match ${1} -vvv +fi +echo $BLOCK > block diff --git a/test.sh b/test.sh index 42179ff..5be365a 100755 --- a/test.sh +++ b/test.sh @@ -1,19 +1,7 @@ #! /bin/bash -[[ "$(cast chain --rpc-url="$ETH_RPC_URL")" == "ethlive" ]] || { echo "Please set a mainnet ETH_RPC_URL"; exit 1; } - -LATEST_BLOCK=$(cast block --rpc-url $ETH_RPC_URL latest number) -if test -f block; then - BLOCK=$(cat block) - AGE=$((LATEST_BLOCK - BLOCK)) - echo "using cached block ${BLOCK} (${AGE} blocks ago), delete ./block to refresh" -else - BLOCK=$(($LATEST_BLOCK-6)) - echo "using fresh block ${BLOCK}" -fi if [[ $# -eq 0 ]] ; then - forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK + dapp --use solc:0.6.12 test --rpc else - forge test --fork-url $ETH_RPC_URL --fork-block-number $BLOCK --match ${1} -vvv + dapp --use solc:0.6.12 test --rpc --verbosity 3 -m ${1} fi -echo $BLOCK > block From 157fe46b42e4ef503ce89554a0590b9b1fa2a04c Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 4 Aug 2022 13:51:53 +0200 Subject: [PATCH 28/32] =?UTF-8?q?fix:=C2=A0pass=20curve=20pools=20as=20con?= =?UTF-8?q?structor=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rETHCurveUniv3Callee.sol | 25 ++++++++++++++++--------- src/test/rETHCurveUniv3Callee.t.sol | 18 +++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/rETHCurveUniv3Callee.sol b/src/rETHCurveUniv3Callee.sol index df60d49..f50b2f7 100644 --- a/src/rETHCurveUniv3Callee.sol +++ b/src/rETHCurveUniv3Callee.sol @@ -70,9 +70,8 @@ interface UniV3RouterLike { contract rETHCurveUniv3Callee { - address public constant rocketToLido = 0x447Ddd4960d9fdBF6af9a790560d0AF76795CB08; - address public constant lidoToETH = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; - + CurvePoolLike public immutable rocketToLido; + CurvePoolLike public immutable lidoToETH; UniV3RouterLike public immutable uniV3Router; DaiJoinLike public immutable daiJoin; TokenLike public immutable dai; @@ -90,7 +89,15 @@ contract rETHCurveUniv3Callee { z = _add(x, _sub(y, 1)) / y; } - constructor(address uniV3Router_, address daiJoin_, address weth_) public { + constructor( + address rocketToLido_, + address lidoToETH_, + address uniV3Router_, + address daiJoin_, + address weth_ + ) public { + rocketToLido = CurvePoolLike(rocketToLido_); + lidoToETH = CurvePoolLike(lidoToETH_); uniV3Router = UniV3RouterLike(uniV3Router_); daiJoin = DaiJoinLike(daiJoin_); TokenLike dai_ = DaiJoinLike(daiJoin_).dai(); @@ -133,22 +140,22 @@ contract rETHCurveUniv3Callee { } // rETH -> wstETH - TokenLike(gem).approve(rocketToLido, slice); - slice = CurvePoolLike(rocketToLido).exchange({ + TokenLike(gem).approve(address(rocketToLido), slice); + slice = 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); + gem = 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({ + TokenLike(gem).approve(address(lidoToETH), slice); + slice = lidoToETH.exchange({ i: 1, // send token id 1 (stETH) j: 0, // receive token id 0 (ETH) dx: slice, // send `slice` amount of stETH diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index b3a1a21..714e35d 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -108,10 +108,12 @@ interface Authable { contract CurveCalleeTest is DSTest { - address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; - address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; - address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address constant hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; + address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; + address constant chainlog = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; + address constant rocketToLido = 0x447Ddd4960d9fdBF6af9a790560d0AF76795CB08; + address constant lidoToETH = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; + address constant uniV3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564; uint256 constant WAD = 1e18; uint256 constant RAY = 1e27; @@ -197,7 +199,13 @@ contract CurveCalleeTest is DSTest { weth = Chainlog(chainlog).getAddress("ETH"); dai = Chainlog(chainlog).getAddress("MCD_DAI"); usdc = Chainlog(chainlog).getAddress("USDC"); - callee = new rETHCurveUniv3Callee(uniV3, daiJoin, weth); + callee = new rETHCurveUniv3Callee( + rocketToLido, + lidoToETH, + uniV3, + daiJoin, + weth + ); vat = Chainlog(chainlog).getAddress("MCD_VAT"); Vat(vat).hope(clipper); tail = ClipperLike(clipper).tail(); From 3728627c8870f8e69befb7f7b7f8aaf572eb4c64 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 4 Aug 2022 14:19:06 +0200 Subject: [PATCH 29/32] refactor: remove nbsp chars --- src/test/Simulation.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/Simulation.t.sol b/src/test/Simulation.t.sol index b0d8102..0485fc7 100644 --- a/src/test/Simulation.t.sol +++ b/src/test/Simulation.t.sol @@ -762,7 +762,7 @@ contract SimulationTests is DSTest { auctionId = lpDaiEthClip.kicks(); } - function testBarkLpDaiEth() private { // most LP tokens are getting offboarded + function testBarkLpDaiEth() private { // most LP tokens are getting offboarded (,,,, uint256 dustRad) = vat.ilks(lpDaiEthName); uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2; uint256 kicksPre = lpDaiEthClip.kicks(); @@ -971,7 +971,7 @@ contract SimulationTests is DSTest { assertLt(dai.balanceOf(bobAddr), amount * auctionPrice / RAY / 5); } - function testTakeLpDaiEthProfit() private { // most LP tokens are getting offboarded + function testTakeLpDaiEthProfit() private { // most LP tokens are getting offboarded uint256 minProfitPct = 30; (,,,, uint256 dustRad) = vat.ilks(lpDaiEthName); uint256 amount = (dustRad / getLpDaiEthPriceRay()) * 2; From 17c783bfd4d89649365a6330994b4e2681ca4587 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 4 Aug 2022 14:46:42 +0200 Subject: [PATCH 30/32] =?UTF-8?q?refactor:=C2=A0make=20code=20more=20simil?= =?UTF-8?q?ar=20to=20WstETH?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rETHCurveUniv3Callee.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rETHCurveUniv3Callee.sol b/src/rETHCurveUniv3Callee.sol index f50b2f7..67024a9 100644 --- a/src/rETHCurveUniv3Callee.sol +++ b/src/rETHCurveUniv3Callee.sol @@ -163,10 +163,10 @@ contract rETHCurveUniv3Callee { }); // ETH -> wETH - WethLike(weth).deposit{ + gem = weth; + WethLike(gem).deposit{ value: slice }(); - gem = weth; // Approve uniV3 to take gem WethLike(gem).approve(address(uniV3Router), slice); From bca2ab6c6ea30cc88724b099afd55b7b56ad7e0e Mon Sep 17 00:00:00 2001 From: Tal Date: Thu, 22 Sep 2022 13:26:33 +0300 Subject: [PATCH 31/32] ttake 1K reth intead of 3K in est_bigAmtWithComplexPath --- src/test/rETHCurveUniv3Callee.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rETHCurveUniv3Callee.t.sol b/src/test/rETHCurveUniv3Callee.t.sol index 714e35d..a45370a 100644 --- a/src/test/rETHCurveUniv3Callee.t.sol +++ b/src/test/rETHCurveUniv3Callee.t.sol @@ -268,7 +268,7 @@ contract CurveCalleeTest is DSTest { } function test_bigAmtWithComplexPath() public { - uint256 amt = 3000 * WAD; + uint256 amt = 1000 * WAD; newAuction(amt); uint24 poolAFee = 500; uint24 poolBFee = 100; From d5051890e35e12c9946feb02eb8e0e0c01d596e3 Mon Sep 17 00:00:00 2001 From: Tal Date: Thu, 22 Sep 2022 14:07:56 +0300 Subject: [PATCH 32/32] Add reth deployment data --- addresses.json | 3 ++- scripts/deploy-mainnet.sh | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/addresses.json b/addresses.json index 60bc828..e7605b9 100644 --- a/addresses.json +++ b/addresses.json @@ -4,7 +4,8 @@ "UniswapV2LpTokenCalleeDai": "0x74893C37beACf205507ea794470b13DE06294220", "UniswapV3Callee": "0xdB9C76109d102d2A1E645dCa3a7E671EBfd8e11A", "WstETHCurveUniv3Callee": "0xC2D837173592194131827775a6Cd88322a98C825", - "CurveLpTokenUniv3Callee": "0x71f2198849F3B1372EA90c079BD634928583f2d2" + "CurveLpTokenUniv3Callee": "0x71f2198849F3B1372EA90c079BD634928583f2d2", + "rETHCurveUniv3Callee": "0x7cdAb0fE16efb1EFE89e53B141347D7F299d6610" }, "0x5": { "UniswapV2CalleeDai": "0x6d9139ac89ad2263f138633de20e47bcae253938", diff --git a/scripts/deploy-mainnet.sh b/scripts/deploy-mainnet.sh index 1a2c049..dc388fd 100755 --- a/scripts/deploy-mainnet.sh +++ b/scripts/deploy-mainnet.sh @@ -23,10 +23,18 @@ CurveLpTokenUniv3Callee=$(dapp create CurveLpTokenUniv3Callee \ 0x9759A6Ac90977b93B58547b4A71c78317f391A28 \ 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) +rETHCurveUniv3Callee=$(dapp create rETHCurveUniv3Callee \ + 0x447Ddd4960d9fdBF6af9a790560d0AF76795CB08 \ + 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022 \ + 0xE592427A0AEce92De3Edee1F18E0157C05861564 \ + 0x9759A6Ac90977b93B58547b4A71c78317f391A28 \ + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) + echo "UniswapV2CalleeDai: ${UniswapV2CalleeDai}" echo "UniswapV2LpTokenCalleeDai: ${UniswapV2LpTokenCalleeDai}" echo "UniswapV3Callee: ${UniswapV3Callee}" echo "WstETHCurveUniv3Callee: ${WstETHCurveUniv3Callee}" echo "CurveLpTokenUniv3Callee: ${CurveLpTokenUniv3Callee}" +echo "rETHCurveUniv3Callee: ${rETHCurveUniv3Callee}" echo "" echo "NOTE: update this repo's addresses.json file with the new addresses."