mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Fixed liquidation tests
This commit is contained in:
parent
395e4aa3a7
commit
f1743a5eac
|
@ -731,6 +731,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (
|
returns (
|
||||||
|
uint256 decimals,
|
||||||
uint256 ltv,
|
uint256 ltv,
|
||||||
uint256 liquidationThreshold,
|
uint256 liquidationThreshold,
|
||||||
uint256 liquidationBonus,
|
uint256 liquidationBonus,
|
||||||
|
@ -746,6 +747,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
|
||||||
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
|
CoreLibrary.ReserveData storage reserve = reserves[_reserve];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
reserve.decimals,
|
||||||
reserve.baseLTVasCollateral,
|
reserve.baseLTVasCollateral,
|
||||||
reserve.liquidationThreshold,
|
reserve.liquidationThreshold,
|
||||||
reserve.liquidationBonus,
|
reserve.liquidationBonus,
|
||||||
|
|
|
@ -10,10 +10,11 @@ import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||||
|
|
||||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||||
import "../tokenization/AToken.sol";
|
import "../tokenization/AToken.sol";
|
||||||
|
import "../tokenization/interfaces/IStableDebtToken.sol";
|
||||||
|
import "../tokenization/interfaces/IVariableDebtToken.sol";
|
||||||
import "../libraries/CoreLibrary.sol";
|
import "../libraries/CoreLibrary.sol";
|
||||||
import "../libraries/WadRayMath.sol";
|
import "../libraries/WadRayMath.sol";
|
||||||
import "../interfaces/IPriceOracleGetter.sol";
|
import "../interfaces/IPriceOracleGetter.sol";
|
||||||
import {IFeeProvider} from "../interfaces/IFeeProvider.sol";
|
|
||||||
import "../libraries/EthAddressLib.sol";
|
import "../libraries/EthAddressLib.sol";
|
||||||
import "../libraries/GenericLogic.sol";
|
import "../libraries/GenericLogic.sol";
|
||||||
import "../libraries/UserLogic.sol";
|
import "../libraries/UserLogic.sol";
|
||||||
|
@ -120,13 +121,12 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
) external payable returns (uint256, string memory) {
|
) external payable returns (uint256, string memory) {
|
||||||
CoreLibrary.ReserveData storage principalReserve = reserves[_reserve];
|
CoreLibrary.ReserveData storage principalReserve = reserves[_reserve];
|
||||||
CoreLibrary.ReserveData storage collateralReserve = reserves[_collateral];
|
CoreLibrary.ReserveData storage collateralReserve = reserves[_collateral];
|
||||||
CoreLibrary.UserReserveData storage userCollateral = usersReserveData[msg
|
CoreLibrary.UserReserveData storage userCollateral = usersReserveData[_user][_collateral];
|
||||||
.sender][_collateral];
|
|
||||||
|
|
||||||
LiquidationCallLocalVars memory vars;
|
LiquidationCallLocalVars memory vars;
|
||||||
|
|
||||||
(, , , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
(, , , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||||
msg.sender,
|
_user,
|
||||||
reserves,
|
reserves,
|
||||||
usersReserveData,
|
usersReserveData,
|
||||||
reservesList,
|
reservesList,
|
||||||
|
@ -140,7 +140,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.userCollateralBalance = IERC20(_collateral).balanceOf(_user);
|
vars.userCollateralBalance = IERC20(collateralReserve.aTokenAddress).balanceOf(_user);
|
||||||
|
|
||||||
//if _user hasn't deposited this specific collateral, nothing can be liquidated
|
//if _user hasn't deposited this specific collateral, nothing can be liquidated
|
||||||
if (vars.userCollateralBalance == 0) {
|
if (vars.userCollateralBalance == 0) {
|
||||||
|
@ -213,6 +213,8 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Balance before liquidation is %s", IERC20(principalReserve.stableDebtTokenAddress).balanceOf(_user));
|
||||||
|
|
||||||
//TODO Burn debt tokens
|
//TODO Burn debt tokens
|
||||||
if(vars.userVariableDebt >= vars.actualAmountToLiquidate){
|
if(vars.userVariableDebt >= vars.actualAmountToLiquidate){
|
||||||
IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate);
|
IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate);
|
||||||
|
@ -222,6 +224,8 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt));
|
IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Balance after liquidation is %s", IERC20(principalReserve.stableDebtTokenAddress).balanceOf(_user));
|
||||||
|
|
||||||
vars.collateralAtoken = AToken(collateralReserve.aTokenAddress);
|
vars.collateralAtoken = AToken(collateralReserve.aTokenAddress);
|
||||||
|
|
||||||
//if liquidator reclaims the aToken, he receives the equivalent atoken amount
|
//if liquidator reclaims the aToken, he receives the equivalent atoken amount
|
||||||
|
@ -235,9 +239,13 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
//otherwise receives the underlying asset
|
//otherwise receives the underlying asset
|
||||||
//burn the equivalent amount of atoken
|
//burn the equivalent amount of atoken
|
||||||
vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate);
|
vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate);
|
||||||
|
console.log("Burned %s collateral tokens, collateral %s",vars.maxCollateralToLiquidate, _collateral);
|
||||||
|
|
||||||
IERC20(_collateral).universalTransfer(msg.sender, vars.maxCollateralToLiquidate);
|
IERC20(_collateral).universalTransfer(msg.sender, vars.maxCollateralToLiquidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Transferring principal, amount %s",vars.actualAmountToLiquidate);
|
||||||
|
|
||||||
//transfers the principal currency to the pool
|
//transfers the principal currency to the pool
|
||||||
IERC20(_reserve).universalTransferFromSenderToThis(vars.actualAmountToLiquidate, true);
|
IERC20(_reserve).universalTransferFromSenderToThis(vars.actualAmountToLiquidate, true);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {WadRayMath} from "./WadRayMath.sol";
|
||||||
|
|
||||||
import "../interfaces/IPriceOracleGetter.sol";
|
import "../interfaces/IPriceOracleGetter.sol";
|
||||||
import {IFeeProvider} from "../interfaces/IFeeProvider.sol";
|
import {IFeeProvider} from "../interfaces/IFeeProvider.sol";
|
||||||
|
import '@nomiclabs/buidler/console.sol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title GenericLogic library
|
* @title GenericLogic library
|
||||||
|
@ -236,7 +237,10 @@ library GenericLogic {
|
||||||
uint256 borrowBalanceETH,
|
uint256 borrowBalanceETH,
|
||||||
uint256 totalFeesETH,
|
uint256 totalFeesETH,
|
||||||
uint256 liquidationThreshold
|
uint256 liquidationThreshold
|
||||||
) internal pure returns (uint256) {
|
) internal view returns (uint256) {
|
||||||
|
|
||||||
|
console.log("Borrow balance ETH is %s", borrowBalanceETH);
|
||||||
|
|
||||||
if (borrowBalanceETH == 0) return uint256(-1);
|
if (borrowBalanceETH == 0) return uint256(-1);
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -4,6 +4,7 @@ pragma solidity ^0.6.8;
|
||||||
import '@openzeppelin/contracts/math/SafeMath.sol';
|
import '@openzeppelin/contracts/math/SafeMath.sol';
|
||||||
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||||
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||||
|
import '@nomiclabs/buidler/console.sol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title UniversalERC20 library
|
* @title UniversalERC20 library
|
||||||
|
@ -37,8 +38,13 @@ library UniversalERC20 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isETH(token)) {
|
if (isETH(token)) {
|
||||||
|
console.log("transfer of ETH, value %s", amount);
|
||||||
|
console.log("Balance is %s", address(this).balance);
|
||||||
|
|
||||||
(bool result, ) = payable(to).call{value: amount, gas: DEFAULT_TRANSFER_GAS}('');
|
(bool result, ) = payable(to).call{value: amount, gas: DEFAULT_TRANSFER_GAS}('');
|
||||||
|
console.log("Transfer done, result %s", result);
|
||||||
require(result, 'ETH_TRANSFER_FAILED');
|
require(result, 'ETH_TRANSFER_FAILED');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
token.safeTransfer(to, amount);
|
token.safeTransfer(to, amount);
|
||||||
}
|
}
|
||||||
|
@ -166,6 +172,6 @@ library UniversalERC20 {
|
||||||
* @return boolean
|
* @return boolean
|
||||||
**/
|
**/
|
||||||
function isETH(IERC20 token) internal pure returns (bool) {
|
function isETH(IERC20 token) internal pure returns (bool) {
|
||||||
return (address(token) == address(ZERO_ADDRESS) || address(token) == address(ETH_ADDRESS));
|
return (address(token) == address(ETH_ADDRESS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ contract AaveProtocolTestHelpers {
|
||||||
address[] memory reserves = pool.getReserves();
|
address[] memory reserves = pool.getReserves();
|
||||||
TokenData[] memory aTokens = new TokenData[](reserves.length);
|
TokenData[] memory aTokens = new TokenData[](reserves.length);
|
||||||
for (uint256 i = 0; i < reserves.length; i++) {
|
for (uint256 i = 0; i < reserves.length; i++) {
|
||||||
(,,,,address aTokenAddress,,,,,) = pool.getReserveConfigurationData(reserves[i]);
|
(,,,,,address aTokenAddress,,,,,) = pool.getReserveConfigurationData(reserves[i]);
|
||||||
aTokens[i] = TokenData({
|
aTokens[i] = TokenData({
|
||||||
symbol: AToken(aTokenAddress).symbol(),
|
symbol: AToken(aTokenAddress).symbol(),
|
||||||
tokenAddress: aTokenAddress
|
tokenAddress: aTokenAddress
|
||||||
|
|
|
@ -92,7 +92,7 @@ contract WalletBalanceProvider {
|
||||||
uint256[] memory balances = new uint256[](reserves.length);
|
uint256[] memory balances = new uint256[](reserves.length);
|
||||||
|
|
||||||
for (uint256 j = 0; j < reserves.length; j++) {
|
for (uint256 j = 0; j < reserves.length; j++) {
|
||||||
(, , , , , , , , bool isActive,) = pool.getReserveConfigurationData(reserves[j]);
|
(, , , , , , , , , bool isActive,) = pool.getReserveConfigurationData(reserves[j]);
|
||||||
|
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
balances[j] = 0;
|
balances[j] = 0;
|
||||||
|
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -2162,6 +2162,12 @@
|
||||||
"integrity": "sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA==",
|
"integrity": "sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"chai-bn": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chai-bn/-/chai-bn-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-01jt2gSXAw7UYFPT5K8d7HYjdXj2vyeIuE+0T/34FWzlNcVbs1JkPxRu7rYMfQnJhrHT8Nr6qjSf5ZwwLU2EYg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"buidler-typechain": "0.1.1",
|
"buidler-typechain": "0.1.1",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"chai-bignumber": "3.0.0",
|
"chai-bignumber": "3.0.0",
|
||||||
|
"chai-bn": "^0.2.1",
|
||||||
"ethereum-waffle": "2.5.1",
|
"ethereum-waffle": "2.5.1",
|
||||||
"ethers": "4.0.47",
|
"ethers": "4.0.47",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.2.5",
|
||||||
|
|
|
@ -270,8 +270,6 @@ const initReserves = async (
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(`Debt tokens for ${assetSymbol}: stable ${stableDebtToken.address} variable ${variableDebtToken.address}`)
|
|
||||||
|
|
||||||
if (process.env.POOL === AavePools.secondary) {
|
if (process.env.POOL === AavePools.secondary) {
|
||||||
if (assetSymbol.search("UNI") === -1) {
|
if (assetSymbol.search("UNI") === -1) {
|
||||||
assetSymbol = `Uni${assetSymbol}`;
|
assetSymbol = `Uni${assetSymbol}`;
|
||||||
|
@ -525,7 +523,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
||||||
fallbackOracle.address,
|
fallbackOracle.address,
|
||||||
]);
|
]);
|
||||||
await waitForTx(
|
await waitForTx(
|
||||||
await addressesProvider.setPriceOracle(chainlinkProxyPriceProvider.address)
|
await addressesProvider.setPriceOracle(fallbackOracle.address)
|
||||||
);
|
);
|
||||||
|
|
||||||
const lendingRateOracle = await deployLendingRateOracle();
|
const lendingRateOracle = await deployLendingRateOracle();
|
||||||
|
|
|
@ -175,7 +175,7 @@ makeSuite("AToken: Transfer", (testEnv: TestEnv) => {
|
||||||
const {users, pool, aDai, dai} = testEnv;
|
const {users, pool, aDai, dai} = testEnv;
|
||||||
await pool
|
await pool
|
||||||
.connect(users[1].signer)
|
.connect(users[1].signer)
|
||||||
.repay(MOCK_ETH_ADDRESS, MAX_UINT_AMOUNT, users[1].address, users[1].address, {
|
.repay(MOCK_ETH_ADDRESS, MAX_UINT_AMOUNT, RateMode.Stable, users[1].address, {
|
||||||
value: ethers.utils.parseEther("1"),
|
value: ethers.utils.parseEther("1"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -326,8 +326,6 @@ export const calcExpectedReserveDataAfterBorrow = (
|
||||||
): ReserveData => {
|
): ReserveData => {
|
||||||
const expectedReserveData = <ReserveData>{};
|
const expectedReserveData = <ReserveData>{};
|
||||||
|
|
||||||
console.log('Computing borrow, amountBorrowed: ', amountBorrowed, ' Rate mode: ', borrowRateMode);
|
|
||||||
|
|
||||||
expectedReserveData.address = reserveDataBeforeAction.address;
|
expectedReserveData.address = reserveDataBeforeAction.address;
|
||||||
|
|
||||||
const amountBorrowedBN = new BigNumber(amountBorrowed);
|
const amountBorrowedBN = new BigNumber(amountBorrowed);
|
||||||
|
@ -419,7 +417,6 @@ export const calcExpectedReserveDataAfterRepay = (
|
||||||
txTimestamp: BigNumber,
|
txTimestamp: BigNumber,
|
||||||
currentTimestamp: BigNumber
|
currentTimestamp: BigNumber
|
||||||
): ReserveData => {
|
): ReserveData => {
|
||||||
console.log('Computing repay, amount repaid: ', amountRepaid, ' Rate mode: ', borrowRateMode);
|
|
||||||
|
|
||||||
const expectedReserveData: ReserveData = <ReserveData>{};
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
||||||
|
|
||||||
|
@ -766,7 +763,6 @@ export const calcExpectedReserveDataAfterSwapRateMode = (
|
||||||
rateMode: string,
|
rateMode: string,
|
||||||
txTimestamp: BigNumber
|
txTimestamp: BigNumber
|
||||||
): ReserveData => {
|
): ReserveData => {
|
||||||
console.log('Computing swap, Rate mode: ', rateMode);
|
|
||||||
|
|
||||||
const expectedReserveData: ReserveData = <ReserveData>{};
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
||||||
|
|
||||||
|
@ -1246,7 +1242,7 @@ const calcExpectedVariableDebtUserIndex = (
|
||||||
return calcExpectedReserveNormalizedDebt(reserveDataBeforeAction, currentTimestamp);
|
return calcExpectedReserveNormalizedDebt(reserveDataBeforeAction, currentTimestamp);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcExpectedVariableDebtTokenBalance = (
|
export const calcExpectedVariableDebtTokenBalance = (
|
||||||
reserveDataBeforeAction: ReserveData,
|
reserveDataBeforeAction: ReserveData,
|
||||||
userDataBeforeAction: UserReserveData,
|
userDataBeforeAction: UserReserveData,
|
||||||
currentTimestamp: BigNumber
|
currentTimestamp: BigNumber
|
||||||
|
@ -1266,7 +1262,7 @@ const calcExpectedVariableDebtTokenBalance = (
|
||||||
.rayToWad();
|
.rayToWad();
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcExpectedStableDebtTokenBalance = (
|
export const calcExpectedStableDebtTokenBalance = (
|
||||||
userDataBeforeAction: UserReserveData,
|
userDataBeforeAction: UserReserveData,
|
||||||
currentTimestamp: BigNumber
|
currentTimestamp: BigNumber
|
||||||
) => {
|
) => {
|
||||||
|
|
|
@ -5,8 +5,12 @@ import {APPROVAL_AMOUNT_LENDING_POOL, MOCK_ETH_ADDRESS, oneEther} from '../helpe
|
||||||
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
|
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
|
||||||
import {makeSuite} from './helpers/make-suite';
|
import {makeSuite} from './helpers/make-suite';
|
||||||
import {ProtocolErrors, RateMode} from '../helpers/types';
|
import {ProtocolErrors, RateMode} from '../helpers/types';
|
||||||
|
import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations';
|
||||||
|
import { getUserData, getReserveData } from './helpers/utils/helpers';
|
||||||
|
|
||||||
const {expect} = require('chai');
|
const chai = require('chai');
|
||||||
|
chai.use(require('chai-bignumber')());
|
||||||
|
const {expect} = chai;
|
||||||
|
|
||||||
makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => {
|
makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => {
|
||||||
const {
|
const {
|
||||||
|
@ -42,7 +46,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
//user 2 borrows
|
//user 2 borrows
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
|
||||||
|
@ -59,7 +62,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
|
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
||||||
console.log('userGlobalDataAfter.healthFactor', userGlobalDataAfter.healthFactor.toString());
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.currentLiquidationThreshold).to.be.bignumber.equal(
|
expect(userGlobalDataAfter.currentLiquidationThreshold).to.be.bignumber.equal(
|
||||||
'80',
|
'80',
|
||||||
|
@ -78,18 +80,14 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
|
||||||
//halving the price of ETH - means doubling the DAIETH exchange rate
|
|
||||||
console.log('DAI price before', daiPrice.toString());
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
await oracle.setAssetPrice(
|
||||||
dai.address,
|
dai.address,
|
||||||
new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0)
|
new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0)
|
||||||
);
|
);
|
||||||
console.log('DAI price after', (await oracle.getAssetPrice(dai.address)).toString());
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
||||||
|
|
||||||
expect(userGlobalData.healthFactor).to.be.bignumber.lt(oneEther.toFixed(0), INVALID_HF);
|
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('LIQUIDATION - Tries to liquidate a different currency than the loan principal', async () => {
|
it('LIQUIDATION - Tries to liquidate a different currency than the loan principal', async () => {
|
||||||
|
@ -107,20 +105,17 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
).revertedWith(USER_DID_NOT_BORROW_SPECIFIED);
|
).revertedWith(USER_DID_NOT_BORROW_SPECIFIED);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(
|
it('LIQUIDATION - Tries to liquidate a different collateral than the borrower collateral', async () => {
|
||||||
'LIQUIDATION - Tries to liquidate a different ' + 'collateral than the borrower collateral',
|
|
||||||
async () => {
|
|
||||||
const {pool, dai, users} = testEnv;
|
const {pool, dai, users} = testEnv;
|
||||||
const borrower = users[1];
|
const borrower = users[1];
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true)
|
pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true)
|
||||||
).revertedWith(INVALID_COLLATERAL_TO_LIQUIDATE);
|
).revertedWith(INVALID_COLLATERAL_TO_LIQUIDATE);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
it('LIQUIDATION - Liquidates the borrow', async () => {
|
it('LIQUIDATION - Liquidates the borrow', async () => {
|
||||||
const {pool, dai, users, addressesProvider, oracle} = testEnv;
|
const {pool, dai, users, oracle} = testEnv;
|
||||||
const borrower = users[1];
|
const borrower = users[1];
|
||||||
|
|
||||||
//mints dai to the caller
|
//mints dai to the caller
|
||||||
|
@ -130,17 +125,16 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
//approve protocol to access depositor wallet
|
//approve protocol to access depositor wallet
|
||||||
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
const userReserveDataBefore = await pool.getUserReserveData(dai.address, borrower.address);
|
const daiReserveDataBefore = await getReserveData(pool, dai.address);
|
||||||
|
|
||||||
const daiReserveDataBefore = await pool.getReserveData(dai.address);
|
|
||||||
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
const userReserveDataBefore = await getUserData(pool, dai.address, borrower.address);
|
||||||
.plus(userReserveDataBefore.currentVariableDebt.toString())
|
|
||||||
|
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString())
|
||||||
.div(2)
|
.div(2)
|
||||||
.toFixed(0);
|
.toFixed(0);
|
||||||
|
|
||||||
await pool.liquidationCall(
|
const tx = await pool.liquidationCall(
|
||||||
MOCK_ETH_ADDRESS,
|
MOCK_ETH_ADDRESS,
|
||||||
dai.address,
|
dai.address,
|
||||||
borrower.address,
|
borrower.address,
|
||||||
|
@ -155,15 +149,15 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
const daiReserveDataAfter = await pool.getReserveData(dai.address);
|
const daiReserveDataAfter = await pool.getReserveData(dai.address);
|
||||||
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const feeAddress = await addressesProvider.getTokenDistributor();
|
|
||||||
|
|
||||||
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
|
|
||||||
|
|
||||||
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
|
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
|
||||||
const principalPrice = (await oracle.getAssetPrice(dai.address)).toString();
|
const principalPrice = (await oracle.getAssetPrice(dai.address)).toString();
|
||||||
|
|
||||||
const collateralDecimals = (await pool.getReserveDecimals(MOCK_ETH_ADDRESS)).toString();
|
const collateralDecimals = (
|
||||||
const principalDecimals = (await pool.getReserveDecimals(dai.address)).toString();
|
await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(dai.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
.times(new BigNumber(amountToLiquidate).times(105))
|
||||||
|
@ -171,15 +165,26 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor).to.be.bignumber.gt(
|
if(!tx.blockNumber){
|
||||||
|
expect(false, "Invalid block number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const txTimestamp = new BigNumber((await BRE.ethers.provider.getBlock(tx.blockNumber)).timestamp);
|
||||||
|
|
||||||
|
const variableDebtBeforeTx = calcExpectedVariableDebtTokenBalance(
|
||||||
|
daiReserveDataBefore,
|
||||||
|
userReserveDataBefore,
|
||||||
|
txTimestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
||||||
oneEther.toFixed(0),
|
oneEther.toFixed(0),
|
||||||
'Invalid health factor'
|
'Invalid health factor'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(feeAddressBalance).to.be.bignumber.gt('0');
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
expect(userReserveDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||||
new BigNumber(userReserveDataBefore.currentVariableDebt.toString())
|
new BigNumber(variableDebtBeforeTx)
|
||||||
.minus(amountToLiquidate)
|
.minus(amountToLiquidate)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
'Invalid user borrow balance after liquidation'
|
'Invalid user borrow balance after liquidation'
|
||||||
|
@ -199,8 +204,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'User 3 deposits 1000 USDC, user 4 1 ETH,' +
|
'User 3 deposits 1000 USDC, user 4 1 ETH, user 4 borrows - drops HF, liquidates the borrow',
|
||||||
' user 4 borrows - drops HF, liquidates the borrow',
|
|
||||||
async () => {
|
async () => {
|
||||||
const {users, pool, usdc, oracle, addressesProvider} = testEnv;
|
const {users, pool, usdc, oracle, addressesProvider} = testEnv;
|
||||||
const depositor = users[3];
|
const depositor = users[3];
|
||||||
|
@ -262,7 +266,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
||||||
.plus(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.div(2)
|
.div(2)
|
||||||
.toFixed(0);
|
.toFixed(0);
|
||||||
|
|
||||||
|
@ -281,15 +284,15 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
const usdcReserveDataAfter = await pool.getReserveData(usdc.address);
|
const usdcReserveDataAfter = await pool.getReserveData(usdc.address);
|
||||||
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const feeAddress = await addressesProvider.getTokenDistributor();
|
|
||||||
|
|
||||||
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
|
|
||||||
|
|
||||||
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
|
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
|
||||||
const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString();
|
const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString();
|
||||||
|
|
||||||
const collateralDecimals = (await pool.getReserveDecimals(MOCK_ETH_ADDRESS)).toString();
|
const collateralDecimals = (
|
||||||
const principalDecimals = (await pool.getReserveDecimals(usdc.address)).toString();
|
await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(usdc.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
.times(new BigNumber(amountToLiquidate).times(105))
|
||||||
|
@ -297,21 +300,19 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor).to.be.bignumber.gt(
|
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
||||||
oneEther.toFixed(0),
|
oneEther.toFixed(0),
|
||||||
'Invalid health factor'
|
'Invalid health factor'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(feeAddressBalance).to.be.bignumber.gt('0');
|
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
||||||
|
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
||||||
expect(userReserveDataAfter.principalBorrowBalance).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentBorrowBalance.toString())
|
|
||||||
.minus(amountToLiquidate)
|
.minus(amountToLiquidate)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
'Invalid user borrow balance after liquidation'
|
'Invalid user borrow balance after liquidation'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(usdcReserveDataAfter.availableLiquidity).to.be.bignumber.almostEqual(
|
expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
||||||
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
||||||
.plus(amountToLiquidate)
|
.plus(amountToLiquidate)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
|
||||||
import {makeSuite} from './helpers/make-suite';
|
import {makeSuite} from './helpers/make-suite';
|
||||||
import {ProtocolErrors, RateMode} from '../helpers/types';
|
import {ProtocolErrors, RateMode} from '../helpers/types';
|
||||||
import {borrow} from './helpers/actions';
|
import {borrow} from './helpers/actions';
|
||||||
|
import {calcExpectedStableDebtTokenBalance} from './helpers/utils/calculations';
|
||||||
|
import { getUserData } from './helpers/utils/helpers';
|
||||||
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
chai.use(require('chai-bignumber')());
|
chai.use(require('chai-bignumber')());
|
||||||
|
@ -115,34 +117,31 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
});
|
});
|
||||||
|
|
||||||
it('LIQUIDATION - Liquidates the borrow', async () => {
|
it('LIQUIDATION - Liquidates the borrow', async () => {
|
||||||
const {dai, users, pool, oracle, addressesProvider} = testEnv;
|
const {dai, users, pool, oracle} = testEnv;
|
||||||
const liquidator = users[3];
|
const liquidator = users[3];
|
||||||
const borrower = users[1];
|
const borrower = users[1];
|
||||||
|
|
||||||
//mints dai to the caller
|
//mints dai to the liquidator
|
||||||
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
await dai.connect(liquidator.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
//approve protocol to access the liquidator wallet
|
||||||
await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
const userReserveDataBefore: any = await pool.getUserReserveData(dai.address, borrower.address);
|
|
||||||
|
|
||||||
const daiReserveDataBefore: any = await pool.getReserveData(dai.address);
|
const daiReserveDataBefore: any = await pool.getReserveData(dai.address);
|
||||||
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance)
|
const userReserveDataBefore: any = await getUserData(pool, dai.address, borrower.address);
|
||||||
|
|
||||||
|
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
|
||||||
.div(2)
|
.div(2)
|
||||||
.toFixed(0);
|
.toFixed(0);
|
||||||
|
|
||||||
await pool.liquidationCall(
|
const tx = await pool
|
||||||
MOCK_ETH_ADDRESS,
|
.connect(liquidator.signer)
|
||||||
dai.address,
|
.liquidationCall(MOCK_ETH_ADDRESS, dai.address, borrower.address, amountToLiquidate, false);
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const userReserveDataAfter: any = await pool.getUserReserveData(dai.address, borrower.address);
|
const userReserveDataAfter: any = await getUserData(pool, dai.address, borrower.address);
|
||||||
|
|
||||||
const daiReserveDataAfter: any = await pool.getReserveData(dai.address);
|
const daiReserveDataAfter: any = await pool.getReserveData(dai.address);
|
||||||
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
@ -150,8 +149,12 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
|
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
|
||||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
|
||||||
const collateralDecimals = await pool.getReserveDecimals(MOCK_ETH_ADDRESS);
|
const collateralDecimals = (
|
||||||
const principalDecimals = await pool.getReserveDecimals(dai.address);
|
await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(dai.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
.times(new BigNumber(amountToLiquidate).times(105))
|
||||||
|
@ -162,29 +165,24 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
.div(100)
|
.div(100)
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
const expectedFeeLiquidated = new BigNumber(principalPrice.toString())
|
if (!tx.blockNumber) {
|
||||||
.times(new BigNumber(userReserveDataBefore.originationFee).times(105))
|
expect(false, 'Invalid block number');
|
||||||
.times(new BigNumber(10).pow(collateralDecimals))
|
return;
|
||||||
.div(
|
}
|
||||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
const txTimestamp = new BigNumber(
|
||||||
)
|
(await BRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
|
||||||
.div(100)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
const feeAddress = await addressesProvider.getTokenDistributor();
|
|
||||||
|
|
||||||
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq(
|
|
||||||
'0',
|
|
||||||
'Origination fee should be repaid'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(feeAddressBalance).to.be.bignumber.gt('0');
|
const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance(
|
||||||
|
userReserveDataBefore,
|
||||||
|
txTimestamp.plus(2)
|
||||||
|
);
|
||||||
|
|
||||||
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual(
|
console.log("debt: ", stableDebtBeforeTx.toFixed(), userReserveDataBefore.currentStableDebt.toFixed(), userReserveDataAfter.currentStableDebt.toString())
|
||||||
new BigNumber(userReserveDataBefore.currentBorrowBalance).minus(amountToLiquidate).toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
||||||
|
new BigNumber(stableDebtBeforeTx).minus(amountToLiquidate).toFixed(0),
|
||||||
|
'Invalid user debt after liquidation'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
||||||
|
@ -192,9 +190,10 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
'Invalid principal available liquidity'
|
'Invalid principal available liquidity'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('eth liquidity: ', daiReserveDataAfter.availableLiquidity.toString());
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity)
|
new BigNumber(ethReserveDataBefore.availableLiquidity)
|
||||||
.minus(expectedFeeLiquidated)
|
|
||||||
.minus(expectedCollateralLiquidated)
|
.minus(expectedCollateralLiquidated)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
'Invalid collateral available liquidity'
|
'Invalid collateral available liquidity'
|
||||||
|
@ -208,7 +207,6 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const borrower = users[4];
|
const borrower = users[4];
|
||||||
const liquidator = users[5];
|
const liquidator = users[5];
|
||||||
|
|
||||||
|
|
||||||
//mints USDC to depositor
|
//mints USDC to depositor
|
||||||
await usdc
|
await usdc
|
||||||
.connect(depositor.signer)
|
.connect(depositor.signer)
|
||||||
|
@ -236,32 +234,47 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
|
|
||||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
||||||
usdc.address,
|
usdc.address,
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH).div(usdcPrice.toString()).multipliedBy(0.95).toFixed(0)
|
new BigNumber(userGlobalData.availableBorrowsETH)
|
||||||
|
.div(usdcPrice.toString())
|
||||||
|
.multipliedBy(0.95)
|
||||||
|
.toFixed(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
await pool.connect(borrower.signer).borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
await pool
|
||||||
|
.connect(borrower.signer)
|
||||||
|
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
await oracle.setAssetPrice(usdc.address, new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0));
|
await oracle.setAssetPrice(
|
||||||
|
usdc.address,
|
||||||
|
new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
//mints dai to the liquidator
|
//mints dai to the liquidator
|
||||||
|
|
||||||
await usdc.connect(liquidator.signer).mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
await usdc
|
||||||
|
.connect(liquidator.signer)
|
||||||
|
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
//approve protocol to access depositor wallet
|
||||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
const userReserveDataBefore: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
const userReserveDataBefore: any = await pool.getUserReserveData(
|
||||||
|
usdc.address,
|
||||||
|
borrower.address
|
||||||
|
);
|
||||||
|
|
||||||
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
|
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
|
||||||
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance)
|
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
|
||||||
.div(2)
|
.div(2)
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
||||||
.toFixed(0);
|
.toFixed(0);
|
||||||
|
|
||||||
await pool.connect(liquidator.signer).liquidationCall(MOCK_ETH_ADDRESS, usdc.address, borrower.address, amountToLiquidate, false);
|
await pool
|
||||||
|
.connect(liquidator.signer)
|
||||||
|
.liquidationCall(MOCK_ETH_ADDRESS, usdc.address, borrower.address, amountToLiquidate, false);
|
||||||
|
|
||||||
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
||||||
|
|
||||||
|
@ -270,27 +283,18 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
|
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
|
||||||
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
||||||
|
|
||||||
const feeAddress = await addressesProvider.getTokenDistributor();
|
|
||||||
|
|
||||||
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
|
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
|
||||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
const collateralDecimals = await pool.getReserveDecimals(MOCK_ETH_ADDRESS);
|
const collateralDecimals = (await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)).decimals.toString();
|
||||||
const principalDecimals = await pool.getReserveDecimals(usdc.address);
|
const principalDecimals = (await pool.getReserveConfigurationData(usdc.address)).decimals.toString();
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
.times(new BigNumber(amountToLiquidate).times(105))
|
||||||
.times(new BigNumber(10).pow(collateralDecimals))
|
.times(new BigNumber(10).pow(collateralDecimals))
|
||||||
.div(new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)))
|
.div(
|
||||||
.div(100)
|
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
)
|
||||||
|
|
||||||
const expectedFeeLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(userReserveDataBefore.originationFee).times(105))
|
|
||||||
.times(new BigNumber(10).pow(collateralDecimals))
|
|
||||||
.div(new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)))
|
|
||||||
.div(100)
|
.div(100)
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
|
@ -299,15 +303,16 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
'Invalid health factor'
|
'Invalid health factor'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq(
|
console.log(
|
||||||
'0',
|
'Debt: ',
|
||||||
'Origination fee should be repaid'
|
userReserveDataAfter.currentStableDebt.toString(),
|
||||||
|
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
||||||
|
.minus(amountToLiquidate)
|
||||||
|
.toFixed(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(feeAddressBalance.toString()).to.be.bignumber.gt('0');
|
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
||||||
|
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
||||||
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentBorrowBalance.toString())
|
|
||||||
.minus(amountToLiquidate)
|
.minus(amountToLiquidate)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
'Invalid user borrow balance after liquidation'
|
'Invalid user borrow balance after liquidation'
|
||||||
|
@ -318,9 +323,16 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
'Invalid principal available liquidity'
|
'Invalid principal available liquidity'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'Debt: ',
|
||||||
|
usdcReserveDataAfter.availableLiquidity.toString(),
|
||||||
|
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
||||||
|
.plus(amountToLiquidate)
|
||||||
|
.toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity)
|
new BigNumber(ethReserveDataBefore.availableLiquidity)
|
||||||
.minus(expectedFeeLiquidated)
|
|
||||||
.minus(expectedCollateralLiquidated)
|
.minus(expectedCollateralLiquidated)
|
||||||
.toFixed(0),
|
.toFixed(0),
|
||||||
'Invalid collateral available liquidity'
|
'Invalid collateral available liquidity'
|
||||||
|
@ -328,7 +340,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User 4 deposits 1000 LEND - drops HF, liquidates the LEND, which results on a lower amount being liquidated', async () => {
|
it('User 4 deposits 1000 LEND - drops HF, liquidates the LEND, which results on a lower amount being liquidated', async () => {
|
||||||
const {lend, usdc, users, pool, oracle, addressesProvider} = testEnv;
|
const {lend, usdc, users, pool, oracle} = testEnv;
|
||||||
|
|
||||||
const depositor = users[3];
|
const depositor = users[3];
|
||||||
const borrower = users[4];
|
const borrower = users[4];
|
||||||
|
@ -347,20 +359,26 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
await oracle.setAssetPrice(usdc.address, new BigNumber(usdcPrice.toString()).multipliedBy(1.1).toFixed(0));
|
await oracle.setAssetPrice(
|
||||||
|
usdc.address,
|
||||||
|
new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
//mints usdc to the liquidator
|
//mints usdc to the liquidator
|
||||||
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
await usdc.connect(liquidator.signer).mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
//approve protocol to access depositor wallet
|
||||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
const userReserveDataBefore: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
const userReserveDataBefore: any = await pool.getUserReserveData(
|
||||||
|
usdc.address,
|
||||||
|
borrower.address
|
||||||
|
);
|
||||||
|
|
||||||
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
|
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
|
||||||
const lendReserveDataBefore: any = await pool.getReserveData(lend.address);
|
const lendReserveDataBefore: any = await pool.getReserveData(lend.address);
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance)
|
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
|
||||||
.div(2)
|
.div(2)
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
||||||
.toFixed(0);
|
.toFixed(0);
|
||||||
|
@ -368,13 +386,9 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const collateralPrice = await oracle.getAssetPrice(lend.address);
|
const collateralPrice = await oracle.getAssetPrice(lend.address);
|
||||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
await pool.connect(liquidator.signer).liquidationCall(
|
await pool
|
||||||
lend.address,
|
.connect(liquidator.signer)
|
||||||
usdc.address,
|
.liquidationCall(lend.address, usdc.address, borrower.address, amountToLiquidate, false);
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
|
||||||
|
|
||||||
|
@ -383,17 +397,19 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
|
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
|
||||||
const lendReserveDataAfter: any = await pool.getReserveData(lend.address);
|
const lendReserveDataAfter: any = await pool.getReserveData(lend.address);
|
||||||
|
|
||||||
const collateralDecimals = await pool.getReserveDecimals(lend.address);
|
const collateralDecimals = (await pool.getReserveConfigurationData(lend.address)).decimals.toString();
|
||||||
const principalDecimals = await pool.getReserveDecimals(usdc.address);
|
const principalDecimals = (await pool.getReserveConfigurationData(usdc.address)).decimals.toString();
|
||||||
|
|
||||||
const expectedCollateralLiquidated = oneEther.multipliedBy('1000');
|
const expectedCollateralLiquidated = oneEther.multipliedBy('1000');
|
||||||
|
|
||||||
const liquidationBonus = await pool.getReserveLiquidationBonus(lend.address);
|
const liquidationBonus = (await pool.getReserveConfigurationData(lend.address)).liquidationBonus.toString();
|
||||||
|
|
||||||
const expectedPrincipal = new BigNumber(collateralPrice.toString())
|
const expectedPrincipal = new BigNumber(collateralPrice.toString())
|
||||||
.times(expectedCollateralLiquidated)
|
.times(expectedCollateralLiquidated)
|
||||||
.times(new BigNumber(10).pow(principalDecimals))
|
.times(new BigNumber(10).pow(principalDecimals))
|
||||||
.div(new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)))
|
.div(
|
||||||
|
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||||
|
)
|
||||||
.times(100)
|
.times(100)
|
||||||
.div(liquidationBonus.toString())
|
.div(liquidationBonus.toString())
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
@ -403,13 +419,8 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
'Invalid health factor'
|
'Invalid health factor'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq(
|
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
||||||
'0',
|
new BigNumber(userReserveDataBefore.currentStableDebt).minus(expectedPrincipal).toFixed(0),
|
||||||
'Origination fee should be repaid'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentBorrowBalance).minus(expectedPrincipal).toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
'Invalid user borrow balance after liquidation'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ BigNumber.config({DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN});
|
||||||
|
|
||||||
const scenarioFolder = './test/helpers/scenarios/';
|
const scenarioFolder = './test/helpers/scenarios/';
|
||||||
|
|
||||||
const selectedScenarios: string[] = [];
|
const selectedScenarios: string[] = ['borrow-repay-variable.json'];
|
||||||
|
|
||||||
fs.readdirSync(scenarioFolder).forEach((file) => {
|
fs.readdirSync(scenarioFolder).forEach((file) => {
|
||||||
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;
|
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12
types/LendingPool.d.ts
vendored
12
types/LendingPool.d.ts
vendored
|
@ -385,6 +385,7 @@ export class LendingPool extends Contract {
|
||||||
getReserveConfigurationData(
|
getReserveConfigurationData(
|
||||||
_reserve: string
|
_reserve: string
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
decimals: BigNumber;
|
||||||
ltv: BigNumber;
|
ltv: BigNumber;
|
||||||
liquidationThreshold: BigNumber;
|
liquidationThreshold: BigNumber;
|
||||||
liquidationBonus: BigNumber;
|
liquidationBonus: BigNumber;
|
||||||
|
@ -398,13 +399,14 @@ export class LendingPool extends Contract {
|
||||||
0: BigNumber;
|
0: BigNumber;
|
||||||
1: BigNumber;
|
1: BigNumber;
|
||||||
2: BigNumber;
|
2: BigNumber;
|
||||||
3: string;
|
3: BigNumber;
|
||||||
4: string;
|
4: string;
|
||||||
5: boolean;
|
5: string;
|
||||||
6: boolean;
|
6: boolean;
|
||||||
7: boolean;
|
7: boolean;
|
||||||
8: boolean;
|
8: boolean;
|
||||||
9: boolean;
|
9: boolean;
|
||||||
|
10: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getReserveData(
|
getReserveData(
|
||||||
|
@ -650,6 +652,7 @@ export class LendingPool extends Contract {
|
||||||
getReserveConfigurationData(
|
getReserveConfigurationData(
|
||||||
_reserve: string
|
_reserve: string
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
decimals: BigNumber;
|
||||||
ltv: BigNumber;
|
ltv: BigNumber;
|
||||||
liquidationThreshold: BigNumber;
|
liquidationThreshold: BigNumber;
|
||||||
liquidationBonus: BigNumber;
|
liquidationBonus: BigNumber;
|
||||||
|
@ -663,13 +666,14 @@ export class LendingPool extends Contract {
|
||||||
0: BigNumber;
|
0: BigNumber;
|
||||||
1: BigNumber;
|
1: BigNumber;
|
||||||
2: BigNumber;
|
2: BigNumber;
|
||||||
3: string;
|
3: BigNumber;
|
||||||
4: string;
|
4: string;
|
||||||
5: boolean;
|
5: string;
|
||||||
6: boolean;
|
6: boolean;
|
||||||
7: boolean;
|
7: boolean;
|
||||||
8: boolean;
|
8: boolean;
|
||||||
9: boolean;
|
9: boolean;
|
||||||
|
10: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getReserveData(
|
getReserveData(
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
30
types/LendingPoolLiquidationManager.d.ts
vendored
30
types/LendingPoolLiquidationManager.d.ts
vendored
|
@ -37,7 +37,6 @@ interface LendingPoolLiquidationManagerInterface extends Interface {
|
||||||
_user,
|
_user,
|
||||||
_purchaseAmount,
|
_purchaseAmount,
|
||||||
_liquidatedCollateralAmount,
|
_liquidatedCollateralAmount,
|
||||||
_accruedBorrowInterest,
|
|
||||||
_liquidator,
|
_liquidator,
|
||||||
_receiveAToken,
|
_receiveAToken,
|
||||||
_timestamp
|
_timestamp
|
||||||
|
@ -49,25 +48,6 @@ interface LendingPoolLiquidationManagerInterface extends Interface {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
null
|
|
||||||
]): string[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
OriginationFeeLiquidated: TypedEventDescription<{
|
|
||||||
encodeTopics([
|
|
||||||
_collateral,
|
|
||||||
_reserve,
|
|
||||||
_user,
|
|
||||||
_feeLiquidated,
|
|
||||||
_liquidatedCollateralForFee,
|
|
||||||
_timestamp
|
|
||||||
]: [
|
|
||||||
string | null,
|
|
||||||
string | null,
|
|
||||||
string | null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
null
|
||||||
]): string[];
|
]): string[];
|
||||||
}>;
|
}>;
|
||||||
|
@ -138,20 +118,10 @@ export class LendingPoolLiquidationManager extends Contract {
|
||||||
_user: string | null,
|
_user: string | null,
|
||||||
_purchaseAmount: null,
|
_purchaseAmount: null,
|
||||||
_liquidatedCollateralAmount: null,
|
_liquidatedCollateralAmount: null,
|
||||||
_accruedBorrowInterest: null,
|
|
||||||
_liquidator: null,
|
_liquidator: null,
|
||||||
_receiveAToken: null,
|
_receiveAToken: null,
|
||||||
_timestamp: null
|
_timestamp: null
|
||||||
): EventFilter;
|
): EventFilter;
|
||||||
|
|
||||||
OriginationFeeLiquidated(
|
|
||||||
_collateral: string | null,
|
|
||||||
_reserve: string | null,
|
|
||||||
_user: string | null,
|
|
||||||
_feeLiquidated: null,
|
|
||||||
_liquidatedCollateralForFee: null,
|
|
||||||
_timestamp: null
|
|
||||||
): EventFilter;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
estimate: {
|
estimate: {
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -134,4 +134,4 @@ const _abi = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const _bytecode =
|
const _bytecode =
|
||||||
"0x608060405234801561001057600080fd5b5060405161099e38038061099e8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055610939806100656000396000f3fe6080604052600436106100385760003560e01c80639e3c930914610083578063b59b28ef1461014f578063f7888aec146102d35761007e565b3661007e5761004633610320565b61007c576040805162461bcd60e51b8152602060048201526002602482015261191960f11b604482015290519081900360640190fd5b005b600080fd5b34801561008f57600080fd5b506100b6600480360360208110156100a657600080fd5b50356001600160a01b031661035c565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100fa5781810151838201526020016100e2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610139578181015183820152602001610121565b5050505090500194505050505060405180910390f35b34801561015b57600080fd5b506102836004803603604081101561017257600080fd5b81019060208101813564010000000081111561018d57600080fd5b82018360208201111561019f57600080fd5b803590602001918460208302840111640100000000831117156101c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561021157600080fd5b82018360208201111561022357600080fd5b8035906020019184602083028401116401000000008311171561024557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506106a9945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102bf5781810151838201526020016102a7565b505050509050019250505060405180910390f35b3480156102df57600080fd5b5061030e600480360360408110156102f657600080fd5b506001600160a01b0381358116916020013516610841565b60408051918252519081900360200190f35b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061035457508115155b949350505050565b60608060008060009054906101000a90046001600160a01b03166001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ae57600080fd5b505afa1580156103c2573d6000803e3d6000fd5b505050506040513d60208110156103d857600080fd5b505160408051630240bc6b60e21b815290519192506060916001600160a01b03841691630902f1ac916004808301926000929190829003018186803b15801561042057600080fd5b505afa158015610434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561045d57600080fd5b810190808051604051939291908464010000000082111561047d57600080fd5b90830190602082018581111561049257600080fd5b82518660208202830111640100000000821117156104af57600080fd5b82525081516020918201928201910280838360005b838110156104dc5781810151838201526020016104c4565b5050505090500160405250505090506060815167ffffffffffffffff8111801561050557600080fd5b5060405190808252806020026020018201604052801561052f578160200160208202803683370190505b50905060005b825181101561069d576000846001600160a01b0316633e15014185848151811061055b57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506101406040518083038186803b1580156105aa57600080fd5b505afa1580156105be573d6000803e3d6000fd5b505050506040513d6101408110156105d557600080fd5b5061010001519050806106025760008383815181106105f057fe5b60200260200101818152505050610695565b61060a6108eb565b6001600160a01b031684838151811061061f57fe5b60200260200101516001600160a01b03161461066f576106528885848151811061064557fe5b6020026020010151610841565b83838151811061065e57fe5b602002602001018181525050610693565b876001600160a01b03163183838151811061068657fe5b6020026020010181815250505b505b600101610535565b50909350915050915091565b606080825184510267ffffffffffffffff811180156106c757600080fd5b506040519080825280602002602001820160405280156106f1578160200160208202803683370190505b50905060005b84518110156108375760005b845181101561082e57845182026107186108eb565b6001600160a01b031686838151811061072d57fe5b60200260200101516001600160a01b031614156107815786838151811061075057fe5b60200260200101516001600160a01b031631848383018151811061077057fe5b602002602001018181525050610825565b6107a686838151811061079057fe5b60200260200101516001600160a01b0316610320565b6107e7576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b604482015290519081900360640190fd5b61080a8784815181106107f657fe5b602002602001015187848151811061064557fe5b848383018151811061081857fe5b6020026020010181815250505b50600101610703565b506001016106f7565b5090505b92915050565b6000610855826001600160a01b0316610320565b156108e357816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156108b057600080fd5b505afa1580156108c4573d6000803e3d6000fd5b505050506040513d60208110156108da57600080fd5b5051905061083b565b50600061083b565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9056fea2646970667358221220690f7ecb9ab912f052e8d44a9a4f27007b3b6f0119cdc6270033c06dbc83a4e664736f6c63430006080033";
|
"0x608060405234801561001057600080fd5b5060405161099e38038061099e8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055610939806100656000396000f3fe6080604052600436106100385760003560e01c80639e3c930914610083578063b59b28ef1461014f578063f7888aec146102d35761007e565b3661007e5761004633610320565b61007c576040805162461bcd60e51b8152602060048201526002602482015261191960f11b604482015290519081900360640190fd5b005b600080fd5b34801561008f57600080fd5b506100b6600480360360208110156100a657600080fd5b50356001600160a01b031661035c565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100fa5781810151838201526020016100e2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610139578181015183820152602001610121565b5050505090500194505050505060405180910390f35b34801561015b57600080fd5b506102836004803603604081101561017257600080fd5b81019060208101813564010000000081111561018d57600080fd5b82018360208201111561019f57600080fd5b803590602001918460208302840111640100000000831117156101c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561021157600080fd5b82018360208201111561022357600080fd5b8035906020019184602083028401116401000000008311171561024557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506106a9945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102bf5781810151838201526020016102a7565b505050509050019250505060405180910390f35b3480156102df57600080fd5b5061030e600480360360408110156102f657600080fd5b506001600160a01b0381358116916020013516610841565b60408051918252519081900360200190f35b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061035457508115155b949350505050565b60608060008060009054906101000a90046001600160a01b03166001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ae57600080fd5b505afa1580156103c2573d6000803e3d6000fd5b505050506040513d60208110156103d857600080fd5b505160408051630240bc6b60e21b815290519192506060916001600160a01b03841691630902f1ac916004808301926000929190829003018186803b15801561042057600080fd5b505afa158015610434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561045d57600080fd5b810190808051604051939291908464010000000082111561047d57600080fd5b90830190602082018581111561049257600080fd5b82518660208202830111640100000000821117156104af57600080fd5b82525081516020918201928201910280838360005b838110156104dc5781810151838201526020016104c4565b5050505090500160405250505090506060815167ffffffffffffffff8111801561050557600080fd5b5060405190808252806020026020018201604052801561052f578160200160208202803683370190505b50905060005b825181101561069d576000846001600160a01b0316633e15014185848151811061055b57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506101606040518083038186803b1580156105aa57600080fd5b505afa1580156105be573d6000803e3d6000fd5b505050506040513d6101608110156105d557600080fd5b5061012001519050806106025760008383815181106105f057fe5b60200260200101818152505050610695565b61060a6108eb565b6001600160a01b031684838151811061061f57fe5b60200260200101516001600160a01b03161461066f576106528885848151811061064557fe5b6020026020010151610841565b83838151811061065e57fe5b602002602001018181525050610693565b876001600160a01b03163183838151811061068657fe5b6020026020010181815250505b505b600101610535565b50909350915050915091565b606080825184510267ffffffffffffffff811180156106c757600080fd5b506040519080825280602002602001820160405280156106f1578160200160208202803683370190505b50905060005b84518110156108375760005b845181101561082e57845182026107186108eb565b6001600160a01b031686838151811061072d57fe5b60200260200101516001600160a01b031614156107815786838151811061075057fe5b60200260200101516001600160a01b031631848383018151811061077057fe5b602002602001018181525050610825565b6107a686838151811061079057fe5b60200260200101516001600160a01b0316610320565b6107e7576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b604482015290519081900360640190fd5b61080a8784815181106107f657fe5b602002602001015187848151811061064557fe5b848383018151811061081857fe5b6020026020010181815250505b50600101610703565b506001016106f7565b5090505b92915050565b6000610855826001600160a01b0316610320565b156108e357816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156108b057600080fd5b505afa1580156108c4573d6000803e3d6000fd5b505050506040513d60208110156108da57600080fd5b5051905061083b565b50600061083b565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9056fea2646970667358221220fa706074cbd7cd86464474ec2c51c52dff3f0f34b17906e75605b4357a7d4dbe64736f6c63430006080033";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user