diff --git a/contracts/BorrowController.sol b/contracts/BorrowController.sol index 9293eb0..979b3ac 100644 --- a/contracts/BorrowController.sol +++ b/contracts/BorrowController.sol @@ -113,20 +113,20 @@ contract BorrowController is IBorrowController, Controller, IPositionLocker { Epoch expiredWith, SwapParams calldata swapParams, ERC20PermitParams calldata collateralPermitParams - ) external payable nonReentrant wrapETH { + ) external payable nonReentrant wrapETH returns (uint256 positionId) { collateralPermitParams.tryPermit(_getUnderlyingToken(collateralToken), msg.sender, address(this)); bytes memory lockData = abi.encode(collateralAmount, debtAmount, expiredWith, maxPayInterest); lockData = abi.encode(0, msg.sender, swapParams, abi.encode(collateralToken, debtToken, lockData)); bytes memory result = _loanPositionManager.lock(lockData); - uint256 positionId = abi.decode(result, (uint256)); + positionId = abi.decode(result, (uint256)); _burnAllSubstitute(collateralToken, msg.sender); _burnAllSubstitute(debtToken, msg.sender); _loanPositionManager.transferFrom(address(this), msg.sender, positionId); } - function adjustPosition( + function adjust( uint256 positionId, uint256 collateralAmount, uint256 debtAmount, diff --git a/contracts/DepositController.sol b/contracts/DepositController.sol index 52f0265..4d271a3 100644 --- a/contracts/DepositController.sol +++ b/contracts/DepositController.sol @@ -84,46 +84,35 @@ contract DepositController is IDepositController, Controller, IPositionLocker { function deposit( address asset, uint256 amount, - uint16 lockEpochs, + Epoch expiredWith, int256 minEarnInterest, ERC20PermitParams calldata tokenPermitParams - ) external payable nonReentrant wrapETH { + ) external payable nonReentrant wrapETH returns (uint256 positionId) { tokenPermitParams.tryPermit(_getUnderlyingToken(asset), msg.sender, address(this)); - bytes memory lockData = abi.encode(amount, EpochLibrary.current().add(lockEpochs - 1), -minEarnInterest); + bytes memory lockData = abi.encode(amount, expiredWith, -minEarnInterest); bytes memory result = _bondPositionManager.lock(abi.encode(0, msg.sender, abi.encode(asset, lockData))); - uint256 id = abi.decode(result, (uint256)); + positionId = abi.decode(result, (uint256)); _burnAllSubstitute(asset, msg.sender); - _bondPositionManager.transferFrom(address(this), msg.sender, id); + _bondPositionManager.transferFrom(address(this), msg.sender, positionId); } - function withdraw( + function adjust( uint256 positionId, - uint256 withdrawAmount, - int256 maxPayInterest, + uint256 amount, + Epoch expiredWith, + int256 interestThreshold, + ERC20PermitParams calldata tokenPermitParams, PermitSignature calldata positionPermitParams - ) external nonReentrant onlyPositionOwner(positionId) { + ) external payable nonReentrant wrapETH onlyPositionOwner(positionId) { positionPermitParams.tryPermit(_bondPositionManager, positionId, address(this)); BondPosition memory position = _bondPositionManager.getPosition(positionId); + tokenPermitParams.tryPermit(position.asset, msg.sender, address(this)); - bytes memory lockData = abi.encode(position.amount - withdrawAmount, position.expiredWith, maxPayInterest); + bytes memory lockData = abi.encode(amount, expiredWith, interestThreshold); _bondPositionManager.lock(abi.encode(positionId, msg.sender, lockData)); _burnAllSubstitute(position.asset, msg.sender); } - - function collect(uint256 positionId, PermitSignature calldata positionPermitParams) - external - nonReentrant - onlyPositionOwner(positionId) - { - positionPermitParams.tryPermit(_bondPositionManager, positionId, address(this)); - BondPosition memory position = _bondPositionManager.getPosition(positionId); - if (position.expiredWith >= EpochLibrary.current()) revert NotExpired(); - - _bondPositionManager.lock(abi.encode(positionId, msg.sender, abi.encode(0, position.expiredWith, 0, 0))); - - _burnAllSubstitute(position.asset, msg.sender); - } } diff --git a/contracts/interfaces/IBorrowController.sol b/contracts/interfaces/IBorrowController.sol index c9cc37a..8d8af0e 100644 --- a/contracts/interfaces/IBorrowController.sol +++ b/contracts/interfaces/IBorrowController.sol @@ -14,7 +14,6 @@ interface IBorrowController is IController { } error CollateralSwapFailed(string reason); - error InvalidDebtAmount(); function borrow( address collateralToken, @@ -25,9 +24,9 @@ interface IBorrowController is IController { Epoch expiredWith, SwapParams calldata swapParams, ERC20PermitParams calldata collateralPermitParams - ) external payable; + ) external payable returns (uint256 positionId); - function adjustPosition( + function adjust( uint256 positionId, uint256 collateralAmount, uint256 debtAmount, diff --git a/contracts/interfaces/IDepositController.sol b/contracts/interfaces/IDepositController.sol index 71704ed..df9e0c3 100644 --- a/contracts/interfaces/IDepositController.sol +++ b/contracts/interfaces/IDepositController.sol @@ -4,24 +4,23 @@ pragma solidity ^0.8.0; import {IController} from "./IController.sol"; import {ERC20PermitParams, PermitSignature} from "../libraries/PermitParams.sol"; +import {Epoch} from "../libraries/Epoch.sol"; interface IDepositController is IController { - error NotExpired(); - function deposit( address token, uint256 amount, - uint16 lockEpochs, + Epoch expiredWith, int256 minEarnInterest, ERC20PermitParams calldata tokenPermitParams - ) external payable; + ) external payable returns (uint256 positionId); - function withdraw( + function adjust( uint256 positionId, - uint256 withdrawAmount, - int256 maxPayInterest, + uint256 amount, + Epoch expiredWith, + int256 interestThreshold, + ERC20PermitParams calldata tokenPermitParams, PermitSignature calldata positionPermitParams - ) external; - - function collect(uint256 positionId, PermitSignature calldata positionPermitParams) external; + ) external payable; } diff --git a/test/foundry/integration/BorrowController.t.sol b/test/foundry/integration/BorrowController.t.sol index 7d496b2..34c44d9 100644 --- a/test/foundry/integration/BorrowController.t.sol +++ b/test/foundry/integration/BorrowController.t.sol @@ -235,7 +235,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv vm.signPermit(1, loanPositionManager, address(borrowController), positionId); IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount + 0.5 ether, @@ -275,7 +275,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv ); IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount + collateralAmount, beforeLoanPosition.debtAmount, @@ -313,7 +313,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, beforeLoanPosition.debtAmount, @@ -353,7 +353,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.startPrank(user); weth.approve(address(borrowController), maxPayInterest); - borrowController.adjustPosition{value: maxPayInterest}( + borrowController.adjust{value: maxPayInterest}( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount, @@ -390,7 +390,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount, @@ -428,7 +428,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition{value: repayAmount}( + borrowController.adjust{value: repayAmount}( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount - repayAmount, @@ -466,7 +466,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); - borrowController.adjustPosition{value: repayAmount}( + borrowController.adjust{value: repayAmount}( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount - repayAmount, @@ -571,7 +571,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv swapParams.inSubstitute = address(wausdc); vm.prank(user); - borrowController.adjustPosition{value: 0.16 ether}( + borrowController.adjust{value: 0.16 ether}( positionId, loanPosition.collateralAmount + collateralAmount, loanPosition.debtAmount + debtAmount, @@ -624,7 +624,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv vm.signPermit(1, loanPositionManager, address(borrowController), positionId); vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, debtAmount, @@ -675,7 +675,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv vm.signPermit(1, loanPositionManager, address(borrowController), positionId); vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, 0, @@ -726,7 +726,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv vm.signPermit(1, loanPositionManager, address(borrowController), positionId); vm.prank(user); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, maxDebtAmount, @@ -765,7 +765,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); vm.expectRevert(abi.encodeWithSelector(ILoanPositionManagerTypes.FullRepaymentRequired.selector)); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount, beforeLoanPosition.debtAmount + 0.5 ether, @@ -790,7 +790,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.prank(user); vm.expectRevert(abi.encodeWithSelector(ILoanPositionManagerTypes.FullRepaymentRequired.selector)); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, beforeLoanPosition.debtAmount, @@ -813,7 +813,7 @@ contract BorrowControllerIntegrationTest is Test, CloberMarketSwapCallbackReceiv IBorrowController.SwapParams memory swapParams; vm.expectRevert(abi.encodeWithSelector(IController.InvalidAccess.selector)); - borrowController.adjustPosition( + borrowController.adjust( positionId, beforeLoanPosition.collateralAmount - collateralAmount, beforeLoanPosition.debtAmount, diff --git a/test/foundry/integration/DepositController.t.sol b/test/foundry/integration/DepositController.t.sol index 5a6825b..aeff54b 100644 --- a/test/foundry/integration/DepositController.t.sol +++ b/test/foundry/integration/DepositController.t.sol @@ -182,7 +182,7 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei depositController.deposit( wausdc, amount, - 2, + EpochLibrary.current().add(1), 0, vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount - 100000) ); @@ -208,7 +208,7 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount); vm.expectRevert(abi.encodeWithSelector(IController.ControllerSlippage.selector)); vm.prank(user); - depositController.deposit(wausdc, amount, 2, int256(amount * 4 / 100), permitParams); + depositController.deposit(wausdc, amount, EpochLibrary.current().add(1), int256(amount * 4 / 100), permitParams); } function testDepositOverCloberMarket() public { @@ -217,7 +217,7 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei ERC20PermitParams memory permitParams = vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount); vm.prank(user); - depositController.deposit(wausdc, amount, 2, 0, permitParams); + depositController.deposit(wausdc, amount, EpochLibrary.current().add(1), 0, permitParams); assertEq(couponManager.balanceOf(user, couponKeys[0].toId()), 1995445908, "COUPON0_BALANCE"); assertEq(couponManager.balanceOf(user, couponKeys[1].toId()), 1995445908, "COUPON0_BALANCE"); @@ -230,7 +230,9 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei uint256 beforeBalance = user.balance; uint256 tokenId = bondPositionManager.nextId(); - depositController.deposit{value: amount}(waweth, amount, 2, 0, emptyERC20PermitParams); + depositController.deposit{value: amount}( + waweth, amount, EpochLibrary.current().add(1), 0, emptyERC20PermitParams + ); BondPosition memory position = bondPositionManager.getPosition(tokenId); @@ -251,16 +253,22 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei uint256 amount = usdc.amount(10); uint256 tokenId = bondPositionManager.nextId(); depositController.deposit( - wausdc, amount, 2, 0, vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) + wausdc, + amount, + EpochLibrary.current().add(1), + 0, + vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) ); BondPosition memory beforePosition = bondPositionManager.getPosition(tokenId); uint256 beforeBalance = usdc.balanceOf(user); - depositController.withdraw( + depositController.adjust( tokenId, amount / 2, + beforePosition.expiredWith, type(int256).max, + emptyERC20PermitParams, vm.signPermit(1, bondPositionManager, address(depositController), tokenId) ); @@ -277,7 +285,9 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei beforeBalance = usdc.balanceOf(user); beforePosition = afterPosition; - depositController.withdraw(tokenId, beforePosition.amount, type(int256).max, emptyERC721PermitParams); + depositController.adjust( + tokenId, 0, beforePosition.expiredWith, type(int256).max, emptyERC20PermitParams, emptyERC721PermitParams + ); afterPosition = bondPositionManager.getPosition(tokenId); @@ -299,16 +309,22 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei uint256 amount = usdc.amount(10); uint256 tokenId = bondPositionManager.nextId(); depositController.deposit( - wausdc, amount, 2, 0, vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) + wausdc, + amount, + EpochLibrary.current().add(1), + 0, + vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) ); BondPosition memory beforePosition = bondPositionManager.getPosition(tokenId); uint256 beforeBalance = usdc.balanceOf(user); - depositController.withdraw( + depositController.adjust( tokenId, - amount - 1, + 1, + beforePosition.expiredWith, type(int256).max, + emptyERC20PermitParams, vm.signPermit(1, bondPositionManager, address(depositController), tokenId) ); @@ -328,15 +344,19 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei vm.startPrank(user); uint256 amount = 10 ether; uint256 tokenId = bondPositionManager.nextId(); - depositController.deposit{value: amount}(waweth, amount, 2, 0, emptyERC20PermitParams); + depositController.deposit{value: amount}( + waweth, amount, EpochLibrary.current().add(1), 0, emptyERC20PermitParams + ); BondPosition memory beforePosition = bondPositionManager.getPosition(tokenId); uint256 beforeBalance = user.balance; - depositController.withdraw( + depositController.adjust( tokenId, amount / 2, + beforePosition.expiredWith, type(int256).max, + emptyERC20PermitParams, vm.signPermit(1, bondPositionManager, address(depositController), tokenId) ); @@ -353,7 +373,9 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei beforeBalance = user.balance; beforePosition = afterPosition; - depositController.withdraw(tokenId, beforePosition.amount, type(int256).max, emptyERC721PermitParams); + depositController.adjust( + tokenId, 0, beforePosition.expiredWith, type(int256).max, emptyERC20PermitParams, emptyERC721PermitParams + ); afterPosition = bondPositionManager.getPosition(tokenId); @@ -375,14 +397,25 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei uint256 amount = usdc.amount(10); uint256 tokenId = bondPositionManager.nextId(); depositController.deposit( - wausdc, amount, 1, 0, vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) + wausdc, + amount, + EpochLibrary.current(), + 0, + vm.signPermit(1, IERC20Permit(Constants.USDC), address(depositController), amount) ); vm.warp(EpochLibrary.current().add(1).startTime()); BondPosition memory beforePosition = bondPositionManager.getPosition(tokenId); uint256 beforeBalance = usdc.balanceOf(user); - depositController.collect(tokenId, vm.signPermit(1, bondPositionManager, address(depositController), tokenId)); + depositController.adjust( + tokenId, + 0, + beforePosition.expiredWith, + 0, + emptyERC20PermitParams, + vm.signPermit(1, bondPositionManager, address(depositController), tokenId) + ); BondPosition memory afterPosition = bondPositionManager.getPosition(tokenId); @@ -401,13 +434,20 @@ contract DepositControllerIntegrationTest is Test, CloberMarketSwapCallbackRecei vm.startPrank(user); uint256 amount = 10 ether; uint256 tokenId = bondPositionManager.nextId(); - depositController.deposit{value: amount}(waweth, amount, 1, 0, emptyERC20PermitParams); + depositController.deposit{value: amount}(waweth, amount, EpochLibrary.current(), 0, emptyERC20PermitParams); vm.warp(EpochLibrary.current().add(1).startTime()); BondPosition memory beforePosition = bondPositionManager.getPosition(tokenId); uint256 beforeBalance = user.balance; - depositController.collect(tokenId, vm.signPermit(1, bondPositionManager, address(depositController), tokenId)); + depositController.adjust( + tokenId, + 0, + beforePosition.expiredWith, + 0, + emptyERC20PermitParams, + vm.signPermit(1, bondPositionManager, address(depositController), tokenId) + ); BondPosition memory afterPosition = bondPositionManager.getPosition(tokenId);