Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Borrow/repay check removal #705

Merged
merged 3 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion contracts/protocol/libraries/helpers/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ library Errors {
string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
string public constant SAME_BLOCK_BORROW_REPAY = '48'; // 'Borrow and repay in same block is not allowed'
string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
Expand Down
14 changes: 0 additions & 14 deletions contracts/protocol/libraries/logic/ValidationLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -327,20 +327,6 @@ library ValidationLogic {
require(isActive, Errors.RESERVE_INACTIVE);
require(!isPaused, Errors.RESERVE_PAUSED);

uint256 variableDebtPreviousIndex = IScaledBalanceToken(reserveCache.variableDebtTokenAddress)
.getPreviousIndex(onBehalfOf);

uint40 stableRatePreviousTimestamp = IStableDebtToken(reserveCache.stableDebtTokenAddress)
.getUserLastUpdated(onBehalfOf);

require(
(stableRatePreviousTimestamp < uint40(block.timestamp) &&
interestRateMode == DataTypes.InterestRateMode.STABLE) ||
(variableDebtPreviousIndex < reserveCache.nextVariableBorrowIndex &&
interestRateMode == DataTypes.InterestRateMode.VARIABLE),
Errors.SAME_BLOCK_BORROW_REPAY
);

require(
(stableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.STABLE) ||
(variableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.VARIABLE),
Expand Down
1 change: 0 additions & 1 deletion helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ export enum ProtocolErrors {
HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45', // 'Health factor is not below the threshold'
COLLATERAL_CANNOT_BE_LIQUIDATED = '46', // 'The collateral chosen cannot be liquidated'
SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47', // 'User did not borrow the specified currency'
SAME_BLOCK_BORROW_REPAY = '48', // 'Borrow and repay in same block is not allowed'
INCONSISTENT_FLASHLOAN_PARAMS = '49', // 'Inconsistent flashloan parameters'
BORROW_CAP_EXCEEDED = '50', // 'Borrow cap is exceeded'
SUPPLY_CAP_EXCEEDED = '51', // 'Supply cap is exceeded'
Expand Down
172 changes: 141 additions & 31 deletions test-suites/stable-debt-token.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { expect } from 'chai';
import { BigNumber, utils } from 'ethers';
import { ProtocolErrors, RateMode } from '../helpers/types';
import { MAX_UINT_AMOUNT, RAY, ZERO_ADDRESS } from '../helpers/constants';
import { impersonateAccountsHardhat, setAutomine } from '../helpers/misc-utils';
import { makeSuite, TestEnv } from './helpers/make-suite';
import { topUpNonPayableWithEther } from './helpers/utils/funds';
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { evmRevert, evmSnapshot, increaseTime, waitForTx } from '@aave/deploy-v3';
import { StableDebtToken__factory } from '../types';
import {expect} from 'chai';
import {BigNumber, utils} from 'ethers';
import {ProtocolErrors, RateMode} from '../helpers/types';
import {MAX_UINT_AMOUNT, RAY, ZERO_ADDRESS} from '../helpers/constants';
import {impersonateAccountsHardhat, setAutomine, setAutomineEvm} from '../helpers/misc-utils';
import {makeSuite, TestEnv} from './helpers/make-suite';
import {topUpNonPayableWithEther} from './helpers/utils/funds';
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {evmRevert, evmSnapshot, getStableDebtToken, increaseTime, waitForTx} from '@aave/deploy-v3';
import {StableDebtToken__factory} from '../types';
declare var hre: HardhatRuntimeEnvironment;

makeSuite('StableDebtToken', (testEnv: TestEnv) => {
const { CALLER_MUST_BE_POOL, CALLER_NOT_POOL_ADMIN } = ProtocolErrors;
const {CALLER_MUST_BE_POOL, CALLER_NOT_POOL_ADMIN} = ProtocolErrors;

let snap: string;

before(async () => {
beforeEach(async () => {
snap = await evmSnapshot();
});
afterEach(async () => {
await evmRevert(snap);
});

it('Check initialization', async () => {
const { pool, weth, dai, helpersContract, users } = testEnv;
const {pool, weth, dai, helpersContract, users} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand Down Expand Up @@ -70,7 +74,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to mint not being the Pool (revert expected)', async () => {
const { deployer, dai, helpersContract } = testEnv;
const {deployer, dai, helpersContract} = testEnv;

const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
Expand All @@ -86,7 +90,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to burn not being the Pool (revert expected)', async () => {
const { deployer, dai, helpersContract } = testEnv;
const {deployer, dai, helpersContract} = testEnv;

const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
Expand All @@ -105,7 +109,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to transfer debt tokens (revert expected)', async () => {
const { users, dai, helpersContract } = testEnv;
const {users, dai, helpersContract} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand All @@ -119,7 +123,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Check Mint and Transfer events when borrowing on behalf', async () => {
const snapId = await evmSnapshot();
// const snapId = await evmSnapshot();
const {
pool,
weth,
Expand Down Expand Up @@ -191,14 +195,14 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
);

const rawTransferEvents = tx.logs.filter(
({ topics, address }) => topics[0] === transferEventSig && address == stableDebtToken.address
({topics, address}) => topics[0] === transferEventSig && address == stableDebtToken.address
);
const transferAmount = stableDebtToken.interface.parseLog(rawTransferEvents[0]).args.value;

const rawMintEvents = tx.logs.filter(
({ topics, address }) => topics[0] === mintEventSig && address == stableDebtToken.address
({topics, address}) => topics[0] === mintEventSig && address == stableDebtToken.address
);
const { amount: mintAmount, balanceIncrease } = stableDebtToken.interface.parseLog(
const {amount: mintAmount, balanceIncrease} = stableDebtToken.interface.parseLog(
rawMintEvents[0]
).args;

Expand All @@ -207,11 +211,11 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
expect(expectedDebtIncreaseUser1).to.be.eq(balanceIncrease);
expect(afterDebtBalanceUser2).to.be.eq(beforeDebtBalanceUser2);

await evmRevert(snapId);
// await evmRevert(snapId);
});

it('Tries to approve debt tokens (revert expected)', async () => {
const { users, dai, helpersContract } = testEnv;
const {users, dai, helpersContract} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand All @@ -228,7 +232,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to increase allowance of debt tokens (revert expected)', async () => {
const { users, dai, helpersContract } = testEnv;
const {users, dai, helpersContract} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand All @@ -242,7 +246,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to decrease allowance of debt tokens (revert expected)', async () => {
const { users, dai, helpersContract } = testEnv;
const {users, dai, helpersContract} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand All @@ -256,7 +260,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('Tries to transferFrom (revert expected)', async () => {
const { users, dai, helpersContract } = testEnv;
const {users, dai, helpersContract} = testEnv;
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
.stableDebtTokenAddress;
const stableDebtContract = StableDebtToken__factory.connect(
Expand All @@ -280,13 +284,13 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
// progress time by a year, to accrue significant debt.
// then let user 2 withdraw sufficient funds such that secondTerm (userStableRate * burnAmount) >= averageRate * supply
// if we do not have user 1 deposit as well, we will have issues getting past previousSupply <= amount, as amount > supply for secondTerm to be > firstTerm.
await evmRevert(snap);
// await evmRevert(snap);
const rateGuess1 = BigNumber.from(RAY);
const rateGuess2 = BigNumber.from(10).pow(30);
const amount1 = BigNumber.from(2);
const amount2 = BigNumber.from(1);

const { deployer, pool, dai, helpersContract, users } = testEnv;
const {deployer, pool, dai, helpersContract, users} = testEnv;

// Impersonate the Pool
await topUpNonPayableWithEther(deployer.signer, [pool.address], utils.parseEther('1'));
Expand Down Expand Up @@ -316,8 +320,8 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
});

it('setIncentivesController() ', async () => {
const snapshot = await evmSnapshot();
const { dai, helpersContract, poolAdmin, aclManager, deployer } = testEnv;
// const snapshot = await evmSnapshot();
const {dai, helpersContract, poolAdmin, aclManager, deployer} = testEnv;
const config = await helpersContract.getReserveTokensAddresses(dai.address);
const stableDebt = StableDebtToken__factory.connect(
config.stableDebtTokenAddress,
Expand All @@ -330,7 +334,7 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
expect(await stableDebt.connect(poolAdmin.signer).setIncentivesController(ZERO_ADDRESS));
expect(await stableDebt.getIncentivesController()).to.be.eq(ZERO_ADDRESS);

await evmRevert(snapshot);
// await evmRevert(snapshot);
});

it('setIncentivesController() from not pool admin (revert expected)', async () => {
Expand All @@ -348,4 +352,110 @@ makeSuite('StableDebtToken', (testEnv: TestEnv) => {
stableDebt.connect(user.signer).setIncentivesController(ZERO_ADDRESS)
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
});

it('User borrows and repays in same block with zero fees', async () => {
const {pool, users, dai, aDai, usdc, stableDebtDai} = testEnv;
const user = users[0];

// We need some debt.
await usdc.connect(user.signer)['mint(uint256)'](utils.parseEther('2000'));
await usdc.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT);
await pool
.connect(user.signer)
.deposit(usdc.address, utils.parseEther('2000'), user.address, 0);
await dai.connect(user.signer)['mint(uint256)'](utils.parseEther('2000'));
await dai.connect(user.signer).transfer(aDai.address, utils.parseEther('2000'));
await dai.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT);

const userDataBefore = await pool.getUserAccountData(user.address);
expect(await stableDebtDai.balanceOf(user.address)).to.be.eq(0);

// Turn off automining - pretty sure that coverage is getting messed up here.
await setAutomine(false);
// Borrow 500 dai
await pool
.connect(user.signer)
.borrow(dai.address, utils.parseEther('500'), RateMode.Stable, 0, user.address);

// Turn on automining, but not mine a new block until next tx
await setAutomineEvm(true);
expect(
await pool
.connect(user.signer)
.repay(dai.address, utils.parseEther('500'), RateMode.Stable, user.address)
);

expect(await stableDebtDai.balanceOf(user.address)).to.be.eq(0);
expect(await dai.balanceOf(user.address)).to.be.eq(0);
expect(await dai.balanceOf(aDai.address)).to.be.eq(utils.parseEther('2000'));

const userDataAfter = await pool.getUserAccountData(user.address);
expect(userDataBefore.totalCollateralBase).to.be.lte(userDataAfter.totalCollateralBase);
expect(userDataBefore.healthFactor).to.be.lte(userDataAfter.healthFactor);
expect(userDataBefore.totalDebtBase).to.be.eq(userDataAfter.totalDebtBase);
});

it('User borrows and repays in same block using credit delegation with zero fees', async () => {
const {
pool,
dai,
aDai,
weth,
users: [user1, user2, user3],
} = testEnv;

// Add liquidity
await dai.connect(user3.signer)['mint(uint256)'](utils.parseUnits('1000', 18));
await dai.connect(user3.signer).approve(pool.address, MAX_UINT_AMOUNT);
await pool
.connect(user3.signer)
.supply(dai.address, utils.parseUnits('1000', 18), user3.address, 0);

// User1 supplies 10 WETH
await dai.connect(user1.signer)['mint(uint256)'](utils.parseUnits('100', 18));
await dai.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT);
await weth.connect(user1.signer)['mint(uint256)'](utils.parseUnits('10', 18));
await weth.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT);
await pool
.connect(user1.signer)
.supply(weth.address, utils.parseUnits('10', 18), user1.address, 0);

const daiData = await pool.getReserveData(dai.address);
const variableDebtToken = await getStableDebtToken(daiData.variableDebtTokenAddress);

// User1 approves User2 to borrow 1000 DAI
expect(
await variableDebtToken
.connect(user1.signer)
.approveDelegation(user2.address, utils.parseUnits('1000', 18))
);

const userDataBefore = await pool.getUserAccountData(user1.address);

// Turn off automining to simulate actions in same block
await setAutomine(false);

// User2 borrows 2 DAI on behalf of User1
await pool
.connect(user2.signer)
.borrow(dai.address, utils.parseEther('2'), RateMode.Stable, 0, user1.address);

// Turn on automining, but not mine a new block until next tx
await setAutomineEvm(true);

expect(
await pool
.connect(user1.signer)
.repay(dai.address, utils.parseEther('2'), RateMode.Stable, user1.address)
);

expect(await variableDebtToken.balanceOf(user1.address)).to.be.eq(0);
expect(await dai.balanceOf(user2.address)).to.be.eq(utils.parseEther('2'));
expect(await dai.balanceOf(aDai.address)).to.be.eq(utils.parseEther('1000'));

const userDataAfter = await pool.getUserAccountData(user1.address);
expect(userDataBefore.totalCollateralBase).to.be.lte(userDataAfter.totalCollateralBase);
expect(userDataBefore.healthFactor).to.be.lte(userDataAfter.healthFactor);
expect(userDataBefore.totalDebtBase).to.be.eq(userDataAfter.totalDebtBase);
});
});
Loading