diff --git a/contracts/mocks/tests/BorrowRepayTestMock.sol b/contracts/mocks/tests/BorrowRepayTestMock.sol new file mode 100644 index 00000000..dda8a428 --- /dev/null +++ b/contracts/mocks/tests/BorrowRepayTestMock.sol @@ -0,0 +1,51 @@ +pragma solidity 0.6.12; + +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {MintableERC20} from '../tokens/MintableERC20.sol'; + +contract BorrowRepayTestMock { + ILendingPool _pool; + address _weth; + address _dai; + + constructor(ILendingPool pool, address weth, address dai) public { + _pool = pool; + _weth = weth; + _dai = dai; + } + + function executeBorrowRepayVariable() external { + //mints 1 eth + MintableERC20(_weth).mint(1e18); + //deposits weth in the protocol + MintableERC20(_weth).approve(address(_pool),type(uint256).max); + _pool.deposit(_weth, 1e18, address(this),0); + //borrow 1 wei of weth at variable + _pool.borrow(_weth, 1, 2, 0, address(this)); + //repay 1 wei of weth (expected to fail) + _pool.repay(_weth, 1, 2, address(this)); + } + + function executeBorrowRepayStable() external { + //mints 1 eth + MintableERC20(_weth).mint(1e18); + //mints 1 dai + MintableERC20(_dai).mint(1e18); + //deposits weth in the protocol + MintableERC20(_weth).approve(address(_pool),type(uint256).max); + _pool.deposit(_weth, 1e18, address(this),0); + + //deposits dai in the protocol + MintableERC20(_dai).approve(address(_pool),type(uint256).max); + _pool.deposit(_dai, 1e18, address(this),0); + + //disabling dai as collateral so it can be borrowed at stable + _pool.setUserUseReserveAsCollateral(_dai, false); + //borrow 1 wei of dai at stable + _pool.borrow(_dai, 1, 1, 0, address(this)); + + //repay 1 wei of dai (expected to fail) + _pool.repay(_dai, 1, 1, address(this)); + } + +} diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 66d8b8c0..d9a11b7c 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -113,7 +113,7 @@ library Errors { string public constant RL_STABLE_DEBT_NOT_ZERO = '89'; string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90'; string public constant LP_CALLER_NOT_EOA = '91'; - string public constant VL_SAME_BLOCK_BORROW = '94'; + string public constant VL_SAME_BLOCK_BORROW_REPAY = '94'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 56a3c777..9c7ea35a 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -272,7 +272,7 @@ library ValidationLogic { require( lastBorrower != onBehalfOf || lastBorrowTimestamp != uint40(block.timestamp), - Errors.VL_SAME_BLOCK_BORROW + Errors.VL_SAME_BLOCK_BORROW_REPAY ); require( diff --git a/helpers/types.ts b/helpers/types.ts index 157f34ba..3c7af8c3 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -187,7 +187,7 @@ export enum ProtocolErrors { RL_ATOKEN_SUPPLY_NOT_ZERO = '88', RL_STABLE_DEBT_NOT_ZERO = '89', RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90', - + VL_SAME_BLOCK_BORROW_REPAY = '94', // old INVALID_FROM_BALANCE_AFTER_TRANSFER = 'Invalid from balance after transfer', diff --git a/test-suites/test-aave/borrow-repay-same-tx.ts b/test-suites/test-aave/borrow-repay-same-tx.ts new file mode 100644 index 00000000..d1fed4f0 --- /dev/null +++ b/test-suites/test-aave/borrow-repay-same-tx.ts @@ -0,0 +1,52 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { + APPROVAL_AMOUNT_LENDING_POOL, + MAX_UINT_AMOUNT, + RAY, + MAX_BORROW_CAP, + MAX_SUPPLY_CAP, +} from '../../helpers/constants'; +import { ProtocolErrors } from '../../helpers/types'; +import { + BorrowRepayTestMock, + BorrowRepayTestMockFactory, + MintableERC20, + WETH9, + WETH9Mocked, +} from '../../types'; +import { parseEther } from '@ethersproject/units'; +import { BigNumber } from '@ethersproject/bignumber'; +import { waitForTx } from '../../helpers/misc-utils'; +import { getFirstSigner } from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('Borrow/repay in the same tx', (testEnv: TestEnv) => { + const { VL_SAME_BLOCK_BORROW_REPAY } = ProtocolErrors; + const unitParse = async (token: WETH9Mocked | MintableERC20, nb: string) => + BigNumber.from(nb).mul(BigNumber.from('10').pow((await token.decimals()) - 3)); + + let testContract: BorrowRepayTestMock; + + it('Deploys the test contract', async () => { + const { weth, dai, pool } = testEnv; + + testContract = await ( + await new BorrowRepayTestMockFactory(await getFirstSigner()) + ).deploy(pool.address, weth.address, dai.address); + }); + + it('Executes a test borrow/repay in the same transaction at variable (revert expected)', async () => { + await expect(testContract.executeBorrowRepayVariable()).to.be.revertedWith( + VL_SAME_BLOCK_BORROW_REPAY, + 'Borrow/repay in the same transaction did not revert as expected' + ); + }); + + it('Executes a test borrow/repay in the same transaction at stabke (revert expected)', async () => { + await expect(testContract.executeBorrowRepayStable()).to.be.revertedWith( + VL_SAME_BLOCK_BORROW_REPAY, + 'Borrow/repay in the same transaction did not revert as expected' + ); + }); +});