diff --git a/SPEC.md b/SPEC.md index c681690..a2ba0d8 100644 --- a/SPEC.md +++ b/SPEC.md @@ -170,7 +170,7 @@ This function describes the initialization process for this contract. We set the * **WRITE IMMUTABLE** `aaveV2LendingPool = aaveV2LendingPool_` * **WRITE IMMUTABLE** `cdpManager = cdpManager_` * **WRITE IMMUTABLE** `daiJoin = daiJoin_` - * **WRITE IMMUTABLE** `dai = daiJoin_.gem()` + * **WRITE IMMUTABLE** `dai = daiJoin_.dai()` * **WRITE IMMUTABLE** `uniswapLiquidityPool = uniswapLiquidityPool_` * **WRITE IMMUTABLE** `isUniswapLiquidityPoolToken0 = uniswapLiquidityPool.token0() == baseToken` * **REQUIRE** `isUniswapLiquidityPoolToken0 || uniswapLiquidityPool.token1() == baseToken` @@ -224,7 +224,7 @@ Notes: - **BIND** `user = msg.sender` - **REQUIRE** `compoundV2Position.borrows.length == compoundV2Position.paths.length` - **REQUIRE** `aaveV2Position.borrows.length == aaveV2Position.paths.length` - - **BIND** `data = abi.encode(MigrationCallbackData{user, flashAmount, compoundV2Position, aaveV2Position, makerPositions})` + - **BIND** `data = abi.encode(MigrationCallbackData{user, flashAmount, compoundV2Position, aaveV2Position, cdpPositions})` - **CALL** `uniswapLiquidityPool.flash(address(this), isUniswapLiquidityPoolToken0 ? flashAmount : 0, isUniswapLiquidityPoolToken0 ? 0 : flashAmount, data)` - **STORE** `inMigration -= 1` @@ -273,8 +273,8 @@ This internal helper function repays the user’s borrow positions on Compound V #### Inputs - - `address user`: Alias for the `msg.sender` of the original `migrate` call - - `compoundV2Position CompoundV2Position` - Structure containing the user’s Compound V2 collateral and borrow positions to migrate to Compound III. + - `user: address`: Alias for the `msg.sender` of the original `migrate` call + - `compoundV2Position: CompoundV2Position` - Structure containing the user’s Compound V2 collateral and borrow positions to migrate to Compound III. #### Bindings @@ -315,8 +315,8 @@ This internal helper function repays the user’s borrow positions on Aave V2 (e #### Inputs - - `address user`: Alias for the `msg.sender` of the original `migrate` call - - `aaveV2Position AaveV2Position` - Structure containing the user’s Aave V2 collateral and borrow positions to migrate to Compound III. + - `user: address`: Alias for the `msg.sender` of the original `migrate` call + - `aaveV2Position: AaveV2Position` - Structure containing the user’s Aave V2 collateral and borrow positions to migrate to Compound III. #### Bindings @@ -353,41 +353,93 @@ This internal helper function repays the user’s borrow positions on Maker (exe #### Inputs - - `address user`: Alias for the `msg.sender` of the original `migrate` call - - `cdpPositions CDPPosition[]` - List of structures that each represent a single CDP’s collateral and borrow position to migrate to Compound III. + - `user: address`: Alias for the `msg.sender` of the original `migrate` call + - `cdpPositions: CDPPosition[]` - List of structures that each represent a single CDP’s collateral and borrow position to migrate to Compound III. #### Bindings * `user: address`: Alias for `msg.sender`. + * `vat: VatLike`: The address of the core vault engine for Maker. + * `cdpOwner: address`: The owner of the CDP. * `withdrawAmount: uint256`: The amount of collateral to withdraw. * `withdrawAmount18: uint256`: The amount of collateral to withdraw, scaled up to 18 decimals. * `repayAmount: uint256`: The amount to repay for each borrow position. + * `dart: int256`: The normalized amount to decrease a CDP’s debt by. * `underlyingDebt: IERC20` - The underlying asset of an Aave debt token. * `underlyingCollateral: IERC20` - The underlying asset of an Aave aToken. No special handling needed for ETH because Aave v2 uses WETH. #### Function Spec `function migrateCDPPositions(address user, CDPPosition[] positions) internal` + - **BIND READ** `vat = cdpManager.vat()` - **FOREACH** `(cdpId, borrowAmount, collateralAmount, path, gemJoin): CDPPosition` in `positions`: - - **WHEN** `borrowAmount == type(uint256).max) || collateralAmount == type(uint256).max`: - - **BIND READ** `(withdrawAmount18, repayAmount) = cdpManager.vat().urns(cdpManager.ilks(cdpId), cdpManager.urns(cdpId))` - - **BIND** `withdrawAmount = withdrawAmount18 / (10 ** (18 - gemJoin.dec()))` - - **WHEN** `borrowAmount != type(uint256).max` + **BIND READ** `cdpOwner = cdpManager.owns(cdpId)` + **WHEN** `cdpOwner != user && cdpManager.cdpCan(cdpOwner, cdpId, user) == 0`: + - **REVERT** `UnauthorizedCDP(cdpId)` + - **WHEN** `borrowAmount == type(uint256).max`: + - **BIND READ** `repayAmount = getVaultDebt(vat, ilk, urn)` + - **BIND READ** `(, dart) = ​​-vat.urns(ilk, urn)` + – **ELSE** - **BIND** `repayAmount = borrowAmount` - - **WHEN** `collateralAmount != type(uint256).max` + - **BIND READ** `dart = getWipeDart(vat, repayAmount, urn, ilk)` + - **WHEN** `collateralAmount == type(uint256).max`: + - **BIND READ** `(withdrawAmount18,) = vat.urns(cdpManager.ilks(cdpId), cdpManager.urns(cdpId))` + - **BIND** `withdrawAmount = withdrawAmount18 / (10 ** (18 - gemJoin.dec()))` + - **ELSE** - **BIND** `withdrawAmount = collateralAmount` - **BIND** `withdrawAmount18 = collateralAmount * (10 ** (18 - gemJoin.dec()))` - **WHEN** `path.length > 0`: - **CALL** `ISwapRouter.exactOutput(ExactOutputParams({path: path, recipient: address(this), amountOut: repayAmount, amountInMaximum: type(uint256).max})` - **CALL** `dai.approve(daiJoin, repayAmount)` - **CALL** `daiJoin.join(cdpManager.urns(cdpId), repayAmount)` - - **CALL** `cdpManager.frob(cdpId, 0, -repayAmount)` - - **CALL** `cdpManager.frob(cdpId, -withdrawAmount18, 0)` + - **CALL** `cdpManager.frob(cdpId, -withdrawAmount18, dart)` - **CALL** `cdpManager.flux(cdpId, address(this), withdrawAmount18)` - **CALL** `gemJoin.exit(address(this), withdrawAmount)` - - **BIND READ** `underlyingCollateral = gemJoin.gem()` - - **CALL** `underlyingCollateral.approve(address(comet), type(uint256).max)` - - **CALL** `comet.supplyTo(user, underlying, underlying.balanceOf(address(this)))` + - **WHEN** `withdrawAmount != 0`: + - **BIND READ** `underlyingCollateral = gemJoin.gem()` + - **CALL** `underlyingCollateral.approve(address(comet), type(uint256).max)` + - **CALL** `comet.supplyTo(user, underlyingCollateral, underlyingCollateral.balanceOf(address(this)))` + +### Get Maker CDP Debt Function + +This internal helper function calculates the total unnormalized debt remaining in a vault. + +#### Inputs + + - `vat: VatLike`: The address of the core vault engine for Maker. + - `ilk: bytes32`: The collateral type for the vault. + - `urn: address`: The address of the vault. + +#### Function Spec + +`function getVaultDebt(VatLike vat, bytes32 ilk, address urn) internal returns (uint256)` + - **BIND READ** `rate = vat.ilks(ilk)` + - **BIND READ** `art = vat.urns(ilk, urn)` + - **BIND READ** `daiInUrn = vat.dai(urn)` + - **BIND** `rad = art * rate - daiInUrn` + - **BIND** `wad = rad / 10**27` + - **BIND** `wad * 10**27 < rad ? wad + 1 : wad` + - **RETURN** `wad` + +### Get CDP Wipe Dart Function + +This internal helper function calculates the normalized amount to decrease a vault's debt by. + +#### Inputs + + - `vat: VatLike`: The address of the core vault engine for Maker. + - `amount: uint256`: The actual unnormalized amount to decrease the debt by. + - `ilk: bytes32`: The collateral type for the vault. + - `urn: address`: The address of the vault. + +#### Function Spec + +`function getVaultDebt(VatLike vat, uint256 amount, address urn, bytes32 ilk) internal returns (int256)` + - **BIND READ** `rate = vat.ilks(ilk)` + - **BIND READ** `art = vat.urns(ilk, urn)` + - **BIND** `dart = amount * 10**27 / rate` + - **BIND** `dart = dart <= art ? -dart : -art` + - **RETURN** `dart` ### Sweep Function diff --git a/script/Playground.s.sol b/script/Playground.s.sol index 29140a9..25c7ea9 100644 --- a/script/Playground.s.sol +++ b/script/Playground.s.sol @@ -65,6 +65,8 @@ contract Playground is Script, Test, MainnetConstants { cETH, weth, aaveV2LendingPool, + cdpManager, + daiJoin, pool_DAI_USDC, swapRouter, sweepee diff --git a/src/CometMigratorV2.sol b/src/CometMigratorV2.sol index b0daaa1..2cf89b4 100644 --- a/src/CometMigratorV2.sol +++ b/src/CometMigratorV2.sol @@ -9,6 +9,7 @@ import "./interfaces/AaveInterface.sol"; import "./interfaces/CTokenInterface.sol"; import "./interfaces/CometInterface.sol"; import "./interfaces/IWETH9.sol"; +import "./interfaces/MakerInterface.sol"; /** * @title Compound III Migrator v2 @@ -23,12 +24,15 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { error InvalidConfiguration(uint256 loc); error InvalidCallback(uint256 loc); error InvalidInputs(uint256 loc); + error InvalidInt256(); + error UnauthorizedCDP(uint256 cdpId); /** Events **/ event Migrated( address indexed user, CompoundV2Position compoundV2Position, AaveV2Position aaveV2Position, + CDPPosition[] cdpPositions, uint256 flashAmount, uint256 flashAmountWithFee); @@ -70,12 +74,22 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { uint256 amount; } + /// @notice Represents a CDP position on Maker to migrate. + struct CDPPosition { + uint256 cdpId; + uint256 collateralAmount; + uint256 borrowAmount; + bytes path; // empty path if no swap is required (e.g. repaying USDC borrow) + GemJoinLike gemJoin; // the adapter contract for depositing/withdrawing collateral + } + /// @notice Represents all data required to continue operation after a flash loan is initiated. struct MigrationCallbackData { address user; uint256 flashAmount; CompoundV2Position compoundV2Position; AaveV2Position aaveV2Position; + CDPPosition[] cdpPositions; } /// @notice The Comet Ethereum mainnet USDC contract @@ -102,12 +116,25 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { /// @notice The address of the Aave v2 LendingPool contract. This is the contract that all `withdraw` and `repay` transactions go through. ILendingPool public immutable aaveV2LendingPool; + /// @notice The address of the Maker CDP Manager contract. This is used to manage CDP positions owned by the user. + ManagerLike public immutable cdpManager; + + /// @notice The address of the DaiJoin contract used to deposit/withdraw DAI into the Maker system. + DaiJoinLike public immutable daiJoin; + + /// @notice The address of the DAI token. + IERC20NonStandard public immutable dai; + /// @notice Address to send swept tokens to, if for any reason they remain locked in this contract. address payable public immutable sweepee; /// @notice A reentrancy guard. uint256 public inMigration; + // Units used in Maker contracts + uint256 internal constant WAD = 10**18; + uint256 internal constant RAY = 10**27; + /** * @notice Construct a new CometMigratorV2 * @param comet_ The Comet Ethereum mainnet USDC contract. @@ -115,6 +142,8 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { * @param cETH_ The address of the `cETH` token. * @param weth_ The address of the `WETH9` token. * @param aaveV2LendingPool_ The address of the Aave v2 LendingPool contract. This is the contract that all `withdraw` and `repay` transactions go through. + * @param cdpManager_ The address of the Maker CDP Manager contract. This is used to manage CDP positions owned by the user. + * @param daiJoin_ The address of the DaiJoin contract used to deposit/withdraw DAI into the Maker system. * @param uniswapLiquidityPool_ The Uniswap pool used by this contract to source liquidity (i.e. flash loans). * @param swapRouter_ The Uniswap router for facilitating token swaps. * @param sweepee_ Sweep excess tokens to this address. @@ -125,6 +154,8 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { CTokenLike cETH_, IWETH9 weth_, ILendingPool aaveV2LendingPool_, + ManagerLike cdpManager_, + DaiJoinLike daiJoin_, IUniswapV3Pool uniswapLiquidityPool_, ISwapRouter swapRouter_, address payable sweepee_ @@ -144,6 +175,15 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { // **WRITE IMMUTABLE** `aaveV2LendingPool = aaveV2LendingPool_` aaveV2LendingPool = aaveV2LendingPool_; + // **WRITE IMMUTABLE** `cdpManager = cdpManager_` + cdpManager = cdpManager_; + + // **WRITE IMMUTABLE** `daiJoin = daiJoin_` + daiJoin = daiJoin_; + + // **WRITE IMMUTABLE** `dai = daiJoin_.dai()` + dai = IERC20NonStandard(daiJoin_.dai()); + // **WRITE IMMUTABLE** `uniswapLiquidityPool = uniswapLiquidityPool_` uniswapLiquidityPool = uniswapLiquidityPool_; @@ -169,13 +209,14 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { * @notice This is the core function of this contract, migrating a position from Compound II to Compound III. We use a flash loan from Uniswap to provide liquidity to move the position. * @param compoundV2Position Structure containing the user’s Compound II collateral and borrow positions to migrate to Compound III. See notes below. * @param aaveV2Position Structure containing the user’s Compound II collateral and borrow positions to migrate to Compound III. See notes below. + * @param cdpPositions List of structures that each represent a single CDP’s collateral and borrow position to migrate to Compound III. See notes below. * @param flashAmount Amount of base asset to borrow from the Uniswap flash loan to facilitate the migration. See notes below. * @dev **N.B.** Collateral requirements may be different in Compound II and Compound III. This may lead to a migration failing or being less collateralized after the migration. There are fees associated with the flash loan, which may affect position or cause migration to fail. * @dev Note: each `collateral` market must be supported in Compound III. * @dev Note: `collateral` amounts of 0 are strictly ignored. Collateral amounts of max uint256 are set to the user's current balance. * @dev Note: `flashAmount` is provided by the user as a hint to the Migrator to know the maximum expected cost (in terms of the base asset) of the migration. If `flashAmount` is less than the total amount needed to migrate the user’s positions, the transaction will revert. **/ - function migrate(CompoundV2Position calldata compoundV2Position, AaveV2Position calldata aaveV2Position, uint256 flashAmount) external { + function migrate(CompoundV2Position calldata compoundV2Position, AaveV2Position calldata aaveV2Position, CDPPosition[] calldata cdpPositions, uint256 flashAmount) external { // **REQUIRE** `inMigration == 0` if (inMigration != 0) { revert Reentrancy(0); @@ -202,7 +243,8 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { user: user, flashAmount: flashAmount, compoundV2Position: compoundV2Position, - aaveV2Position: aaveV2Position + aaveV2Position: aaveV2Position, + cdpPositions: cdpPositions })); // **CALL** `uniswapLiquidityPool.flash(address(this), isUniswapLiquidityPoolToken0 ? flashAmount : 0, isUniswapLiquidityPoolToken0 ? 0 : flashAmount, data)` @@ -241,6 +283,9 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { // **EXEC** `migrateAaveV2Position(user, aaveV2Position)` migrateAaveV2Position(migrationData.user, migrationData.aaveV2Position); + // **EXEC** `migrateCDPPositions(user, cdpPositions)` + migrateCDPPositions(migrationData.user, migrationData.cdpPositions); + // **CALL** `comet.withdrawFrom(user, address(this), baseToken, flashAmountWithFee - baseToken.balanceOf(address(this)))` comet.withdrawFrom(migrationData.user, address(this), address(baseToken), flashAmountWithFee - baseToken.balanceOf(address(this))); @@ -249,7 +294,7 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { doTransferOut(baseToken, address(uniswapLiquidityPool), flashAmountWithFee); // **EMIT** `Migrated(user, compoundV2Position, aaveV2Position, cdpPositions, flashAmount, flashAmountWithFee)` - emit Migrated(migrationData.user, migrationData.compoundV2Position, migrationData.aaveV2Position, migrationData.flashAmount, flashAmountWithFee); + emit Migrated(migrationData.user, migrationData.compoundV2Position, migrationData.aaveV2Position, migrationData.cdpPositions, migrationData.flashAmount, flashAmountWithFee); } /** @@ -365,17 +410,17 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { for (uint i = 0; i < position.borrows.length; i++) { AaveV2Borrow memory borrow = position.borrows[i]; uint256 repayAmount; - // **WHEN** `borrowAmount == type(uint256).max)`: + // **WHEN** `borrowAmount == type(uint256).max)`: if (borrow.amount == type(uint256).max) { // **BIND READ** `repayAmount = aDebtToken.balanceOf(user)` repayAmount = borrow.aDebtToken.balanceOf(user); } else { - // **BIND** `repayAmount = borrowAmount` + // **BIND** `repayAmount = borrowAmount` repayAmount = borrow.amount; } // **WHEN** `path.length > 0`: if (position.paths[i].length > 0) { - // **CALL** `ISwapRouter.exactOutput(ExactOutputParams({path: path, recipient: address(this), amountOut: repayAmount, amountInMaximum: type(uint256).max})` + // **CALL** `ISwapRouter.exactOutput(ExactOutputParams({path: path, recipient: address(this), amountOut: repayAmount, amountInMaximum: type(uint256).max})` uint256 amountIn = swapRouter.exactOutput( ISwapRouter.ExactOutputParams({ path: position.paths[i], @@ -429,6 +474,167 @@ contract CometMigratorV2 is IUniswapV3FlashCallback { } } + /** + * @notice This internal helper function repays the user’s borrow positions on Maker (executing swaps first if necessary) before migrating their collateral over to Compound III. + * @param user Alias for the `msg.sender` of the original `migrate` call. + * @param positions List of structures that each represent a single CDP’s collateral and borrow position to migrate to Compound III. + **/ + function migrateCDPPositions(address user, CDPPosition[] memory positions) internal { + // **BIND READ** `vat = cdpManager.vat()` + VatLike vat = cdpManager.vat(); + + // **FOREACH** `(cdpId, borrowAmount, collateralAmount, path, gemJoin): CDPPosition` in `positions`: + for (uint i = 0; i < positions.length; i++) { + CDPPosition memory position = positions[i]; + uint256 cdpId = position.cdpId; + + // **BIND READ** `cdpOwner = cdpManager.owns(cdpId)` + address cdpOwner = cdpManager.owns(cdpId); + // **WHEN** `cdpOwner != user && cdpManager.cdpCan(cdpOwner, cdpId, user) == 0`: + if (cdpOwner != user && cdpManager.cdpCan(cdpOwner, cdpId, user) == 0) { + // **REVERT** `UnauthorizedCDP(cdpId)` + revert UnauthorizedCDP(cdpId); + } + + bytes32 ilk = cdpManager.ilks(cdpId); + address urn = cdpManager.urns(cdpId); + uint256 withdrawAmount18; + uint256 withdrawAmount; + uint256 repayAmount; + int256 dart; // change in debt + + // **WHEN** `borrowAmount == type(uint256).max`: + if (position.borrowAmount == type(uint256).max) { + // **BIND** `repayAmount = getVaultDebt(vat, ilk, urn)` + repayAmount = getVaultDebt(vat, ilk, urn); + + // **BIND** `dart = -vat.urns(ilk, urn)` + (, uint256 art) = vat.urns(ilk, urn); + dart = -signed256(art); + } else { + // **BIND** `repayAmount = borrowAmount` + repayAmount = position.borrowAmount; + + // **BIND READ** `dart = getWipeDart(vat, repayAmount, urn, ilk)` + dart = getWipeDart(vat, repayAmount, urn, ilk); + } + + // **WHEN** `collateralAmount == type(uint256).max`: + if (position.collateralAmount == type(uint256).max) { + // **BIND READ** `(withdrawAmount18,) = vat.urns(cdpManager.ilks(cdpId), cdpManager.urns(cdpId))` + (withdrawAmount18,) = vat.urns(ilk, urn); + + // **BIND** `withdrawAmount = withdrawAmount18 / (10 ** (18 - gemJoin.dec()))` + withdrawAmount = withdrawAmount18 / (10 ** (18 - position.gemJoin.dec())); + } else { + // **BIND** `withdrawAmount = collateralAmount` + withdrawAmount = position.collateralAmount; + + // **BIND** `withdrawAmount18 = collateralAmount * (10 ** (18 - gemJoin.dec()))` + withdrawAmount18 = position.collateralAmount * (10 ** (18 - position.gemJoin.dec())); + } + + // **WHEN** `path.length > 0`: + if (position.path.length > 0) { + // **CALL** `ISwapRouter.exactOutput(ExactOutputParams({path: path, recipient: address(this), amountOut: repayAmount, amountInMaximum: type(uint256).max})` + uint256 amountIn = swapRouter.exactOutput( + ISwapRouter.ExactOutputParams({ + path: position.path, + recipient: address(this), + amountOut: repayAmount, + amountInMaximum: type(uint256).max, + deadline: block.timestamp + }) + ); + } + + // **CALL** `dai.approve(daiJoin, repayAmount)` + dai.approve(address(daiJoin), repayAmount); + + // **CALL** `daiJoin.join(cdpManager.urns(cdpId), repayAmount)` + daiJoin.join(cdpManager.urns(cdpId), repayAmount); + + // **CALL** `cdpManager.frob(cdpId, -withdrawAmount18, dart)` + cdpManager.frob(cdpId, -signed256(withdrawAmount18), dart); + + // **CALL** `cdpManager.flux(cdpId, address(this), withdrawAmount18)` + cdpManager.flux(cdpId, address(this), withdrawAmount18); + + // **CALL** `gemJoin.exit(address(this), withdrawAmount)` + position.gemJoin.exit(address(this), withdrawAmount); + + // **WHEN** `withdrawAmount != 0`: + if (withdrawAmount != 0) { + // **BIND READ** `underlyingCollateral = gemJoin.gem()` + IERC20NonStandard underlyingCollateral = IERC20NonStandard(position.gemJoin.gem()); + + // **CALL** `underlyingCollateral.approve(address(comet), type(uint256).max)` + underlyingCollateral.approve(address(comet), type(uint256).max); + + // **CALL** `comet.supplyTo(user, underlyingCollateral, underlyingCollateral.balanceOf(address(this))) + comet.supplyTo(user, address(underlyingCollateral), underlyingCollateral.balanceOf(address(this))); + } + } + } + + /** + * @notice Calculates the total unnormalized debt remaining in a vault. + * @param vat The address of the core vault engine for Maker. + * @param urn The address of the vault. + * @param ilk The collateral type for the vault. + * @return wad The total unnormalized debt remaining in a vault. + * @dev Note: Adapted from https://github.com/Instadapp/dsa-connectors/blob/8932e8aa5edbab7eb91ca1c00ad73f6e0062f21f/contracts/mainnet/connectors/makerdao/helpers.sol#L53 + **/ + function getVaultDebt( + VatLike vat, + bytes32 ilk, + address urn + ) internal view returns (uint wad) { + (, uint rate,,,) = vat.ilks(ilk); + (, uint art) = vat.urns(ilk, urn); + uint daiInUrn = vat.dai(urn); + + uint rad = (art * rate) - daiInUrn; + wad = rad / RAY; + wad = wad * RAY < rad ? wad + 1 : wad; + } + + /** + * @notice Calculates the normalized amount to decrease a vault's debt by. + * @param vat The address of the core vault engine for Maker. + * @param amount The actual unnormalized amount to decrease the debt by. + * @param urn The address of the vault. + * @param ilk The collateral type for the vault. + * @return dart The normalized amount to decrease a vault's debt by. + * @dev Note: Adapted from https://github.com/makerdao/dss-proxy-actions/blob/master/src/DssProxyActions.sol#L183 + **/ + function getWipeDart( + VatLike vat, + uint256 amount, + address urn, + bytes32 ilk + ) internal view returns (int256 dart) { + // Gets actual rate from the vat + (, uint256 rate,,,) = vat.ilks(ilk); + // Gets actual art value of the urn + (, uint256 art) = vat.urns(ilk, urn); + + // Uses the whole dai balance in the vat to reduce the debt + dart = signed256(amount * RAY / rate); + // Checks the calculated dart is not higher than urn.art (total debt), otherwise uses its value + dart = uint256(dart) <= art ? -dart : -signed256(art); + } + + /** + * @notice Safely converts a uint256 to a int256. + * @param n The uint256 to convert. + * @return The converted int256. + **/ + function signed256(uint256 n) internal pure returns (int256) { + if (n > uint256(type(int256).max)) revert InvalidInt256(); + return int256(n); + } + /** * @notice Similar to ERC-20 transfer, except it also properly handles `transfer` from non-standard ERC-20 tokens. * @param asset The ERC-20 token to transfer out. diff --git a/src/interfaces/MakerInterface.sol b/src/interfaces/MakerInterface.sol new file mode 100644 index 0000000..576e14c --- /dev/null +++ b/src/interfaces/MakerInterface.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +interface ManagerLike { + function cdpCan(address, uint, address) external view returns (uint); + function ilks(uint) external view returns (bytes32); + function last(address) external view returns (uint); + function count(address) external view returns (uint); + function owns(uint) external view returns (address); + function urns(uint) external view returns (address); + function vat() external view returns (VatLike); + function open(bytes32, address) external returns (uint); + function cdpAllow(uint, address, uint) external; + function give(uint, address) external; + function frob(uint, int, int) external; + function flux(uint, address, uint) external; + function move(uint, address, uint) external; +} + +interface VatLike { + function can(address, address) external view returns (uint); + function ilks(bytes32) external view returns (uint, uint, uint, uint, uint); + function dai(address) external view returns (uint); + function urns(bytes32, address) external view returns (uint, uint); + function frob( + bytes32, + address, + address, + address, + int, + int + ) external; + function hope(address) external; + function move(address, address, uint) external; + function gem(bytes32, address) external view returns (uint); +} + +interface GemJoinLike { + function dec() external returns (uint); + function gem() external returns (address); + function ilk() external returns (bytes32); + function join(address, uint) external payable; + function exit(address, uint) external; +} + +interface DaiJoinLike { + function vat() external returns (VatLike); + function dai() external returns (address); + function join(address, uint) external payable; + function exit(address, uint) external; +} + +interface JugLike { + function drip(bytes32) external returns (uint256); +} diff --git a/test/CometMigratorV2.t.sol b/test/CometMigratorV2.t.sol index 1fe5303..8d4f807 100644 --- a/test/CometMigratorV2.t.sol +++ b/test/CometMigratorV2.t.sol @@ -13,6 +13,7 @@ contract CometMigratorV2Test is Positor { address indexed user, CometMigratorV2.CompoundV2Position compoundV2Position, CometMigratorV2.AaveV2Position aaveV2Position, + CometMigratorV2.CDPPosition[] cdpPositions, uint256 flashAmount, uint256 flashAmountWithFee); @@ -61,12 +62,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -118,12 +119,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); cETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cETH.balanceOf(borrower), cETHPre - migrateAmount, "Amount of cETH should have been migrated"); @@ -179,12 +180,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -234,12 +235,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 700e6, 700e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 700e6, 700e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 700e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 700e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), 0, "Amount of cUNI should have been migrated"); @@ -289,12 +290,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 700e6, 700e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 700e6, 700e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 700e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 700e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), 0, "Amount of cUNI should have been migrated"); @@ -350,12 +351,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -417,13 +418,13 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, 1200e6, 1200e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 1200e6, 1200e6 * 1.0001); vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); cETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 1200e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 1200e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - uniMigrateAmount, "Amount of cUNI should have been migrated"); @@ -478,7 +479,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), 0); comet.allow(address(migrator), true); vm.expectRevert(stdError.arithmeticError); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -531,7 +532,7 @@ contract CometMigratorV2Test is Positor { cETH.approve(address(migrator), 0); comet.allow(address(migrator), true); vm.expectRevert(CometMigratorV2.CTokenTransferFailure.selector); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cETH.balanceOf(borrower), cETHPre, "Amount of cETH should have been migrated"); @@ -584,7 +585,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CTokenLike.TransferComptrollerRejection.selector, 4)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -637,7 +638,7 @@ contract CometMigratorV2Test is Positor { cETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(CometMigratorV2.CTokenTransferFailure.selector); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cETH.balanceOf(borrower), cETHPre, "Amount of cETH should have been migrated"); @@ -688,7 +689,7 @@ contract CometMigratorV2Test is Positor { vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); vm.expectRevert(Comet.Unauthorized.selector); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 600e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 600e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -741,7 +742,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CTokenLike.TransferComptrollerRejection.selector, 4)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -794,7 +795,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.CompoundV2Error.selector, 0, 9)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 800e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 800e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -846,7 +847,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.CompoundV2Error.selector, 0, 9)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 800e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 800e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -898,7 +899,7 @@ contract CometMigratorV2Test is Positor { cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.CompoundV2Error.selector, 0, 9)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 800e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 800e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -939,7 +940,7 @@ contract CometMigratorV2Test is Positor { vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -1027,14 +1028,14 @@ contract CometMigratorV2Test is Positor { // Check event 0 vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position0, EMPTY_AAVE_V2_POSITION, 650e6, 650e6 * 1.0001); + emit Migrated(borrower, compoundV2Position0, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 650e6, 650e6 * 1.0001); // Check event 1 vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position1, EMPTY_AAVE_V2_POSITION, 550e6, 550e6 * 1.0001); + emit Migrated(borrower, compoundV2Position1, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 550e6, 550e6 * 1.0001); // Migration 0 - migrator.migrate(compoundV2Position0, EMPTY_AAVE_V2_POSITION, 650e6); + migrator.migrate(compoundV2Position0, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 650e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - uniAndethMigrateAmount0[0], "Amount of cUNI should have been migrated first"); @@ -1047,7 +1048,7 @@ contract CometMigratorV2Test is Positor { assertEq(comet.borrowBalanceOf(borrower), 650e6 * 1.0001, "v3 borrow balance"); // Migration 1 - migrator.migrate(compoundV2Position1, EMPTY_AAVE_V2_POSITION, 550e6); + migrator.migrate(compoundV2Position1, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 550e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - uniAndethMigrateAmount0[0] - uniAndethMigrateAmount1[0], "Amount of cUNI should have been migrated both"); @@ -1128,14 +1129,14 @@ contract CometMigratorV2Test is Positor { // Check event 0 vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position0, EMPTY_AAVE_V2_POSITION, 1200e6, 1200e6 * 1.0001); + emit Migrated(borrower, compoundV2Position0, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 1200e6, 1200e6 * 1.0001); // Check event 1 vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position1, EMPTY_AAVE_V2_POSITION, 200e6, 200e6 * 1.0001); + emit Migrated(borrower, compoundV2Position1, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 200e6, 200e6 * 1.0001); // Migration 0 - migrator.migrate(compoundV2Position0, EMPTY_AAVE_V2_POSITION, 1200e6); + migrator.migrate(compoundV2Position0, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 1200e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - uniMigrateAmount0, "Amount of cUNI should have been migrated first"); @@ -1148,7 +1149,7 @@ contract CometMigratorV2Test is Positor { assertEq(comet.borrowBalanceOf(borrower), 1200e6 * 1.0001, "v3 borrow balance"); // Migration 1 [No collateral moved, but still okay] - migrator.migrate(compoundV2Position1, EMPTY_AAVE_V2_POSITION, 200e6); + migrator.migrate(compoundV2Position1, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 200e6); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - uniMigrateAmount0, "Amount of cUNI should have been migrated both"); @@ -1269,6 +1270,8 @@ contract CometMigratorV2Test is Positor { cETH, weth, aaveV2LendingPool, + cdpManager, + daiJoin, pool_ETH_USDT, swapRouter, sweepee @@ -1289,7 +1292,7 @@ contract CometMigratorV2Test is Positor { paths: new bytes[](0) }); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.Reentrancy.selector, 0)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); } function testInvalidCallbackZero() public { @@ -1306,7 +1309,7 @@ contract CometMigratorV2Test is Positor { paths: new bytes[](0) }); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.InvalidCallback.selector, 0)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); } function testReentrancyTwo_SweepToken() public { @@ -1323,7 +1326,7 @@ contract CometMigratorV2Test is Positor { paths: new bytes[](0) }); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.Reentrancy.selector, 2)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); } function testSweepFailure_Zero() public { @@ -1334,6 +1337,8 @@ contract CometMigratorV2Test is Positor { cETH, weth, aaveV2LendingPool, + cdpManager, + daiJoin, pool_DAI_USDC, swapRouter, payable(address(lazyToken)) @@ -1364,7 +1369,7 @@ contract CometMigratorV2Test is Positor { paths: new bytes[](0) }); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.CompoundV2Error.selector, 1, 10)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, 0e6); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, 0e6); } /* ===== Migrator V2 Specific Tests ===== */ @@ -1405,9 +1410,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 0e6); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 0e6); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), 0e8, "Amount of cUNI should have been migrated"); @@ -1457,9 +1462,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 350e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 350e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1511,9 +1516,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 360e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 360e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1566,9 +1571,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 360e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 360e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1621,9 +1626,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 1500e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 1500e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1681,9 +1686,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 750e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 750e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1734,9 +1739,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 600e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 600e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), 0e18, "Amount of cUNI should have been migrated"); @@ -1786,9 +1791,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 350e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 350e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUSDC.balanceOf(borrower), 0, "Amount of cUNI should have been migrated"); @@ -1840,9 +1845,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 360e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 360e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1902,9 +1907,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -1956,7 +1961,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.CompoundV2Error.selector, 0, 13)); // revert due to lack of USDC to repay borrow - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2014,7 +2019,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(bytes("STF")); // Uniswap SafeTransferFrom revert due to lack of USDC to complete swap - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2066,9 +2071,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 35000e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 35000e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -2119,7 +2124,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.InvalidInputs.selector, 0)); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2174,7 +2179,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(); // XXX no revert message - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2226,7 +2231,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(bytes("STF")); // slippage is too high so `flashEstimate` is not enough to cover the swap - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2279,9 +2284,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate, 500e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate, 500e6 * 1.0001); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre - migrateAmount, "Amount of cUNI should have been migrated"); @@ -2332,7 +2337,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(Comet.NotCollateralized.selector); - migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, flashEstimate); + migrator.migrate(compoundV2Position, EMPTY_AAVE_V2_POSITION, EMPTY_CDP_POSITIONS, flashEstimate); // Check v2 balances assertEq(cUNI.balanceOf(borrower), cUNIPre, "Amount of cUNI should have been migrated"); @@ -2370,12 +2375,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 0e6, 0e6); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 0e6, 0e6); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 0e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 0e6); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -2426,12 +2431,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -2484,12 +2489,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -2541,12 +2546,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -2597,12 +2602,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -2654,12 +2659,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); aWETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -2712,12 +2717,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6, 600e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6, 600e6 * 1.0001); vm.startPrank(borrower); aWETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -2769,12 +2774,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); vm.startPrank(borrower); aWETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aWETH.balanceOf(borrower), 0, "Amount of aWETH should have been migrated"); @@ -2825,12 +2830,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); vm.startPrank(borrower); aWETH.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aWETH.balanceOf(borrower), 0, "Amount of aWETH should have been migrated"); @@ -2884,12 +2889,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 610e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 610e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -2945,12 +2950,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 610e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 610e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -3005,12 +3010,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -3063,12 +3068,12 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 600e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 600e6 * 1.0001); vm.startPrank(borrower); aUNI.approve(address(migrator), type(uint256).max); comet.allow(address(migrator), true); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -3128,9 +3133,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -3192,9 +3197,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances // XXX off by 1 wei due to rounding? @@ -3255,9 +3260,9 @@ contract CometMigratorV2Test is Positor { // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate, 710e6 * 1.0001); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate, 710e6 * 1.0001); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), 0, "Amount of aUNI should have been migrated"); @@ -3310,7 +3315,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.InvalidInputs.selector, 1)); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), aUNIPre, "Amount of aUNI should have been migrated"); @@ -3365,7 +3370,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(); // XXX no revert message - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), aUNIPre, "Amount of aUNI should have been migrated"); @@ -3404,7 +3409,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(bytes("ERC20: transfer amount exceeds allowance")); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, 600e6); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, 600e6); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), aUNIPre, "Amount of aUNI should have been migrated"); @@ -3457,7 +3462,7 @@ contract CometMigratorV2Test is Positor { comet.allow(address(migrator), true); vm.expectRevert(Comet.NotCollateralized.selector); - migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, flashEstimate); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, aaveV2Position, EMPTY_CDP_POSITIONS, flashEstimate); // Check Aave v2 balances assertEq(aUNI.balanceOf(borrower), aUNIPre, "Amount of aUNI should have been migrated"); @@ -3468,9 +3473,725 @@ contract CometMigratorV2Test is Positor { assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); } + /* ===== Migrate from Maker ===== */ + + function testMigrateSingleMakerBorrow_ethAVault_migrateOnlyCollateral() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 10e18, + borrowAmount: 0e18, + path: "", + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 0e6; + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, flashEstimate); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 90e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 10e18, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_ethAVault_migrateOnlyBorrow() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Also need to supply some collateral to Comet on behalf of the borrower (after preflight checks) + deal(address(weth), address(this), 30e18); + weth.approve(address(comet), 30e18); + comet.supplyTo(borrower, address(weth), 30e18); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 0e18, + borrowAmount: 15_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 15_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 15_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 15_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 30e18, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 15_000e6 + (15_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_ethAVault_migrateSome() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 50e18, + borrowAmount: 15_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 15_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 15_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 50e18, 15_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 50e18, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 15_000e6 + (15_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_ethAVault_migrateAll() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 30_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 0e18, 0e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 100e18, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 30_000e6 + (30_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_ethAVault_migrateNone() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 0e18, + borrowAmount: 0e18, + path: "", + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 0e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, flashEstimate); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 0e18, "v3 collateral balance"); + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_non18DecimalCollateral_wbtcAVault_migrateSome() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 6e8, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_WBTC_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 3e8, + borrowAmount: 15_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_WBTC_A + }); + + uint256 flashEstimate = 15_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 15_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_WBTC_A, 3e8, 15_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(wbtc)), 3e8, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 15_000e6 + (15_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_non18DecimalCollateral_wbtcAVault_migrateAll() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 6e8, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_WBTC_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_WBTC_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 30_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_WBTC_A, 0e8, 0e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(wbtc)), 6e8, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 30_000e6 + (30_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_cdpWithUnsupportedCollateral_migrateOnlyBorrow() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 2_000_000e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_MANA_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Need to give the borrower some collateral in Comet so we can migrate only borrows + deal(address(weth), address(this), 30e18); + weth.approve(address(comet), 30e18); + comet.supplyTo(borrower, address(weth), 30e18); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 0e18, + borrowAmount: 15_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_MANA_A + }); + + uint256 flashEstimate = 15_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 15_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_MANA_A, 2_000_000e18, 15_000e18); + + // Check v3 balances + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 15_000e6 + (15_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateSingleMakerBorrow_cdpWithUnsupportedCollateral_migrateCollateralReverts() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 2_000_000e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_MANA_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_MANA_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + vm.expectRevert(Comet.BadAsset.selector); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_MANA_A, 2_000_000e18, 30_000e18); + + // Check v3 balances + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateMakerBorrow_migrateMultipleVaults() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](2); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + initialPositions[1] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 6e8, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_WBTC_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](2); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + cdpPosition[1] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[1], + collateralAmount: 3e8, + borrowAmount: 15_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_WBTC_A + }); + + uint256 flashEstimate = 45_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + cdpManager.cdpAllow(cdpIds[1], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 45_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 0e18, 0e18); + assertCdpCollateralAndDebt(cdpIds[1], join_WBTC_A, 3e8, 15_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 100e18, "v3 collateral balance"); + assertEq(comet.collateralBalanceOf(borrower, address(wbtc)), 3e8, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 45_000e6 + (45_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + + function testMigrateMakerBorrow_tooLittleDebtLeftInCdpAfterMigration() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 40e18, + borrowAmount: 16_000e18, // this should leave the CDP with 14k of debt, which is below the 15k minimum + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 16_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + vm.expectRevert(bytes("Vat/dust")); // too little debt left in CDP + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 0e18, "v3 collateral balance"); + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + // XXX Revisit this. Kind of weird that this reverts + function testMigrateSingleMakerBorrow_ethAVault_migrateAllWithoutMaxReverts() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 100e18, // XXX withdrawing all without max doesn't work due to dust + borrowAmount: 30_000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + vm.expectRevert(bytes("Vat/not-safe")); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 0e18, "v3 collateral balance"); + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateMaker_noApprovalGivenToMigrator() public { + // Posit + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + comet.allow(address(migrator), true); + + vm.expectRevert(bytes("cdp-not-allowed")); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 0e18, "v3 collateral balance"); + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateMaker_userHasNoCdpPermission() public { + // Posit + address vaultOwner = address(0x501e5F0dBe5C3aeeeC682Dcc66387211e58f3cdd); + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: vaultOwner, + positions: initialPositions + })); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + comet.allow(address(migrator), true); + + vm.expectRevert(abi.encodeWithSelector(CometMigratorV2.UnauthorizedCDP.selector, cdpIds[0])); + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 100e18, 30_000e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 0e18, "v3 collateral balance"); + assertEq(comet.borrowBalanceOf(borrower), 0e6, "v3 borrow balance"); + } + + function testMigrateMaker_nonOwnerOfCdpHasPermission() public { + // Posit + address vaultOwner = address(0x501e5F0dBe5C3aeeeC682Dcc66387211e58f3cdd); + CometMigratorV2.CDPPosition[] memory initialPositions = new CometMigratorV2.CDPPosition[](1); + initialPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: vaultOwner, + positions: initialPositions + })); + + // Vault owner grants the borrower permission to modify the vault + vm.prank(vaultOwner); + cdpManager.cdpAllow(cdpIds[0], borrower, 1); + + preflightChecks(); + + // Migrate + CometMigratorV2.CDPPosition[] memory cdpPosition = new CometMigratorV2.CDPPosition[](1); + cdpPosition[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: type(uint256).max, + borrowAmount: type(uint256).max, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + + uint256 flashEstimate = 30_500e6; // We overestimate slightly to account for slippage + vm.startPrank(borrower); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + comet.allow(address(migrator), true); + + // Check event + vm.expectEmit(true, false, false, true); + emit Migrated(borrower, EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate, 30_500e6 * 1.0001); + + migrator.migrate(EMPTY_COMPOUND_V2_POSITION, EMPTY_AAVE_V2_POSITION, cdpPosition, flashEstimate); + + // Check CDP balance + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 0e18, 0e18); + + // Check v3 balances + assertEq(comet.collateralBalanceOf(borrower, address(weth)), 100e18, "v3 collateral balance"); + // Approximate assertion because of slippage from DAI to USDC + assertApproxEqRel(comet.borrowBalanceOf(borrower), 30_000e6 + (30_500e6 * 0.0001), 0.01e18, "v3 borrow balance"); + } + /* ===== Migrate from multiple sources ===== */ - function testMigrateCompoundV2AaveV2() public { + function testMigrateCompoundV2AaveV2Maker() public { // Posit Compound v2 CometMigratorV2.CompoundV2Collateral[] memory initialCompoundCollateral = new CometMigratorV2.CompoundV2Collateral[](2); initialCompoundCollateral[0] = CometMigratorV2.CompoundV2Collateral({ @@ -3529,6 +4250,27 @@ contract CometMigratorV2Test is Positor { borrows: initialAaveBorrows })); + // Posit Maker CDP + CometMigratorV2.CDPPosition[] memory initialCdpPositions = new CometMigratorV2.CDPPosition[](2); + initialCdpPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 100e18, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_ETH_A + }); + initialCdpPositions[1] = CometMigratorV2.CDPPosition({ + cdpId: 0, // unused + collateralAmount: 6e8, + borrowAmount: 30_000e18, // minimum of 15k required for this vault + path: "", // unused + gemJoin: join_WBTC_A + }); + uint256[] memory cdpIds = positCdp(PositCdp({ + borrower: borrower, + positions: initialCdpPositions + })); + preflightChecks(); // Migrate @@ -3561,19 +4303,37 @@ contract CometMigratorV2Test is Positor { borrows: initialAaveBorrows, paths: paths }); - uint256 flashEstimate = 2110e6; // We overestimate slightly to account for slippage + CometMigratorV2.CDPPosition[] memory cdpPositions = new CometMigratorV2.CDPPosition[](2); + cdpPositions[0] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[0], + collateralAmount: 2e18, + borrowAmount: 500e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_ETH_A + }); + cdpPositions[1] = CometMigratorV2.CDPPosition({ + cdpId: cdpIds[1], + collateralAmount: 0.5e8, + borrowAmount: 1000e18, + path: swapPath(address(dai), 500, address(usdc)), + gemJoin: join_WBTC_A + }); + + uint256 flashEstimate = 3610e6; // Compound II: 3 * 350, Aave v2: 3 * 350, CDP: 500 + 1000, Buffer: 500 vm.startPrank(borrower); cUNI.approve(address(migrator), type(uint256).max); cETH.approve(address(migrator), type(uint256).max); aUNI.approve(address(migrator), type(uint256).max); aWETH.approve(address(migrator), type(uint256).max); + cdpManager.cdpAllow(cdpIds[0], address(migrator), 1); + cdpManager.cdpAllow(cdpIds[1], address(migrator), 1); comet.allow(address(migrator), true); // Check event vm.expectEmit(true, false, false, true); - emit Migrated(borrower, compoundV2Position, aaveV2Position, flashEstimate, 2110e6 * 1.0001); + emit Migrated(borrower, compoundV2Position, aaveV2Position, cdpPositions, flashEstimate, 3610e6 * 1.0001); - migrator.migrate(compoundV2Position, aaveV2Position, flashEstimate); + migrator.migrate(compoundV2Position, aaveV2Position, cdpPositions, flashEstimate); // Check Compound v2 balances assertEq(cUNI.balanceOf(borrower), 0, "Amount of cUNI should have been migrated"); @@ -3589,11 +4349,16 @@ contract CometMigratorV2Test is Positor { assertEq(variableDebtDAI.balanceOf(borrower), 0e18, "Remainder of tokens"); assertEq(variableDebtUSDT.balanceOf(borrower), 0e6, "Remainder of tokens"); + // Check Maker CDP balances + assertCdpCollateralAndDebt(cdpIds[0], join_ETH_A, 98e18, 29_500e18); + assertCdpCollateralAndDebt(cdpIds[1], join_WBTC_A, 5.5e8, 29_000e18); + // Check v3 balances assertApproxEqRel(comet.collateralBalanceOf(borrower, address(uni)), 600e18, 0.005e18, "v3 collateral balance"); - assertApproxEqRel(comet.collateralBalanceOf(borrower, address(weth)), 2e18, 0.005e18, "v3 collateral balance"); + assertApproxEqRel(comet.collateralBalanceOf(borrower, address(weth)), 4e18, 0.005e18, "v3 collateral balance"); + assertEq(comet.collateralBalanceOf(borrower, address(wbtc)), 0.5e8, "v3 collateral balance"); // Approximate assertion because of slippage from DAI to USDC - assertApproxEqRel(comet.borrowBalanceOf(borrower), 2100e6 + 2110e6 * 0.0001, 0.01e18, "v3 borrow balance"); + assertApproxEqRel(comet.borrowBalanceOf(borrower), 3600e6 + 3610e6 * 0.0001, 0.01e18, "v3 borrow balance"); } // XXX More general tests: @@ -3637,4 +4402,19 @@ contract CometMigratorV2Test is Positor { arr[1] = data1; return arr; } + + function assertCdpCollateralAndDebt(uint256 cdpId, GemJoinLike gemJoin, uint256 expectedCollateral, uint256 expectedDebt) internal { + VatLike vat = VatLike(cdpManager.vat()); + bytes32 ilk = cdpManager.ilks(cdpId); + address urn = cdpManager.urns(cdpId); + (uint256 collateral18, uint256 normalizedDebt) = vat.urns(ilk, urn); + uint256 collateral = collateral18 / (10 ** (18 - gemJoin.dec())); + uint256 rate = jug.drip(ilk); + uint256 debt = normalizedDebt * rate / RAY; + // This is needed due to lack of precision. We might need to subtract an extra wei + debt = debt > expectedDebt ? debt - 1 : debt; + + assertEq(collateral, expectedCollateral, "cdp collateral"); + assertEq(debt, expectedDebt, "cdp debt"); + } } diff --git a/test/MainnetConstantsV2.t.sol b/test/MainnetConstantsV2.t.sol index 8c402ee..6b5021f 100644 --- a/test/MainnetConstantsV2.t.sol +++ b/test/MainnetConstantsV2.t.sol @@ -14,6 +14,7 @@ contract MainnetConstants { IERC20NonStandard public constant usdt = IERC20NonStandard(0xdAC17F958D2ee523a2206206994597C13D831ec7); IERC20NonStandard public constant dai = IERC20NonStandard(0x6B175474E89094C44Da98b954EedeAC495271d0F); IERC20NonStandard public constant uni = IERC20NonStandard(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); + IERC20NonStandard public constant wbtc = IERC20NonStandard(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); CErc20 public constant cUSDC = CErc20(0x39AA39c021dfbaE8faC545936693aC917d5E7563); CErc20 public constant cUSDT = CErc20(0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9); CErc20 public constant cDAI = CErc20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); @@ -40,7 +41,14 @@ contract MainnetConstants { Comptroller public constant comptroller = Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); ISwapRouter public constant swapRouter = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); ILendingPool public constant aaveV2LendingPool = ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9); + ManagerLike public constant cdpManager = ManagerLike(0x5ef30b9986345249bc32d8928B7ee64DE9435E39); + DaiJoinLike public constant daiJoin = DaiJoinLike(0x9759A6Ac90977b93B58547b4A71c78317f391A28); + JugLike public constant jug = JugLike(0x19c0976f590D67707E62397C87829d896Dc0f1F1); + GemJoinLike public constant join_ETH_A = GemJoinLike(0x2F0b23f53734252Bda2277357e97e1517d6B042A); + GemJoinLike public constant join_WBTC_A = GemJoinLike(0xBF72Da2Bd84c5170618Fbe5914B0ECA9638d5eb5); + GemJoinLike public constant join_MANA_A = GemJoinLike(0xA6EA3b9C04b8a38Ff5e224E7c3D6937ca44C0ef9); CometMigratorV2.CompoundV2Position internal EMPTY_COMPOUND_V2_POSITION; CometMigratorV2.AaveV2Position internal EMPTY_AAVE_V2_POSITION; + CometMigratorV2.CDPPosition[] internal EMPTY_CDP_POSITIONS; } diff --git a/test/PositorV2.t.sol b/test/PositorV2.t.sol index fed2726..ec731c8 100644 --- a/test/PositorV2.t.sol +++ b/test/PositorV2.t.sol @@ -7,6 +7,9 @@ import "forge-std/Test.sol"; import "./MainnetConstantsV2.t.sol"; contract Positor is Test, MainnetConstants { + // Units used in Maker contracts + uint256 internal constant RAY = 10**27; + // XXX change name to `PositCompoundV2`? struct Posit { address borrower; @@ -20,6 +23,11 @@ contract Positor is Test, MainnetConstants { CometMigratorV2.AaveV2Borrow[] borrows; } + struct PositCdp { + address borrower; + CometMigratorV2.CDPPosition[] positions; + } + // Note: We need this because `deal` is currently incompatible with aTokens // See: https://github.com/foundry-rs/forge-std/issues/140 mapping (ATokenLike => address) aTokenHolders; @@ -42,6 +50,10 @@ contract Positor is Test, MainnetConstants { setupAaveV2MigratorBorrow(posit_.borrower, posit_.collateral, posit_.borrows); } + function positCdp(PositCdp memory posit_) public returns (uint256[] memory) { + return setupCdpBorrows(posit_.borrower, posit_.positions); + } + function setupCompoundV2MigratorBorrow(address borrower, CometMigratorV2.CompoundV2Collateral[] memory collateral, CometMigratorV2.CompoundV2Borrow[] memory borrows) internal returns (CometMigratorV2) { for (uint8 i = 0; i < collateral.length; i++) { setupCompoundV2Collateral(borrower, collateral[i].cToken, collateral[i].amount); @@ -118,6 +130,39 @@ contract Positor is Test, MainnetConstants { require(aToken.balanceOf(borrower) == amount, "invalid aToken balance"); } + function setupCdpBorrows(address borrower, CometMigratorV2.CDPPosition[] memory cdpPositions) internal returns (uint256[] memory) { + uint256[] memory cdpIds = new uint256[](cdpPositions.length); + for (uint8 i = 0; i < cdpPositions.length; i++) { + CometMigratorV2.CDPPosition memory position = cdpPositions[i]; + GemJoinLike gemJoin = position.gemJoin; + bytes32 ilk = position.gemJoin.ilk(); + VatLike vat = VatLike(cdpManager.vat()); + + // Open new CDP for borrower + uint256 cdpId = cdpManager.open(ilk, borrower); + cdpIds[i] = cdpId; + // Borrower allows this contract to manage the CDP + vm.prank(borrower); + cdpManager.cdpAllow(cdpId, address(this), 1); + // Deposit collateral and borrow DAI + IERC20NonStandard collateral = IERC20NonStandard(gemJoin.gem()); + deal(address(collateral), address(this), position.collateralAmount); + collateral.approve(address(gemJoin), position.collateralAmount); + address urn = cdpManager.urns(cdpId); + gemJoin.join(urn, position.collateralAmount); + cdpManager.frob(cdpId, int256(convertTo18(gemJoin, position.collateralAmount)), getDrawDart(vat, urn, ilk, position.borrowAmount)); + cdpManager.move(cdpId, address(this), position.borrowAmount * RAY); + vat.hope(address(daiJoin)); + daiJoin.exit(address(this), position.borrowAmount); + // Transfer borrowed DAI to borrower + dai.transfer(borrower, position.borrowAmount); + } + + require(dai.balanceOf(address(this)) == 0, "migrator should not own DAI"); + + return cdpIds; + } + function deployCometMigrator() internal returns (CometMigratorV2) { return new CometMigratorV2( comet, @@ -125,6 +170,8 @@ contract Positor is Test, MainnetConstants { cETH, weth, aaveV2LendingPool, + cdpManager, + daiJoin, pool_DAI_USDC, swapRouter, sweepee @@ -134,4 +181,34 @@ contract Positor is Test, MainnetConstants { function amountToTokens(uint256 amount, CTokenLike cToken) internal returns (uint256) { return ( 1e18 * amount ) / cToken.exchangeRateCurrent(); } + + function convertTo18(GemJoinLike gemJoin, uint256 amount) internal returns (uint256 wad) + { + // For those collaterals that have less than 18 decimals precision we need to do the conversion before + // passing to frob function + // Adapters will automatically handle the difference of precision + wad = amount * (10 ** (18 - gemJoin.dec())); + } + + // Adapted from https://github.com/makerdao/dss-proxy-actions/blob/master/src/DssProxyActions.sol#L161 + function getDrawDart( + VatLike vat, + address urn, + bytes32 ilk, + uint256 wad + ) internal returns (int256 dart) { + // Updates stability fee rate + uint256 rate = jug.drip(ilk); + + // Gets DAI balance of the urn in the vat + uint256 dai = vat.dai(urn); + + // If there was already enough DAI in the vat balance, just exits it without adding more debt + if (dai < wad * RAY) { + // Calculates the needed dart so together with the existing dai in the vat is enough to exit wad amount of DAI tokens + dart = int256(((wad * RAY) - dai) / rate); + // This is needed due to lack of precision. It might need to sum an extra dart wei (for the given DAI wad amount) + dart = uint256(dart) * rate < wad * RAY ? dart + 1 : dart; + } + } } diff --git a/test/TokensV2.t.sol b/test/TokensV2.t.sol index b8503a1..c3c7336 100644 --- a/test/TokensV2.t.sol +++ b/test/TokensV2.t.sol @@ -64,17 +64,10 @@ contract ReentrantToken is LazyToken { } function transferFrom(address, address, uint256) external override returns (bool) { - CometMigratorV2.CompoundV2Position memory compoundV2Position = CometMigratorV2.CompoundV2Position({ - collateral: new CometMigratorV2.CompoundV2Collateral[](0), - borrows: new CometMigratorV2.CompoundV2Borrow[](0), - paths: new bytes[](0) - }); - CometMigratorV2.AaveV2Position memory aaveV2Position = CometMigratorV2.AaveV2Position({ - collateral: new CometMigratorV2.AaveV2Collateral[](0), - borrows: new CometMigratorV2.AaveV2Borrow[](0), - paths: new bytes[](0) - }); - migrator.migrate(compoundV2Position, aaveV2Position, 0e6); + CometMigratorV2.CompoundV2Position memory compoundV2Position; + CometMigratorV2.AaveV2Position memory aaveV2Position; + CometMigratorV2.CDPPosition[] memory cdpPositions; + migrator.migrate(compoundV2Position, aaveV2Position, cdpPositions, 0e6); return false; } }