mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into feat/data-helpers
This commit is contained in:
commit
3400d3b2a8
|
@ -11,7 +11,7 @@ usePlugin('buidler-typechain');
|
|||
usePlugin('solidity-coverage');
|
||||
usePlugin('@nomiclabs/buidler-waffle');
|
||||
usePlugin('@nomiclabs/buidler-etherscan');
|
||||
//usePlugin('buidler-gas-reporter');
|
||||
usePlugin('buidler-gas-reporter');
|
||||
|
||||
const SKIP_LOAD = process.env.SKIP_LOAD === 'true';
|
||||
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
|
||||
|
@ -90,6 +90,16 @@ const buidlerConfig: any = {
|
|||
balance,
|
||||
})),
|
||||
},
|
||||
buidlerevm_docker: {
|
||||
hardfork: 'istanbul',
|
||||
blockGasLimit: 9500000,
|
||||
gas: 9500000,
|
||||
gasPrice: 8000000000,
|
||||
chainId: BUIDLEREVM_CHAINID,
|
||||
throwOnTransactionFailures: true,
|
||||
throwOnCallFailures: true,
|
||||
url: 'http://localhost:8545',
|
||||
},
|
||||
ganache: {
|
||||
url: 'http://ganache:8545',
|
||||
accounts: {
|
||||
|
|
|
@ -270,7 +270,7 @@ export const AaveConfig: IAaveConfiguration = {
|
|||
MKR: '0x61e4CAE3DA7FD189e52a4879C7B8067D7C2Cc0FA',
|
||||
LINK: '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789',
|
||||
KNC: '0x3F80c39c0b96A0945f9F0E9f55d8A8891c5671A8',
|
||||
WBTC: '0x3b92f58feD223E2cB1bCe4c286BD97e42f2A12EA',
|
||||
WBTC: '0xD1B98B6607330172f1D991521145A22BCe793277',
|
||||
MANA: '0x738Dc6380157429e957d223e6333Dc385c85Fec7',
|
||||
ZRX: '0xD0d76886cF8D952ca26177EB7CfDf83bad08C00C',
|
||||
SNX: '0x7FDb81B0b8a010dd4FFc57C3fecbf145BA8Bd947',
|
||||
|
|
|
@ -258,4 +258,11 @@ export const CommonsConfig: ICommonConfiguration = {
|
|||
[eEthereumNetwork.ropsten]: '',
|
||||
[eEthereumNetwork.main]: '',
|
||||
},
|
||||
ProxyPriceProvider: {
|
||||
[eEthereumNetwork.coverage]: '',
|
||||
[eEthereumNetwork.buidlerevm]: '',
|
||||
[eEthereumNetwork.kovan]: '0x276C4793F2EE3D5Bf18C5b879529dD4270BA4814',
|
||||
[eEthereumNetwork.ropsten]: '',
|
||||
[eEthereumNetwork.main]: '',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
|
||||
import {
|
||||
InitializableAdminUpgradeabilityProxy
|
||||
} from '../libraries/openzeppelin-upgradeability/InitializableAdminUpgradeabilityProxy.sol';
|
||||
InitializableImmutableAdminUpgradeabilityProxy
|
||||
} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
|
||||
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
|
||||
|
@ -153,14 +153,14 @@ contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider
|
|||
function _updateImpl(bytes32 id, address newAddress) internal {
|
||||
address payable proxyAddress = payable(_addresses[id]);
|
||||
|
||||
InitializableAdminUpgradeabilityProxy proxy = InitializableAdminUpgradeabilityProxy(
|
||||
proxyAddress
|
||||
);
|
||||
|
||||
InitializableImmutableAdminUpgradeabilityProxy proxy
|
||||
= InitializableImmutableAdminUpgradeabilityProxy(proxyAddress);
|
||||
bytes memory params = abi.encodeWithSignature('initialize(address)', address(this));
|
||||
|
||||
if (proxyAddress == address(0)) {
|
||||
proxy = new InitializableAdminUpgradeabilityProxy();
|
||||
proxy.initialize(newAddress, address(this), params);
|
||||
proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this));
|
||||
proxy.initialize(newAddress, params);
|
||||
_addresses[id] = address(proxy);
|
||||
emit ProxyCreated(id, address(proxy));
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
|
||||
import {
|
||||
ILendingPoolAddressesProviderRegistry
|
||||
} from '../interfaces/ILendingPoolAddressesProviderRegistry.sol';
|
||||
|
@ -36,13 +36,15 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP
|
|||
* @return the list of addressesProviders
|
||||
**/
|
||||
function getAddressesProvidersList() external override view returns (address[] memory) {
|
||||
uint256 maxLength = _addressesProvidersList.length;
|
||||
address[] memory addressesProvidersList = _addressesProvidersList;
|
||||
|
||||
uint256 maxLength = addressesProvidersList.length;
|
||||
|
||||
address[] memory activeProviders = new address[](maxLength);
|
||||
|
||||
for (uint256 i = 0; i < _addressesProvidersList.length; i++) {
|
||||
if (_addressesProviders[_addressesProvidersList[i]] > 0) {
|
||||
activeProviders[i] = _addressesProvidersList[i];
|
||||
for (uint256 i = 0; i < maxLength; i++) {
|
||||
if (_addressesProviders[addressesProvidersList[i]] > 0) {
|
||||
activeProviders[i] = addressesProvidersList[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +56,8 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP
|
|||
* @param provider the pool address to be registered
|
||||
**/
|
||||
function registerAddressesProvider(address provider, uint256 id) external override onlyOwner {
|
||||
require(id != 0, Errors.INVALID_ADDRESSES_PROVIDER_ID);
|
||||
|
||||
_addressesProviders[provider] = id;
|
||||
_addToAddressesProvidersList(provider);
|
||||
emit AddressesProviderRegistered(provider);
|
||||
|
@ -74,7 +78,9 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP
|
|||
* @param provider the pool address to be added
|
||||
**/
|
||||
function _addToAddressesProvidersList(address provider) internal {
|
||||
for (uint256 i = 0; i < _addressesProvidersList.length; i++) {
|
||||
uint256 providersCount = _addressesProvidersList.length;
|
||||
|
||||
for (uint256 i = 0; i < providersCount; i++) {
|
||||
if (_addressesProvidersList[i] == provider) {
|
||||
return;
|
||||
}
|
||||
|
|
344
contracts/dependencies/openzeppelin/contracts/ERC20.sol
Normal file
344
contracts/dependencies/openzeppelin/contracts/ERC20.sol
Normal file
|
@ -0,0 +1,344 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import './Context.sol';
|
||||
import './IERC20.sol';
|
||||
import './SafeMath.sol';
|
||||
import './Address.sol';
|
||||
|
||||
/**
|
||||
* @dev Implementation of the {IERC20} interface.
|
||||
*
|
||||
* This implementation is agnostic to the way tokens are created. This means
|
||||
* that a supply mechanism has to be added in a derived contract using {_mint}.
|
||||
* For a generic mechanism see {ERC20PresetMinterPauser}.
|
||||
*
|
||||
* TIP: For a detailed writeup see our guide
|
||||
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
|
||||
* to implement supply mechanisms].
|
||||
*
|
||||
* We have followed general OpenZeppelin guidelines: functions revert instead
|
||||
* of returning `false` on failure. This behavior is nonetheless conventional
|
||||
* and does not conflict with the expectations of ERC20 applications.
|
||||
*
|
||||
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
|
||||
* This allows applications to reconstruct the allowance for all accounts just
|
||||
* by listening to said events. Other implementations of the EIP may not emit
|
||||
* these events, as it isn't required by the specification.
|
||||
*
|
||||
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
|
||||
* functions have been added to mitigate the well-known issues around setting
|
||||
* allowances. See {IERC20-approve}.
|
||||
*/
|
||||
contract ERC20 is Context, IERC20 {
|
||||
using SafeMath for uint256;
|
||||
using Address for address;
|
||||
|
||||
mapping(address => uint256) private _balances;
|
||||
|
||||
mapping(address => mapping(address => uint256)) private _allowances;
|
||||
|
||||
uint256 private _totalSupply;
|
||||
|
||||
string private _name;
|
||||
string private _symbol;
|
||||
uint8 private _decimals;
|
||||
|
||||
/**
|
||||
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
|
||||
* a default value of 18.
|
||||
*
|
||||
* To select a different value for {decimals}, use {_setupDecimals}.
|
||||
*
|
||||
* All three of these values are immutable: they can only be set once during
|
||||
* construction.
|
||||
*/
|
||||
constructor(string memory name, string memory symbol) public {
|
||||
_name = name;
|
||||
_symbol = symbol;
|
||||
_decimals = 18;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the name of the token.
|
||||
*/
|
||||
function name() public view returns (string memory) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the symbol of the token, usually a shorter version of the
|
||||
* name.
|
||||
*/
|
||||
function symbol() public view returns (string memory) {
|
||||
return _symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of decimals used to get its user representation.
|
||||
* For example, if `decimals` equals `2`, a balance of `505` tokens should
|
||||
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
|
||||
*
|
||||
* Tokens usually opt for a value of 18, imitating the relationship between
|
||||
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
|
||||
* called.
|
||||
*
|
||||
* NOTE: This information is only used for _display_ purposes: it in
|
||||
* no way affects any of the arithmetic of the contract, including
|
||||
* {IERC20-balanceOf} and {IERC20-transfer}.
|
||||
*/
|
||||
function decimals() public view returns (uint8) {
|
||||
return _decimals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-totalSupply}.
|
||||
*/
|
||||
function totalSupply() public override view returns (uint256) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-balanceOf}.
|
||||
*/
|
||||
function balanceOf(address account) public override view returns (uint256) {
|
||||
return _balances[account];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-transfer}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `recipient` cannot be the zero address.
|
||||
* - the caller must have a balance of at least `amount`.
|
||||
*/
|
||||
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
|
||||
_transfer(_msgSender(), recipient, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-allowance}.
|
||||
*/
|
||||
function allowance(address owner, address spender)
|
||||
public
|
||||
virtual
|
||||
override
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _allowances[owner][spender];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-approve}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function approve(address spender, uint256 amount) public virtual override returns (bool) {
|
||||
_approve(_msgSender(), spender, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-transferFrom}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance. This is not
|
||||
* required by the EIP. See the note at the beginning of {ERC20};
|
||||
*
|
||||
* Requirements:
|
||||
* - `sender` and `recipient` cannot be the zero address.
|
||||
* - `sender` must have a balance of at least `amount`.
|
||||
* - the caller must have allowance for ``sender``'s tokens of at least
|
||||
* `amount`.
|
||||
*/
|
||||
function transferFrom(
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 amount
|
||||
) public virtual override returns (bool) {
|
||||
_transfer(sender, recipient, amount);
|
||||
_approve(
|
||||
sender,
|
||||
_msgSender(),
|
||||
_allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Atomically increases the allowance granted to `spender` by the caller.
|
||||
*
|
||||
* This is an alternative to {approve} that can be used as a mitigation for
|
||||
* problems described in {IERC20-approve}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
|
||||
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Atomically decreases the allowance granted to `spender` by the caller.
|
||||
*
|
||||
* This is an alternative to {approve} that can be used as a mitigation for
|
||||
* problems described in {IERC20-approve}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
* - `spender` must have allowance for the caller of at least
|
||||
* `subtractedValue`.
|
||||
*/
|
||||
function decreaseAllowance(address spender, uint256 subtractedValue)
|
||||
public
|
||||
virtual
|
||||
returns (bool)
|
||||
{
|
||||
_approve(
|
||||
_msgSender(),
|
||||
spender,
|
||||
_allowances[_msgSender()][spender].sub(
|
||||
subtractedValue,
|
||||
'ERC20: decreased allowance below zero'
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Moves tokens `amount` from `sender` to `recipient`.
|
||||
*
|
||||
* This is internal function is equivalent to {transfer}, and can be used to
|
||||
* e.g. implement automatic token fees, slashing mechanisms, etc.
|
||||
*
|
||||
* Emits a {Transfer} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `sender` cannot be the zero address.
|
||||
* - `recipient` cannot be the zero address.
|
||||
* - `sender` must have a balance of at least `amount`.
|
||||
*/
|
||||
function _transfer(
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 amount
|
||||
) internal virtual {
|
||||
require(sender != address(0), 'ERC20: transfer from the zero address');
|
||||
require(recipient != address(0), 'ERC20: transfer to the zero address');
|
||||
|
||||
_beforeTokenTransfer(sender, recipient, amount);
|
||||
|
||||
_balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
|
||||
_balances[recipient] = _balances[recipient].add(amount);
|
||||
emit Transfer(sender, recipient, amount);
|
||||
}
|
||||
|
||||
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
|
||||
* the total supply.
|
||||
*
|
||||
* Emits a {Transfer} event with `from` set to the zero address.
|
||||
*
|
||||
* Requirements
|
||||
*
|
||||
* - `to` cannot be the zero address.
|
||||
*/
|
||||
function _mint(address account, uint256 amount) internal virtual {
|
||||
require(account != address(0), 'ERC20: mint to the zero address');
|
||||
|
||||
_beforeTokenTransfer(address(0), account, amount);
|
||||
|
||||
_totalSupply = _totalSupply.add(amount);
|
||||
_balances[account] = _balances[account].add(amount);
|
||||
emit Transfer(address(0), account, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Destroys `amount` tokens from `account`, reducing the
|
||||
* total supply.
|
||||
*
|
||||
* Emits a {Transfer} event with `to` set to the zero address.
|
||||
*
|
||||
* Requirements
|
||||
*
|
||||
* - `account` cannot be the zero address.
|
||||
* - `account` must have at least `amount` tokens.
|
||||
*/
|
||||
function _burn(address account, uint256 amount) internal virtual {
|
||||
require(account != address(0), 'ERC20: burn from the zero address');
|
||||
|
||||
_beforeTokenTransfer(account, address(0), amount);
|
||||
|
||||
_balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
|
||||
_totalSupply = _totalSupply.sub(amount);
|
||||
emit Transfer(account, address(0), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
|
||||
*
|
||||
* This is internal function is equivalent to `approve`, and can be used to
|
||||
* e.g. set automatic allowances for certain subsystems, etc.
|
||||
*
|
||||
* Emits an {Approval} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `owner` cannot be the zero address.
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function _approve(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 amount
|
||||
) internal virtual {
|
||||
require(owner != address(0), 'ERC20: approve from the zero address');
|
||||
require(spender != address(0), 'ERC20: approve to the zero address');
|
||||
|
||||
_allowances[owner][spender] = amount;
|
||||
emit Approval(owner, spender, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets {decimals} to a value other than the default one of 18.
|
||||
*
|
||||
* WARNING: This function should only be called from the constructor. Most
|
||||
* applications that interact with token contracts will not expect
|
||||
* {decimals} to ever change, and may work incorrectly if it does.
|
||||
*/
|
||||
function _setupDecimals(uint8 decimals_) internal {
|
||||
_decimals = decimals_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Hook that is called before any transfer of tokens. This includes
|
||||
* minting and burning.
|
||||
*
|
||||
* Calling conditions:
|
||||
*
|
||||
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
||||
* will be to transferred to `to`.
|
||||
* - when `from` is zero, `amount` tokens will be minted for `to`.
|
||||
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
|
||||
* - `from` and `to` are never both zero.
|
||||
*
|
||||
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
||||
*/
|
||||
function _beforeTokenTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal virtual {}
|
||||
}
|
69
contracts/dependencies/openzeppelin/contracts/Ownable.sol
Normal file
69
contracts/dependencies/openzeppelin/contracts/Ownable.sol
Normal file
|
@ -0,0 +1,69 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import './Context.sol';
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides a basic access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* By default, the owner account will be the one that deploys the contract. This
|
||||
* can later be changed with {transferOwnership}.
|
||||
*
|
||||
* This module is used through inheritance. It will make available the modifier
|
||||
* `onlyOwner`, which can be applied to your functions to restrict their use to
|
||||
* the owner.
|
||||
*/
|
||||
contract Ownable is Context {
|
||||
address private _owner;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract setting the deployer as the initial owner.
|
||||
*/
|
||||
constructor() internal {
|
||||
address msgSender = _msgSender();
|
||||
_owner = msgSender;
|
||||
emit OwnershipTransferred(address(0), msgSender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the current owner.
|
||||
*/
|
||||
function owner() public view returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(_owner == _msgSender(), 'Ownable: caller is not the owner');
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Leaves the contract without owner. It will not be possible to call
|
||||
* `onlyOwner` functions anymore. Can only be called by the current owner.
|
||||
*
|
||||
* NOTE: Renouncing ownership will leave the contract without an owner,
|
||||
* thereby removing any functionality that is only available to the owner.
|
||||
*/
|
||||
function renounceOwnership() public virtual onlyOwner {
|
||||
emit OwnershipTransferred(_owner, address(0));
|
||||
_owner = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) public virtual onlyOwner {
|
||||
require(newOwner != address(0), 'Ownable: new owner is the zero address');
|
||||
emit OwnershipTransferred(_owner, newOwner);
|
||||
_owner = newOwner;
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
pragma solidity 0.6.8;
|
||||
|
||||
import {IERC20} from '../interfaces/IERC20.sol';
|
||||
import {SafeMath} from '../libraries/math/SafeMath.sol';
|
||||
import {IERC20} from './IERC20.sol';
|
||||
import {SafeMath} from './SafeMath.sol';
|
||||
import {Address} from './Address.sol';
|
||||
|
||||
/**
|
|
@ -2,7 +2,7 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import './Proxy.sol';
|
||||
import '@openzeppelin/contracts/utils/Address.sol';
|
||||
import '../contracts/Address.sol';
|
||||
|
||||
/**
|
||||
* @title BaseUpgradeabilityProxy
|
|
@ -1,12 +1,11 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {IFlashLoanReceiver} from '../interfaces/IFlashLoanReceiver.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import '@nomiclabs/buidler/console.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
|
||||
abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
|
||||
using SafeERC20 for IERC20;
|
||||
|
|
|
@ -9,9 +9,9 @@ pragma solidity ^0.6.8;
|
|||
**/
|
||||
interface IFlashLoanReceiver {
|
||||
function executeOperation(
|
||||
address reserve,
|
||||
uint256 amount,
|
||||
uint256 fee,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256[] calldata premiums,
|
||||
bytes calldata params
|
||||
) external returns (bool);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
interface IExchangeAdapter {
|
||||
event Exchange(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {LendingPoolAddressesProvider} from '../configuration/LendingPoolAddressesProvider.sol';
|
||||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
|
||||
import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
|
||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||
|
@ -100,46 +99,20 @@ interface ILendingPool {
|
|||
/**
|
||||
* @dev emitted when a flashloan is executed
|
||||
* @param target the address of the flashLoanReceiver
|
||||
* @param reserve the address of the reserve
|
||||
* @param amount the amount requested
|
||||
* @param totalPremium the total fee on the amount
|
||||
* @param assets the address of the assets being flashborrowed
|
||||
* @param amounts the amount requested
|
||||
* @param premiums the total fee on the amount
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
event FlashLoan(
|
||||
address indexed target,
|
||||
address indexed reserve,
|
||||
uint256 amount,
|
||||
uint256 totalPremium,
|
||||
uint256 mode,
|
||||
address[] assets,
|
||||
uint256[] amounts,
|
||||
uint256[] premiums,
|
||||
uint16 referralCode
|
||||
);
|
||||
/**
|
||||
* @dev these events are not emitted directly by the LendingPool
|
||||
* but they are declared here as the LendingPoolCollateralManager
|
||||
* is executed using a delegateCall().
|
||||
* This allows to have the events in the generated ABI for LendingPool.
|
||||
**/
|
||||
|
||||
/**
|
||||
* @dev emitted when a borrower is liquidated
|
||||
* @param collateral the address of the collateral being liquidated
|
||||
* @param reserve the address of the reserve
|
||||
* @param user the address of the user being liquidated
|
||||
* @param purchaseAmount the total amount liquidated
|
||||
* @param liquidatedCollateralAmount the amount of collateral being liquidated
|
||||
* @param accruedBorrowInterest the amount of interest accrued by the borrower since the last action
|
||||
* @param liquidator the address of the liquidator
|
||||
* @param receiveAToken true if the liquidator wants to receive aTokens, false otherwise
|
||||
**/
|
||||
event LiquidationCall(
|
||||
address indexed collateral,
|
||||
address indexed reserve,
|
||||
address indexed user,
|
||||
uint256 purchaseAmount,
|
||||
uint256 liquidatedCollateralAmount,
|
||||
uint256 accruedBorrowInterest,
|
||||
address liquidator,
|
||||
bool receiveAToken
|
||||
);
|
||||
/**
|
||||
* @dev Emitted when the pause is triggered.
|
||||
*/
|
||||
|
@ -150,6 +123,51 @@ interface ILendingPool {
|
|||
*/
|
||||
event Unpaused();
|
||||
|
||||
/**
|
||||
* @dev emitted when a borrower is liquidated. Thos evemt is emitted directly by the LendingPool
|
||||
* but it's declared here as the LendingPoolCollateralManager
|
||||
* is executed using a delegateCall().
|
||||
* This allows to have the events in the generated ABI for LendingPool.
|
||||
* @param collateral the address of the collateral being liquidated
|
||||
* @param principal the address of the reserve
|
||||
* @param user the address of the user being liquidated
|
||||
* @param purchaseAmount the total amount liquidated
|
||||
* @param liquidatedCollateralAmount the amount of collateral being liquidated
|
||||
* @param liquidator the address of the liquidator
|
||||
* @param receiveAToken true if the liquidator wants to receive aTokens, false otherwise
|
||||
**/
|
||||
event LiquidationCall(
|
||||
address indexed collateral,
|
||||
address indexed principal,
|
||||
address indexed user,
|
||||
uint256 purchaseAmount,
|
||||
uint256 liquidatedCollateralAmount,
|
||||
address liquidator,
|
||||
bool receiveAToken
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
|
||||
* in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
|
||||
* the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
|
||||
* gets added to the LendingPool ABI
|
||||
* @param reserve the address of the reserve
|
||||
* @param liquidityRate the new liquidity rate
|
||||
* @param stableBorrowRate the new stable borrow rate
|
||||
* @param variableBorrowRate the new variable borrow rate
|
||||
* @param liquidityIndex the new liquidity index
|
||||
* @param variableBorrowIndex the new variable borrow index
|
||||
**/
|
||||
event ReserveDataUpdated(
|
||||
address indexed reserve,
|
||||
uint256 liquidityRate,
|
||||
uint256 stableBorrowRate,
|
||||
uint256 averageStableBorrowRate,
|
||||
uint256 variableBorrowRate,
|
||||
uint256 liquidityIndex,
|
||||
uint256 variableBorrowIndex
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens)
|
||||
* is minted.
|
||||
|
@ -262,62 +280,26 @@ interface ILendingPool {
|
|||
bool receiveAToken
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below
|
||||
* @param collateral The address of the collateral asset
|
||||
* @param principal The address of the owed asset
|
||||
* @param user Address of the borrower
|
||||
* @param principalAmount Amount of the debt to repay. type(uint256).max to repay the maximum possible
|
||||
* @param receiver Address of the contract receiving the collateral to swap
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
|
||||
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||
* that must be kept into consideration. For further details please visit https://developers.aave.com
|
||||
* @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
|
||||
* @param reserve the address of the principal reserve
|
||||
* @param amount the amount requested for this flashloan
|
||||
* @param assets the address of the principal reserve
|
||||
* @param amounts the amount requested for this flashloan
|
||||
* @param mode the flashloan mode
|
||||
* @param params a bytes array to be sent to the flashloan executor
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
function flashLoan(
|
||||
address receiver,
|
||||
address reserve,
|
||||
uint256 amount,
|
||||
uint256 debtType,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256 mode,
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external;
|
||||
|
||||
function getUserAccountData(address user)
|
||||
external
|
||||
view
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
|
||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||
|
@ -162,7 +162,7 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
|
|||
_stableRateSlope1.rayMul(utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE))
|
||||
);
|
||||
vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(
|
||||
utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE).rayMul(_variableRateSlope1)
|
||||
utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {IAToken} from '../tokenization/interfaces/IAToken.sol';
|
||||
import {Helpers} from '../libraries/helpers/Helpers.sol';
|
||||
|
@ -25,7 +23,7 @@ import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol
|
|||
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
|
||||
import {LendingPoolCollateralManager} from './LendingPoolCollateralManager.sol';
|
||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {LendingPoolStorage} from './LendingPoolStorage.sol';
|
||||
import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
|
||||
|
@ -106,13 +104,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
reserve.updateState();
|
||||
reserve.updateInterestRates(asset, aToken, amount, 0);
|
||||
|
||||
bool isFirstDeposit = IAToken(aToken).balanceOf(onBehalfOf) == 0;
|
||||
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
|
||||
|
||||
if (isFirstDeposit) {
|
||||
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
|
||||
}
|
||||
|
||||
IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
|
||||
|
||||
//transfer to the aToken contract
|
||||
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
|
||||
|
||||
|
@ -160,7 +157,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
|
||||
|
||||
emit Withdraw(asset, msg.sender, amount);
|
||||
emit Withdraw(asset, msg.sender, amountToWithdraw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,15 +262,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
ReserveLogic.InterestRateMode interestRateMode = ReserveLogic.InterestRateMode(rateMode);
|
||||
|
||||
//default to max amount
|
||||
uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE
|
||||
? stableDebt
|
||||
: variableDebt;
|
||||
|
||||
if (amount != type(uint256).max && amount < paybackAmount) {
|
||||
paybackAmount = amount;
|
||||
}
|
||||
|
||||
ValidationLogic.validateRepay(
|
||||
reserve,
|
||||
amount,
|
||||
|
@ -283,6 +271,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
variableDebt
|
||||
);
|
||||
|
||||
//default to max amount
|
||||
uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE
|
||||
? stableDebt
|
||||
: variableDebt;
|
||||
|
||||
if (amount < paybackAmount) {
|
||||
paybackAmount = amount;
|
||||
}
|
||||
|
||||
reserve.updateState();
|
||||
|
||||
//burns an equivalent amount of debt tokens
|
||||
|
@ -359,9 +356,10 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
}
|
||||
|
||||
/**
|
||||
* @dev rebalances the stable interest rate of a user if current liquidity rate > user stable rate.
|
||||
* this is regulated by Aave to ensure that the protocol is not abused, and the user is paying a fair
|
||||
* rate. Anyone can call this function.
|
||||
* @dev rebalances the stable interest rate of a user. Users can be rebalanced if the following conditions are satisfied:
|
||||
* 1. Usage ratio is above 95%
|
||||
* 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
|
||||
* borrowed at a stable rate and depositors are not earning enough.
|
||||
* @param asset the address of the reserve
|
||||
* @param user the address of the user to be rebalanced
|
||||
**/
|
||||
|
@ -376,7 +374,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
uint256 stableBorrowBalance = IERC20(stableDebtToken).balanceOf(user);
|
||||
|
||||
//if the utilization rate is below 95%, no rebalances are needed
|
||||
//if the usage ratio is below 95%, no rebalances are needed
|
||||
uint256 totalBorrows = stableDebtToken
|
||||
.totalSupply()
|
||||
.add(variableDebtToken.totalSupply())
|
||||
|
@ -420,7 +418,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
/**
|
||||
* @dev allows depositors to enable or disable a specific deposit as collateral.
|
||||
* @param asset the address of the reserve
|
||||
* @param useAsCollateral true if the user wants to user the deposit as collateral, false otherwise.
|
||||
* @param useAsCollateral true if the user wants to use the deposit as collateral, false otherwise.
|
||||
**/
|
||||
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override {
|
||||
_whenNotPaused();
|
||||
|
@ -485,61 +483,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below
|
||||
* @param collateral The address of the collateral asset
|
||||
* @param principal The address of the owed asset
|
||||
* @param user Address of the borrower
|
||||
* @param principalAmount Amount of the debt to repay. type(uint256).max to repay the maximum possible
|
||||
* @param receiver Address of the contract receiving the collateral to swap
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
|
||||
_flashLiquidationLocked = true;
|
||||
|
||||
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
|
||||
|
||||
//solium-disable-next-line
|
||||
(bool success, bytes memory result) = collateralManager.delegatecall(
|
||||
abi.encodeWithSignature(
|
||||
'repayWithCollateral(address,address,address,uint256,address,bytes)',
|
||||
collateral,
|
||||
principal,
|
||||
user,
|
||||
principalAmount,
|
||||
receiver,
|
||||
params
|
||||
)
|
||||
);
|
||||
require(success, Errors.FAILED_REPAY_WITH_COLLATERAL);
|
||||
|
||||
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
|
||||
|
||||
if (returnCode != 0) {
|
||||
revert(string(abi.encodePacked(returnMessage)));
|
||||
}
|
||||
|
||||
_flashLiquidationLocked = false;
|
||||
}
|
||||
|
||||
struct FlashLoanLocalVars {
|
||||
uint256 premium;
|
||||
uint256 amountPlusPremium;
|
||||
IFlashLoanReceiver receiver;
|
||||
address aTokenAddress;
|
||||
address oracle;
|
||||
ReserveLogic.InterestRateMode debtMode;
|
||||
uint256 i;
|
||||
address currentAsset;
|
||||
address currentATokenAddress;
|
||||
uint256 currentAmount;
|
||||
uint256 currentPremium;
|
||||
uint256 currentAmountPlusPremium;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -547,106 +500,90 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||
* that must be kept into consideration. For further details please visit https://developers.aave.com
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
|
||||
* @param asset The address of the principal reserve
|
||||
* @param amount The amount requested for this flashloan
|
||||
* @param assets The addresss of the assets being flashborrowed
|
||||
* @param amounts The amounts requested for this flashloan for each asset
|
||||
* @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
|
||||
* @param params Variadic packed params to pass to the receiver as extra information
|
||||
* @param referralCode Referral code of the flash loan
|
||||
**/
|
||||
function flashLoan(
|
||||
address receiverAddress,
|
||||
address asset,
|
||||
uint256 amount,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256 mode,
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
ReserveLogic.ReserveData storage reserve = _reserves[asset];
|
||||
|
||||
FlashLoanLocalVars memory vars;
|
||||
|
||||
vars.aTokenAddress = reserve.aTokenAddress;
|
||||
ValidationLogic.validateFlashloan(assets, amounts, mode);
|
||||
|
||||
vars.premium = amount.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
|
||||
|
||||
ValidationLogic.validateFlashloan(mode, vars.premium);
|
||||
|
||||
ReserveLogic.InterestRateMode debtMode = ReserveLogic.InterestRateMode(mode);
|
||||
address[] memory aTokenAddresses = new address[](assets.length);
|
||||
uint256[] memory premiums = new uint256[](assets.length);
|
||||
|
||||
vars.receiver = IFlashLoanReceiver(receiverAddress);
|
||||
vars.debtMode = ReserveLogic.InterestRateMode(mode);
|
||||
|
||||
//transfer funds to the receiver
|
||||
IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
|
||||
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
||||
aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
|
||||
|
||||
premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
|
||||
|
||||
//transfer funds to the receiver
|
||||
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
|
||||
}
|
||||
|
||||
//execute action of the receiver
|
||||
require(
|
||||
vars.receiver.executeOperation(asset, amount, vars.premium, params),
|
||||
vars.receiver.executeOperation(assets, amounts, premiums, params),
|
||||
Errors.INVALID_FLASH_LOAN_EXECUTOR_RETURN
|
||||
);
|
||||
|
||||
vars.amountPlusPremium = amount.add(vars.premium);
|
||||
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
||||
vars.currentAsset = assets[vars.i];
|
||||
vars.currentAmount = amounts[vars.i];
|
||||
vars.currentPremium = premiums[vars.i];
|
||||
vars.currentATokenAddress = aTokenAddresses[vars.i];
|
||||
|
||||
if (debtMode == ReserveLogic.InterestRateMode.NONE) {
|
||||
IERC20(asset).safeTransferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
|
||||
vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
|
||||
|
||||
reserve.updateState();
|
||||
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
|
||||
reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0);
|
||||
if (vars.debtMode == ReserveLogic.InterestRateMode.NONE) {
|
||||
_reserves[vars.currentAsset].updateState();
|
||||
_reserves[vars.currentAsset].cumulateToLiquidityIndex(
|
||||
IERC20(vars.currentATokenAddress).totalSupply(),
|
||||
vars.currentPremium
|
||||
);
|
||||
_reserves[vars.currentAsset].updateInterestRates(
|
||||
vars.currentAsset,
|
||||
vars.currentATokenAddress,
|
||||
vars.currentPremium,
|
||||
0
|
||||
);
|
||||
|
||||
emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode);
|
||||
} else {
|
||||
//if the user didn't choose to return the funds, the system checks if there
|
||||
//is enough collateral and eventually open a position
|
||||
_executeBorrow(
|
||||
ExecuteBorrowParams(
|
||||
asset,
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
vars.amountPlusPremium,
|
||||
mode,
|
||||
vars.aTokenAddress,
|
||||
referralCode,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
|
||||
|
||||
//solium-disable-next-line
|
||||
(bool success, bytes memory result) = collateralManager.delegatecall(
|
||||
abi.encodeWithSignature(
|
||||
'swapLiquidity(address,address,address,uint256,bytes)',
|
||||
receiverAddress,
|
||||
fromAsset,
|
||||
toAsset,
|
||||
amountToSwap,
|
||||
params
|
||||
)
|
||||
);
|
||||
require(success, Errors.FAILED_COLLATERAL_SWAP);
|
||||
|
||||
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
|
||||
|
||||
if (returnCode != 0) {
|
||||
revert(string(abi.encodePacked(returnMessage)));
|
||||
IERC20(vars.currentAsset).safeTransferFrom(
|
||||
receiverAddress,
|
||||
vars.currentATokenAddress,
|
||||
vars.currentAmountPlusPremium
|
||||
);
|
||||
} else {
|
||||
//if the user didn't choose to return the funds, the system checks if there
|
||||
//is enough collateral and eventually open a position
|
||||
_executeBorrow(
|
||||
ExecuteBorrowParams(
|
||||
vars.currentAsset,
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
vars.currentAmount,
|
||||
mode,
|
||||
vars.currentATokenAddress,
|
||||
referralCode,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
emit FlashLoan(receiverAddress, mode, assets, amounts, premiums, referralCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts//SafeMath.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts//IERC20.sol';
|
||||
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {IAToken} from '../tokenization/interfaces/IAToken.sol';
|
||||
import {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol';
|
||||
import {IVariableDebtToken} from '../tokenization/interfaces/IVariableDebtToken.sol';
|
||||
import {DebtTokenBase} from '../tokenization/base/DebtTokenBase.sol';
|
||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
|
||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||
|
@ -17,7 +14,7 @@ import {UserConfiguration} from '../libraries/configuration/UserConfiguration.so
|
|||
import {Helpers} from '../libraries/helpers/Helpers.sol';
|
||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
|
||||
|
@ -61,24 +58,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
bool receiveAToken
|
||||
);
|
||||
|
||||
/**
|
||||
@dev emitted when a borrower/liquidator repays with the borrower's collateral
|
||||
@param collateral the address of the collateral being swapped to repay
|
||||
@param principal the address of the reserve of the debt
|
||||
@param user the borrower's address
|
||||
@param liquidator the address of the liquidator, same as the one of the borrower on self-repayment
|
||||
@param principalAmount the amount of the debt finally covered
|
||||
@param swappedCollateralAmount the amount of collateral finally swapped
|
||||
*/
|
||||
event RepaidWithCollateral(
|
||||
address indexed collateral,
|
||||
address indexed principal,
|
||||
address indexed user,
|
||||
address liquidator,
|
||||
uint256 principalAmount,
|
||||
uint256 swappedCollateralAmount
|
||||
);
|
||||
|
||||
struct LiquidationCallLocalVars {
|
||||
uint256 userCollateralBalance;
|
||||
uint256 userStableDebt;
|
||||
|
@ -99,16 +78,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
string errorMsg;
|
||||
}
|
||||
|
||||
struct SwapLiquidityLocalVars {
|
||||
uint256 healthFactor;
|
||||
uint256 amountToReceive;
|
||||
uint256 userBalanceBefore;
|
||||
IAToken fromReserveAToken;
|
||||
IAToken toReserveAToken;
|
||||
uint256 errorCode;
|
||||
string errorMsg;
|
||||
}
|
||||
|
||||
struct AvailableCollateralToLiquidateLocalVars {
|
||||
uint256 userCompoundedBorrowBalance;
|
||||
uint256 liquidationBonus;
|
||||
|
@ -192,7 +161,7 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
(
|
||||
vars.maxCollateralToLiquidate,
|
||||
vars.principalAmountNeeded
|
||||
) = calculateAvailableCollateralToLiquidate(
|
||||
) = _calculateAvailableCollateralToLiquidate(
|
||||
collateralReserve,
|
||||
principalReserve,
|
||||
collateral,
|
||||
|
@ -225,13 +194,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
//update the principal reserve
|
||||
principalReserve.updateState();
|
||||
|
||||
principalReserve.updateInterestRates(
|
||||
principal,
|
||||
principalReserve.aTokenAddress,
|
||||
vars.actualAmountToLiquidate,
|
||||
0
|
||||
);
|
||||
|
||||
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
||||
IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
|
@ -254,6 +216,13 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
);
|
||||
}
|
||||
|
||||
principalReserve.updateInterestRates(
|
||||
principal,
|
||||
principalReserve.aTokenAddress,
|
||||
vars.actualAmountToLiquidate,
|
||||
0
|
||||
);
|
||||
|
||||
//if liquidator reclaims the aToken, he receives the equivalent atoken amount
|
||||
if (receiveAToken) {
|
||||
vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate);
|
||||
|
@ -298,259 +267,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it.
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor.
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below.
|
||||
* @param collateral The address of the collateral asset.
|
||||
* @param principal The address of the owed asset.
|
||||
* @param user Address of the borrower.
|
||||
* @param principalAmount Amount of the debt to repay.
|
||||
* @param receiver Address of the contract receiving the collateral to swap.
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external returns (uint256, string memory) {
|
||||
ReserveLogic.ReserveData storage collateralReserve = _reserves[collateral];
|
||||
ReserveLogic.ReserveData storage debtReserve = _reserves[principal];
|
||||
UserConfiguration.Map storage userConfig = _usersConfig[user];
|
||||
|
||||
LiquidationCallLocalVars memory vars;
|
||||
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
user,
|
||||
_reserves,
|
||||
_usersConfig[user],
|
||||
_reservesList,
|
||||
_reservesCount,
|
||||
_addressesProvider.getPriceOracle()
|
||||
);
|
||||
|
||||
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateRepayWithCollateral(
|
||||
collateralReserve,
|
||||
debtReserve,
|
||||
userConfig,
|
||||
user,
|
||||
vars.healthFactor,
|
||||
vars.userStableDebt,
|
||||
vars.userVariableDebt
|
||||
);
|
||||
|
||||
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
|
||||
return (vars.errorCode, vars.errorMsg);
|
||||
}
|
||||
|
||||
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt);
|
||||
|
||||
vars.actualAmountToLiquidate = principalAmount > vars.maxPrincipalAmountToLiquidate
|
||||
? vars.maxPrincipalAmountToLiquidate
|
||||
: principalAmount;
|
||||
|
||||
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
|
||||
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
|
||||
|
||||
(
|
||||
vars.maxCollateralToLiquidate,
|
||||
vars.principalAmountNeeded
|
||||
) = calculateAvailableCollateralToLiquidate(
|
||||
collateralReserve,
|
||||
debtReserve,
|
||||
collateral,
|
||||
principal,
|
||||
vars.actualAmountToLiquidate,
|
||||
vars.userCollateralBalance
|
||||
);
|
||||
|
||||
//if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough
|
||||
//of collateral to cover the actual amount that is being liquidated, hence we liquidate
|
||||
//a smaller amount
|
||||
if (vars.principalAmountNeeded < vars.actualAmountToLiquidate) {
|
||||
vars.actualAmountToLiquidate = vars.principalAmountNeeded;
|
||||
}
|
||||
//updating collateral reserve indexes
|
||||
collateralReserve.updateState();
|
||||
|
||||
//updating collateral reserve interest rates
|
||||
collateralReserve.updateInterestRates(
|
||||
collateral,
|
||||
address(vars.collateralAtoken),
|
||||
0,
|
||||
vars.maxCollateralToLiquidate
|
||||
);
|
||||
|
||||
vars.collateralAtoken.burn(
|
||||
user,
|
||||
receiver,
|
||||
vars.maxCollateralToLiquidate,
|
||||
collateralReserve.liquidityIndex
|
||||
);
|
||||
|
||||
if (vars.userCollateralBalance == vars.maxCollateralToLiquidate) {
|
||||
_usersConfig[user].setUsingAsCollateral(collateralReserve.id, false);
|
||||
}
|
||||
|
||||
vars.principalAToken = debtReserve.aTokenAddress;
|
||||
|
||||
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||
ISwapAdapter(receiver).executeOperation(
|
||||
collateral,
|
||||
principal,
|
||||
vars.maxCollateralToLiquidate,
|
||||
address(this),
|
||||
params
|
||||
);
|
||||
|
||||
//updating debt reserve
|
||||
debtReserve.updateState();
|
||||
debtReserve.updateInterestRates(
|
||||
principal,
|
||||
vars.principalAToken,
|
||||
vars.actualAmountToLiquidate,
|
||||
0
|
||||
);
|
||||
IERC20(principal).safeTransferFrom(
|
||||
receiver,
|
||||
vars.principalAToken,
|
||||
vars.actualAmountToLiquidate
|
||||
);
|
||||
|
||||
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualAmountToLiquidate,
|
||||
debtReserve.variableBorrowIndex
|
||||
);
|
||||
} else {
|
||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.userVariableDebt,
|
||||
debtReserve.variableBorrowIndex
|
||||
);
|
||||
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualAmountToLiquidate.sub(vars.userVariableDebt)
|
||||
);
|
||||
}
|
||||
|
||||
emit RepaidWithCollateral(
|
||||
collateral,
|
||||
principal,
|
||||
user,
|
||||
msg.sender,
|
||||
vars.actualAmountToLiquidate,
|
||||
vars.maxCollateralToLiquidate
|
||||
);
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external returns (uint256, string memory) {
|
||||
ReserveLogic.ReserveData storage fromReserve = _reserves[fromAsset];
|
||||
ReserveLogic.ReserveData storage toReserve = _reserves[toAsset];
|
||||
|
||||
SwapLiquidityLocalVars memory vars;
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateSwapLiquidity(
|
||||
fromReserve,
|
||||
toReserve,
|
||||
fromAsset,
|
||||
toAsset
|
||||
);
|
||||
|
||||
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
|
||||
return (vars.errorCode, vars.errorMsg);
|
||||
}
|
||||
|
||||
vars.fromReserveAToken = IAToken(fromReserve.aTokenAddress);
|
||||
vars.toReserveAToken = IAToken(toReserve.aTokenAddress);
|
||||
|
||||
fromReserve.updateState();
|
||||
toReserve.updateState();
|
||||
|
||||
if (vars.fromReserveAToken.balanceOf(msg.sender) == amountToSwap) {
|
||||
_usersConfig[msg.sender].setUsingAsCollateral(fromReserve.id, false);
|
||||
}
|
||||
|
||||
fromReserve.updateInterestRates(fromAsset, address(vars.fromReserveAToken), 0, amountToSwap);
|
||||
|
||||
vars.fromReserveAToken.burn(
|
||||
msg.sender,
|
||||
receiverAddress,
|
||||
amountToSwap,
|
||||
fromReserve.liquidityIndex
|
||||
);
|
||||
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||
ISwapAdapter(receiverAddress).executeOperation(
|
||||
fromAsset,
|
||||
toAsset,
|
||||
amountToSwap,
|
||||
address(this),
|
||||
params
|
||||
);
|
||||
|
||||
vars.amountToReceive = IERC20(toAsset).balanceOf(receiverAddress);
|
||||
if (vars.amountToReceive != 0) {
|
||||
IERC20(toAsset).safeTransferFrom(
|
||||
receiverAddress,
|
||||
address(vars.toReserveAToken),
|
||||
vars.amountToReceive
|
||||
);
|
||||
|
||||
if (vars.toReserveAToken.balanceOf(msg.sender) == 0) {
|
||||
_usersConfig[msg.sender].setUsingAsCollateral(toReserve.id, true);
|
||||
}
|
||||
|
||||
vars.toReserveAToken.mint(msg.sender, vars.amountToReceive, toReserve.liquidityIndex);
|
||||
toReserve.updateInterestRates(
|
||||
toAsset,
|
||||
address(vars.toReserveAToken),
|
||||
vars.amountToReceive,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
msg.sender,
|
||||
_reserves,
|
||||
_usersConfig[msg.sender],
|
||||
_reservesList,
|
||||
_reservesCount,
|
||||
_addressesProvider.getPriceOracle()
|
||||
);
|
||||
|
||||
if (vars.healthFactor < GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD),
|
||||
Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates how much of a specific collateral can be liquidated, given
|
||||
* a certain amount of principal currency. This function needs to be called after
|
||||
|
@ -562,7 +278,7 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
* @return collateralAmount the maximum amount that is possible to liquidated given all the liquidation constraints (user balance, close factor)
|
||||
* @return principalAmountNeeded the purchase amount
|
||||
**/
|
||||
function calculateAvailableCollateralToLiquidate(
|
||||
function _calculateAvailableCollateralToLiquidate(
|
||||
ReserveLogic.ReserveData storage collateralReserve,
|
||||
ReserveLogic.ReserveData storage principalReserve,
|
||||
address collateralAddress,
|
||||
|
@ -590,8 +306,8 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
.principalCurrencyPrice
|
||||
.mul(purchaseAmount)
|
||||
.mul(10**vars.collateralDecimals)
|
||||
.div(vars.collateralPrice.mul(10**vars.principalDecimals))
|
||||
.percentMul(vars.liquidationBonus);
|
||||
.percentMul(vars.liquidationBonus)
|
||||
.div(vars.collateralPrice.mul(10**vars.principalDecimals));
|
||||
|
||||
if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
|
||||
collateralAmount = userCollateralBalance;
|
||||
|
|
|
@ -2,17 +2,16 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
|
||||
import {
|
||||
InitializableAdminUpgradeabilityProxy
|
||||
} from '../libraries/openzeppelin-upgradeability/InitializableAdminUpgradeabilityProxy.sol';
|
||||
InitializableImmutableAdminUpgradeabilityProxy
|
||||
} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
|
||||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol';
|
||||
import {ITokenConfiguration} from '../tokenization/interfaces/ITokenConfiguration.sol';
|
||||
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||
|
||||
|
@ -202,7 +201,6 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
|
||||
/**
|
||||
* @dev initializes a reserve
|
||||
* @param asset the address of the reserve to be initialized
|
||||
* @param aTokenImpl the address of the aToken contract implementation
|
||||
* @param stableDebtTokenImpl the address of the stable debt token contract
|
||||
* @param variableDebtTokenImpl the address of the variable debt token contract
|
||||
|
@ -210,13 +208,35 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param interestRateStrategyAddress the address of the interest rate strategy contract for this reserve
|
||||
**/
|
||||
function initReserve(
|
||||
address asset,
|
||||
address aTokenImpl,
|
||||
address stableDebtTokenImpl,
|
||||
address variableDebtTokenImpl,
|
||||
uint8 underlyingAssetDecimals,
|
||||
address interestRateStrategyAddress
|
||||
) public onlyAaveAdmin {
|
||||
address asset = ITokenConfiguration(aTokenImpl).UNDERLYING_ASSET_ADDRESS();
|
||||
|
||||
require(
|
||||
address(pool) == ITokenConfiguration(aTokenImpl).POOL(),
|
||||
Errors.INVALID_ATOKEN_POOL_ADDRESS
|
||||
);
|
||||
require(
|
||||
address(pool) == ITokenConfiguration(stableDebtTokenImpl).POOL(),
|
||||
Errors.INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS
|
||||
);
|
||||
require(
|
||||
address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(),
|
||||
Errors.INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS
|
||||
);
|
||||
require(
|
||||
asset == ITokenConfiguration(stableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
|
||||
Errors.INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
|
||||
);
|
||||
require(
|
||||
asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
|
||||
Errors.INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
|
||||
);
|
||||
|
||||
address aTokenProxyAddress = _initTokenWithProxy(aTokenImpl, underlyingAssetDecimals);
|
||||
|
||||
address stableDebtTokenProxyAddress = _initTokenWithProxy(
|
||||
|
@ -551,7 +571,9 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param decimals the decimals of the token
|
||||
**/
|
||||
function _initTokenWithProxy(address implementation, uint8 decimals) internal returns (address) {
|
||||
InitializableAdminUpgradeabilityProxy proxy = new InitializableAdminUpgradeabilityProxy();
|
||||
|
||||
InitializableImmutableAdminUpgradeabilityProxy proxy
|
||||
= new InitializableImmutableAdminUpgradeabilityProxy(address(this));
|
||||
|
||||
bytes memory params = abi.encodeWithSignature(
|
||||
'initialize(uint8,string,string)',
|
||||
|
@ -560,7 +582,7 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
IERC20Detailed(implementation).symbol()
|
||||
);
|
||||
|
||||
proxy.initialize(implementation, address(this), params);
|
||||
proxy.initialize(implementation, params);
|
||||
|
||||
return address(proxy);
|
||||
}
|
||||
|
@ -570,9 +592,9 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
address proxyAddress,
|
||||
address implementation
|
||||
) internal {
|
||||
InitializableAdminUpgradeabilityProxy proxy = InitializableAdminUpgradeabilityProxy(
|
||||
payable(proxyAddress)
|
||||
);
|
||||
|
||||
InitializableImmutableAdminUpgradeabilityProxy proxy
|
||||
= InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress));
|
||||
|
||||
ReserveConfiguration.Map memory configuration = pool.getConfiguration(asset);
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import '../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
|
||||
|
||||
/**
|
||||
* @title BaseImmutableAdminUpgradeabilityProxy
|
||||
* @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
|
||||
* @dev This contract combines an upgradeability proxy with an authorization
|
||||
* mechanism for administrative tasks. The admin role is stored in an immutable, which
|
||||
* helps saving transactions costs
|
||||
* All external functions in this contract must be guarded by the
|
||||
* `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
|
||||
* feature proposal that would enable this to be done automatically.
|
||||
*/
|
||||
contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
|
||||
address immutable ADMIN;
|
||||
|
||||
constructor(address admin) public {
|
||||
ADMIN = admin;
|
||||
}
|
||||
|
||||
modifier ifAdmin() {
|
||||
if (msg.sender == ADMIN) {
|
||||
_;
|
||||
} else {
|
||||
_fallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The address of the proxy admin.
|
||||
*/
|
||||
function admin() external ifAdmin returns (address) {
|
||||
return ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The address of the implementation.
|
||||
*/
|
||||
function implementation() external ifAdmin returns (address) {
|
||||
return _implementation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Upgrade the backing implementation of the proxy.
|
||||
* Only the admin can call this function.
|
||||
* @param newImplementation Address of the new implementation.
|
||||
*/
|
||||
function upgradeTo(address newImplementation) external ifAdmin {
|
||||
_upgradeTo(newImplementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Upgrade the backing implementation of the proxy and call a function
|
||||
* on the new implementation.
|
||||
* This is useful to initialize the proxied contract.
|
||||
* @param newImplementation Address of the new implementation.
|
||||
* @param data Data to send as msg.data in the low level call.
|
||||
* It should include the signature and the parameters of the function to be called, as described in
|
||||
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
|
||||
*/
|
||||
function upgradeToAndCall(address newImplementation, bytes calldata data)
|
||||
external
|
||||
payable
|
||||
ifAdmin
|
||||
{
|
||||
_upgradeTo(newImplementation);
|
||||
(bool success, ) = newImplementation.delegatecall(data);
|
||||
require(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Only fall back when the sender is not the admin.
|
||||
*/
|
||||
function _willFallback() internal virtual override {
|
||||
require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
|
||||
super._willFallback();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import './BaseImmutableAdminUpgradeabilityProxy.sol';
|
||||
import '../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
|
||||
|
||||
/**
|
||||
* @title InitializableAdminUpgradeabilityProxy
|
||||
* @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for
|
||||
* initializing the implementation, admin, and init data.
|
||||
*/
|
||||
contract InitializableImmutableAdminUpgradeabilityProxy is
|
||||
BaseImmutableAdminUpgradeabilityProxy,
|
||||
InitializableUpgradeabilityProxy
|
||||
{
|
||||
constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
|
||||
|
||||
/**
|
||||
* @dev Only fall back when the sender is not the admin.
|
||||
*/
|
||||
function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
|
||||
BaseImmutableAdminUpgradeabilityProxy._willFallback();
|
||||
}
|
||||
}
|
|
@ -1,12 +1,6 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {ReserveLogic} from '../logic/ReserveLogic.sol';
|
||||
import {WadRayMath} from '../math/WadRayMath.sol';
|
||||
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
|
||||
|
||||
/**
|
||||
* @title ReserveConfiguration library
|
||||
* @author Aave
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {WadRayMath} from '../math/WadRayMath.sol';
|
||||
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
|
||||
|
||||
/**
|
||||
* @title UserConfiguration library
|
||||
* @author Aave
|
||||
|
|
|
@ -45,13 +45,14 @@ library Errors {
|
|||
string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56';
|
||||
string public constant NO_MORE_RESERVES_ALLOWED = '59';
|
||||
string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60';
|
||||
string public constant INCONSISTENT_FLASHLOAN_PARAMS = '69';
|
||||
|
||||
// require error messages - aToken - DebtTokens
|
||||
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
|
||||
string public constant CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
|
||||
string public constant TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
|
||||
string public constant INVALID_MINT_AMOUNT = '53'; //invalid amount to mint
|
||||
string public constant INVALID_BURN_AMOUNT = '54'; //invalid amount to burn
|
||||
string public constant INVALID_MINT_AMOUNT = '61'; //invalid amount to mint
|
||||
string public constant INVALID_BURN_AMOUNT = '62'; //invalid amount to burn
|
||||
|
||||
// require error messages - ReserveLogic
|
||||
string public constant RESERVE_ALREADY_INITIALIZED = '34'; // 'Reserve has already been initialized'
|
||||
|
@ -64,9 +65,15 @@ library Errors {
|
|||
//require error messages - LendingPoolConfiguration
|
||||
string public constant CALLER_NOT_AAVE_ADMIN = '35'; // 'The caller must be the aave admin'
|
||||
string public constant RESERVE_LIQUIDITY_NOT_0 = '36'; // 'The liquidity of the reserve needs to be 0'
|
||||
string public constant INVALID_ATOKEN_POOL_ADDRESS = '63'; // the lending pool in the aToken implementation is not configured correctly
|
||||
string public constant INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '64'; // the lending pool in the stable debt token implementation is not configured correctly
|
||||
string public constant INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '65'; // the lending pool in the variable debt token implementation is not configured correctly
|
||||
string public constant INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '66'; // the underlying asset in the stable debt token implementation is not configured correctly
|
||||
string public constant INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '67'; // the underlying asset in the variable debt token implementation is not configured correctly
|
||||
|
||||
//require error messages - LendingPoolAddressesProviderRegistry
|
||||
string public constant PROVIDER_NOT_REGISTERED = '37'; // 'Provider is not registered'
|
||||
string public constant INVALID_ADDRESSES_PROVIDER_ID = '68'; // the addresses provider id needs to be greater than 0
|
||||
|
||||
//return error messages - LendingPoolCollateralManager
|
||||
string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '38'; // 'Health factor is not below the threshold'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {ReserveLogic} from './ReserveLogic.sol';
|
||||
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
|
||||
import {UserConfiguration} from '../configuration/UserConfiguration.sol';
|
||||
|
@ -25,7 +25,6 @@ library GenericLogic {
|
|||
using UserConfiguration for UserConfiguration.Map;
|
||||
|
||||
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
|
||||
uint256 public constant HEALTH_FACTOR_CRITICAL_THRESHOLD = 0.98 ether;
|
||||
|
||||
struct balanceDecreaseAllowedLocalVars {
|
||||
uint256 decimals;
|
||||
|
@ -68,7 +67,9 @@ library GenericLogic {
|
|||
|
||||
balanceDecreaseAllowedLocalVars memory vars;
|
||||
|
||||
(, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset].configuration.getParams();
|
||||
(, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset]
|
||||
.configuration
|
||||
.getParams();
|
||||
|
||||
if (vars.liquidationThreshold == 0) {
|
||||
return true; //if reserve is not used as collateral, no reasons to block the transfer
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {MathUtils} from '../math/MathUtils.sol';
|
||||
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {IAToken} from '../../tokenization/interfaces/IAToken.sol';
|
||||
import {IStableDebtToken} from '../../tokenization/interfaces/IStableDebtToken.sol';
|
||||
import {IVariableDebtToken} from '../../tokenization/interfaces/IVariableDebtToken.sol';
|
||||
|
@ -147,21 +146,22 @@ library ReserveLogic {
|
|||
* a formal specification.
|
||||
* @param reserve the reserve object
|
||||
**/
|
||||
function updateState(ReserveData storage reserve) external {
|
||||
address variableDebtToken = reserve.variableDebtTokenAddress;
|
||||
function updateState(ReserveData storage reserve) internal {
|
||||
uint256 scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
|
||||
.scaledTotalSupply();
|
||||
uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
|
||||
uint256 previousLiquidityIndex = reserve.liquidityIndex;
|
||||
|
||||
(uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes(
|
||||
reserve,
|
||||
variableDebtToken,
|
||||
scaledVariableDebt,
|
||||
previousLiquidityIndex,
|
||||
previousVariableBorrowIndex
|
||||
);
|
||||
|
||||
_mintToTreasury(
|
||||
reserve,
|
||||
variableDebtToken,
|
||||
scaledVariableDebt,
|
||||
previousVariableBorrowIndex,
|
||||
newLiquidityIndex,
|
||||
newVariableBorrowIndex
|
||||
|
@ -179,7 +179,7 @@ library ReserveLogic {
|
|||
ReserveData storage reserve,
|
||||
uint256 totalLiquidity,
|
||||
uint256 amount
|
||||
) external {
|
||||
) internal {
|
||||
uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay());
|
||||
|
||||
uint256 result = amountToLiquidityRatio.add(WadRayMath.ray());
|
||||
|
@ -227,6 +227,7 @@ library ReserveLogic {
|
|||
uint256 newStableRate;
|
||||
uint256 newVariableRate;
|
||||
uint256 avgStableRate;
|
||||
uint256 totalVariableDebt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,7 +243,7 @@ library ReserveLogic {
|
|||
address aTokenAddress,
|
||||
uint256 liquidityAdded,
|
||||
uint256 liquidityTaken
|
||||
) external {
|
||||
) internal {
|
||||
UpdateInterestRatesLocalVars memory vars;
|
||||
|
||||
vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
|
||||
|
@ -250,6 +251,13 @@ library ReserveLogic {
|
|||
(vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress)
|
||||
.getTotalSupplyAndAvgRate();
|
||||
|
||||
//calculates the total variable debt locally using the scaled total supply instead
|
||||
//of totalSupply(), as it's noticeably cheaper. Also, the index has been
|
||||
//updated by the previous updateState() call
|
||||
vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
|
||||
.scaledTotalSupply()
|
||||
.rayMul(reserve.variableBorrowIndex);
|
||||
|
||||
vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress);
|
||||
|
||||
(
|
||||
|
@ -260,7 +268,7 @@ library ReserveLogic {
|
|||
reserveAddress,
|
||||
vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken),
|
||||
vars.totalStableDebt,
|
||||
IERC20(reserve.variableDebtTokenAddress).totalSupply(),
|
||||
vars.totalVariableDebt,
|
||||
vars.avgStableRate,
|
||||
reserve.configuration.getReserveFactor()
|
||||
);
|
||||
|
@ -287,7 +295,6 @@ library ReserveLogic {
|
|||
uint256 principalStableDebt;
|
||||
uint256 previousStableDebt;
|
||||
uint256 currentVariableDebt;
|
||||
uint256 scaledVariableDebt;
|
||||
uint256 previousVariableDebt;
|
||||
uint256 avgStableRate;
|
||||
uint256 cumulatedStableInterest;
|
||||
|
@ -301,14 +308,14 @@ library ReserveLogic {
|
|||
* @dev mints part of the repaid interest to the reserve treasury, depending on the reserveFactor for the
|
||||
* specific asset.
|
||||
* @param reserve the reserve reserve to be updated
|
||||
* @param variableDebtToken the debt token address
|
||||
* @param scaledVariableDebt the current scaled total variable debt
|
||||
* @param previousVariableBorrowIndex the variable borrow index before the last accumulation of the interest
|
||||
* @param newLiquidityIndex the new liquidity index
|
||||
* @param newVariableBorrowIndex the variable borrow index after the last accumulation of the interest
|
||||
**/
|
||||
function _mintToTreasury(
|
||||
ReserveData storage reserve,
|
||||
address variableDebtToken,
|
||||
uint256 scaledVariableDebt,
|
||||
uint256 previousVariableBorrowIndex,
|
||||
uint256 newLiquidityIndex,
|
||||
uint256 newVariableBorrowIndex
|
||||
|
@ -321,9 +328,6 @@ library ReserveLogic {
|
|||
return;
|
||||
}
|
||||
|
||||
//fetching the last scaled total variable debt
|
||||
vars.scaledVariableDebt = IVariableDebtToken(variableDebtToken).scaledTotalSupply();
|
||||
|
||||
//fetching the principal, total stable debt and the avg stable rate
|
||||
(
|
||||
vars.principalStableDebt,
|
||||
|
@ -333,10 +337,10 @@ library ReserveLogic {
|
|||
) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
|
||||
|
||||
//calculate the last principal variable debt
|
||||
vars.previousVariableDebt = vars.scaledVariableDebt.rayMul(previousVariableBorrowIndex);
|
||||
vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex);
|
||||
|
||||
//calculate the new total supply after accumulation of the index
|
||||
vars.currentVariableDebt = vars.scaledVariableDebt.rayMul(newVariableBorrowIndex);
|
||||
vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex);
|
||||
|
||||
//calculate the stable debt until the last timestamp update
|
||||
vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
|
||||
|
@ -355,19 +359,21 @@ library ReserveLogic {
|
|||
|
||||
vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor);
|
||||
|
||||
IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex);
|
||||
if (vars.amountToMint != 0) {
|
||||
IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the reserve indexes and the timestamp of the update
|
||||
* @param reserve the reserve reserve to be updated
|
||||
* @param variableDebtToken the debt token address
|
||||
* @param scaledVariableDebt the scaled variable debt
|
||||
* @param liquidityIndex the last stored liquidity index
|
||||
* @param variableBorrowIndex the last stored variable borrow index
|
||||
**/
|
||||
function _updateIndexes(
|
||||
ReserveData storage reserve,
|
||||
address variableDebtToken,
|
||||
uint256 scaledVariableDebt,
|
||||
uint256 liquidityIndex,
|
||||
uint256 variableBorrowIndex
|
||||
) internal returns (uint256, uint256) {
|
||||
|
@ -391,7 +397,7 @@ library ReserveLogic {
|
|||
|
||||
//as the liquidity rate might come only from stable rate loans, we need to ensure
|
||||
//that there is actual variable debt before accumulating
|
||||
if (IERC20(variableDebtToken).totalSupply() > 0) {
|
||||
if (scaledVariableDebt != 0) {
|
||||
uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
|
||||
reserve.currentVariableBorrowRate,
|
||||
timestamp
|
||||
|
|
|
@ -2,16 +2,15 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {ReserveLogic} from './ReserveLogic.sol';
|
||||
import {GenericLogic} from './GenericLogic.sol';
|
||||
import {WadRayMath} from '../math/WadRayMath.sol';
|
||||
import {PercentageMath} from '../math/PercentageMath.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
|
||||
import {UserConfiguration} from '../configuration/UserConfiguration.sol';
|
||||
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
|
||||
import {Errors} from '../helpers/Errors.sol';
|
||||
import {Helpers} from '../helpers/Helpers.sol';
|
||||
|
||||
|
@ -34,7 +33,7 @@ library ValidationLogic {
|
|||
* @param reserve the reserve state on which the user is depositing
|
||||
* @param amount the amount to be deposited
|
||||
*/
|
||||
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) external view {
|
||||
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) internal view {
|
||||
(bool isActive, bool isFreezed, , ) = reserve.configuration.getFlags();
|
||||
|
||||
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
|
||||
|
@ -57,7 +56,7 @@ library ValidationLogic {
|
|||
mapping(uint256 => address) storage reserves,
|
||||
uint256 reservesCount,
|
||||
address oracle
|
||||
) external view {
|
||||
) internal view {
|
||||
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
|
||||
|
||||
require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
|
||||
|
@ -66,7 +65,7 @@ library ValidationLogic {
|
|||
GenericLogic.balanceDecreaseAllowed(
|
||||
reserveAddress,
|
||||
msg.sender,
|
||||
userBalance,
|
||||
amount,
|
||||
reservesData,
|
||||
userConfig,
|
||||
reserves,
|
||||
|
@ -328,11 +327,16 @@ library ValidationLogic {
|
|||
/**
|
||||
* @dev validates a flashloan action
|
||||
* @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan)
|
||||
* @param premium the premium paid on the flashloan
|
||||
* @param assets the assets being flashborrowed
|
||||
* @param amounts the amounts for each asset being borrowed
|
||||
**/
|
||||
function validateFlashloan(uint256 mode, uint256 premium) internal pure {
|
||||
require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL);
|
||||
function validateFlashloan(
|
||||
address[] memory assets,
|
||||
uint256[] memory amounts,
|
||||
uint256 mode
|
||||
) internal pure {
|
||||
require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
|
||||
require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -385,95 +389,4 @@ library ValidationLogic {
|
|||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates the repayWithCollateral() action
|
||||
* @param collateralReserve The reserve data of the collateral
|
||||
* @param principalReserve The reserve data of the principal
|
||||
* @param userConfig The user configuration
|
||||
* @param user The address of the user
|
||||
* @param userHealthFactor The user's health factor
|
||||
* @param userStableDebt Total stable debt balance of the user
|
||||
* @param userVariableDebt Total variable debt balance of the user
|
||||
**/
|
||||
function validateRepayWithCollateral(
|
||||
ReserveLogic.ReserveData storage collateralReserve,
|
||||
ReserveLogic.ReserveData storage principalReserve,
|
||||
UserConfiguration.Map storage userConfig,
|
||||
address user,
|
||||
uint256 userHealthFactor,
|
||||
uint256 userStableDebt,
|
||||
uint256 userVariableDebt
|
||||
) internal view returns (uint256, string memory) {
|
||||
if (
|
||||
!collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()
|
||||
) {
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE);
|
||||
}
|
||||
|
||||
if (
|
||||
msg.sender != user && userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD
|
||||
) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
|
||||
Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
if (msg.sender != user) {
|
||||
bool isCollateralEnabled = collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
||||
userConfig.isUsingAsCollateral(collateralReserve.id);
|
||||
|
||||
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
||||
if (!isCollateralEnabled) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
|
||||
Errors.COLLATERAL_CANNOT_BE_LIQUIDATED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (userStableDebt == 0 && userVariableDebt == 0) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED),
|
||||
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates the swapLiquidity() action
|
||||
* @param fromReserve The reserve data of the asset to swap from
|
||||
* @param toReserve The reserve data of the asset to swap to
|
||||
* @param fromAsset Address of the asset to swap from
|
||||
* @param toAsset Address of the asset to swap to
|
||||
**/
|
||||
function validateSwapLiquidity(
|
||||
ReserveLogic.ReserveData storage fromReserve,
|
||||
ReserveLogic.ReserveData storage toReserve,
|
||||
address fromAsset,
|
||||
address toAsset
|
||||
) internal view returns (uint256, string memory) {
|
||||
if (fromAsset == toAsset) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.INVALID_EQUAL_ASSETS_TO_SWAP),
|
||||
Errors.INVALID_EQUAL_ASSETS_TO_SWAP
|
||||
);
|
||||
}
|
||||
|
||||
(bool isToActive, bool isToFreezed, , ) = toReserve.configuration.getFlags();
|
||||
if (!fromReserve.configuration.getActive() || !isToActive) {
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE);
|
||||
}
|
||||
if (isToFreezed) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.NO_UNFREEZED_RESERVE),
|
||||
Errors.NO_UNFREEZED_RESERVE
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {WadRayMath} from './WadRayMath.sol';
|
||||
|
||||
library MathUtils {
|
||||
using SafeMath for uint256;
|
||||
using WadRayMath for uint256;
|
||||
|
||||
/// @dev Ignoring leap years
|
||||
uint256 internal constant SECONDS_PER_YEAR = 365 days;
|
||||
|
||||
/**
|
||||
|
@ -25,9 +26,7 @@ library MathUtils {
|
|||
//solium-disable-next-line
|
||||
uint256 timeDifference = block.timestamp.sub(uint256(lastUpdateTimestamp));
|
||||
|
||||
uint256 timeDelta = timeDifference.wadToRay().rayDiv(SECONDS_PER_YEAR.wadToRay());
|
||||
|
||||
return rate.rayMul(timeDelta).add(WadRayMath.ray());
|
||||
return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,13 +58,9 @@ library WadRayMath {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint256 result = a * b;
|
||||
uint256 result = a * b + halfWAD;
|
||||
|
||||
require(result / a == b, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
result += halfWAD;
|
||||
|
||||
require(result >= halfWAD, Errors.ADDITION_OVERFLOW);
|
||||
require(result >= halfWAD && (result - halfWAD) / a == b, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
return result / WAD;
|
||||
}
|
||||
|
@ -80,13 +76,9 @@ library WadRayMath {
|
|||
|
||||
uint256 halfB = b / 2;
|
||||
|
||||
uint256 result = a * WAD;
|
||||
uint256 result = a * WAD + halfB;
|
||||
|
||||
require(result / WAD == a, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
result += halfB;
|
||||
|
||||
require(result >= halfB, Errors.ADDITION_OVERFLOW);
|
||||
require(result >= halfB && (result - halfB) / WAD == a, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
return result / b;
|
||||
}
|
||||
|
@ -102,13 +94,9 @@ library WadRayMath {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint256 result = a * b;
|
||||
uint256 result = a * b + halfRAY;
|
||||
|
||||
require(result / a == b, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
result += halfRAY;
|
||||
|
||||
require(result >= halfRAY, Errors.ADDITION_OVERFLOW);
|
||||
require(result >= halfRAY && (result - halfRAY) / a == b, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
return result / RAY;
|
||||
}
|
||||
|
@ -124,13 +112,9 @@ library WadRayMath {
|
|||
|
||||
uint256 halfB = b / 2;
|
||||
|
||||
uint256 result = a * RAY;
|
||||
uint256 result = a * RAY + halfB;
|
||||
|
||||
require(result / RAY == a, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
result += halfB;
|
||||
|
||||
require(result >= halfB, Errors.ADDITION_OVERFLOW);
|
||||
require(result >= halfB && (result - halfB) / RAY == a, Errors.MULTIPLICATION_OVERFLOW);
|
||||
|
||||
return result / b;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ pragma solidity ^0.6.8;
|
|||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol';
|
||||
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
|
||||
/// @title ChainlinkProxyPriceProvider
|
||||
/// @author Aave
|
||||
|
|
|
@ -3,12 +3,12 @@ pragma solidity ^0.6.8;
|
|||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {Address} from '../dependencies/openzeppelin/contracts/Address.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
import {LendingPoolAddressesProvider} from '../configuration/LendingPoolAddressesProvider.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
import {FlashLoanReceiverBase} from '../../flashloan/base/FlashLoanReceiverBase.sol';
|
||||
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
|
||||
contract MockFlashLoanReceiver is FlashLoanReceiverBase {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
ILendingPoolAddressesProvider internal _provider;
|
||||
|
||||
event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee);
|
||||
event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee);
|
||||
event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums);
|
||||
event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums);
|
||||
|
||||
bool _failExecution;
|
||||
uint256 _amountToApprove;
|
||||
|
@ -45,33 +44,40 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
|
|||
}
|
||||
|
||||
function executeOperation(
|
||||
address reserve,
|
||||
uint256 amount,
|
||||
uint256 fee,
|
||||
address[] memory assets,
|
||||
uint256[] memory amounts,
|
||||
uint256[] memory premiums,
|
||||
bytes memory params
|
||||
) public override returns (bool) {
|
||||
params;
|
||||
//mint to this contract the specific amount
|
||||
MintableERC20 token = MintableERC20(reserve);
|
||||
|
||||
//check the contract has the specified balance
|
||||
require(amount <= IERC20(reserve).balanceOf(address(this)), 'Invalid balance for the contract');
|
||||
|
||||
uint256 amountToReturn = (_amountToApprove != 0) ? _amountToApprove : amount.add(fee);
|
||||
|
||||
if (_failExecution) {
|
||||
emit ExecutedWithFail(reserve, amount, fee);
|
||||
emit ExecutedWithFail(assets, amounts, premiums);
|
||||
return !_simulateEOA;
|
||||
}
|
||||
|
||||
//execution does not fail - mint tokens and return them to the _destination
|
||||
//note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation
|
||||
for (uint256 i = 0; i < assets.length; i++) {
|
||||
//mint to this contract the specific amount
|
||||
MintableERC20 token = MintableERC20(assets[i]);
|
||||
|
||||
token.mint(fee);
|
||||
//check the contract has the specified balance
|
||||
require(
|
||||
amounts[i] <= IERC20(assets[i]).balanceOf(address(this)),
|
||||
'Invalid balance for the contract'
|
||||
);
|
||||
|
||||
IERC20(reserve).approve(_addressesProvider.getLendingPool(), amountToReturn);
|
||||
uint256 amountToReturn = (_amountToApprove != 0)
|
||||
? _amountToApprove
|
||||
: amounts[i].add(premiums[i]);
|
||||
//execution does not fail - mint tokens and return them to the _destination
|
||||
//note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation
|
||||
|
||||
emit ExecutedWithSuccess(reserve, amount, fee);
|
||||
token.mint(premiums[i]);
|
||||
|
||||
IERC20(assets[i]).approve(_addressesProvider.getLendingPool(), amountToReturn);
|
||||
}
|
||||
|
||||
emit ExecutedWithSuccess(assets, amounts, premiums);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {ISwapAdapter} from '../../interfaces/ISwapAdapter.sol';
|
||||
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
|
||||
contract MockSwapAdapter is ISwapAdapter {
|
||||
uint256 internal _amountToReturn;
|
||||
bool internal _tryReentrancy;
|
||||
ILendingPoolAddressesProvider public addressesProvider;
|
||||
|
||||
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||
|
||||
constructor(ILendingPoolAddressesProvider provider) public {
|
||||
addressesProvider = provider;
|
||||
}
|
||||
|
||||
function setAmountToReturn(uint256 amount) public {
|
||||
_amountToReturn = amount;
|
||||
}
|
||||
|
||||
function setTryReentrancy(bool tryReentrancy) public {
|
||||
_tryReentrancy = tryReentrancy;
|
||||
}
|
||||
|
||||
function executeOperation(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
address fundsDestination,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
params;
|
||||
IERC20(assetToSwapFrom).transfer(address(1), amountToSwap); // We don't want to keep funds here
|
||||
MintableERC20(assetToSwapTo).mint(_amountToReturn);
|
||||
IERC20(assetToSwapTo).approve(fundsDestination, _amountToReturn);
|
||||
|
||||
if (_tryReentrancy) {
|
||||
ILendingPool(fundsDestination).repayWithCollateral(
|
||||
assetToSwapFrom,
|
||||
assetToSwapTo,
|
||||
address(1), // Doesn't matter, we just want to test the reentrancy
|
||||
1 ether, // Same
|
||||
address(1), // Same
|
||||
'0x'
|
||||
);
|
||||
}
|
||||
|
||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amountToSwap, _amountToReturn);
|
||||
}
|
||||
|
||||
function burnAsset(IERC20 asset, uint256 amount) public {
|
||||
uint256 amountToBurn = (amount == type(uint256).max) ? asset.balanceOf(address(this)) : amount;
|
||||
asset.transfer(address(0), amountToBurn);
|
||||
}
|
||||
}
|
|
@ -14,4 +14,13 @@ contract MockAggregator {
|
|||
function latestAnswer() external view returns (int256) {
|
||||
return _latestAnswer;
|
||||
}
|
||||
|
||||
function getTokenType() external view returns (uint256) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// function getSubTokens() external view returns (address[] memory) {
|
||||
// TODO: implement mock for when multiple subtokens. Maybe we need to create diff mock contract
|
||||
// to call it from the migration for this case??
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
|
||||
|
||||
contract LendingRateOracle is ILendingRateOracle, Ownable {
|
||||
mapping(address => uint256) borrowRates;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
|
||||
import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
|
||||
|
||||
/**
|
||||
* @title ERC20Mintable
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import {StableDebtToken} from '../../tokenization/StableDebtToken.sol';
|
||||
import {LendingPool} from '../../lendingpool/LendingPool.sol';
|
||||
|
||||
contract MockStableDebtToken is StableDebtToken {
|
||||
constructor(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import {VariableDebtToken} from '../../tokenization/VariableDebtToken.sol';
|
||||
import {LendingPool} from '../../lendingpool/LendingPool.sol';
|
||||
|
||||
contract MockVariableDebtToken is VariableDebtToken {
|
||||
constructor(
|
||||
|
|
|
@ -2,15 +2,13 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import {IncentivizedERC20} from './IncentivizedERC20.sol';
|
||||
import {LendingPool} from '../lendingpool/LendingPool.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
|
||||
import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {IAToken} from './interfaces/IAToken.sol';
|
||||
import {IERC20} from '../interfaces/IERC20.sol';
|
||||
import {SafeERC20} from '../misc/SafeERC20.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
|
||||
/**
|
||||
* @title Aave ERC20 AToken
|
||||
|
@ -34,7 +32,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
|
|||
uint256 public constant ATOKEN_REVISION = 0x1;
|
||||
address public immutable UNDERLYING_ASSET_ADDRESS;
|
||||
address public immutable RESERVE_TREASURY_ADDRESS;
|
||||
LendingPool public immutable POOL;
|
||||
ILendingPool public immutable POOL;
|
||||
|
||||
/// @dev owner => next valid nonce to submit with permit()
|
||||
mapping(address => uint256) public _nonces;
|
||||
|
@ -47,7 +45,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
|
|||
}
|
||||
|
||||
constructor(
|
||||
LendingPool pool,
|
||||
ILendingPool pool,
|
||||
address underlyingAssetAddress,
|
||||
address reserveTreasuryAddress,
|
||||
string memory tokenName,
|
||||
|
@ -119,12 +117,15 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
|
|||
* @param user the address receiving the minted tokens
|
||||
* @param amount the amount of tokens to mint
|
||||
* @param index the the last index of the reserve
|
||||
* @return true if the the previous balance of the user is 0
|
||||
*/
|
||||
function mint(
|
||||
address user,
|
||||
uint256 amount,
|
||||
uint256 index
|
||||
) external override onlyLendingPool {
|
||||
) external override onlyLendingPool returns (bool) {
|
||||
uint256 previousBalance = super.balanceOf(user);
|
||||
|
||||
uint256 amountScaled = amount.rayDiv(index);
|
||||
require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
|
||||
_mint(user, amountScaled);
|
||||
|
@ -132,6 +133,8 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
|
|||
//transfer event to track balances
|
||||
emit Transfer(address(0), user, amount);
|
||||
emit Mint(user, amount, index);
|
||||
|
||||
return previousBalance == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.8;
|
||||
|
||||
import {Context} from '../misc/Context.sol';
|
||||
import {IERC20} from '../interfaces/IERC20.sol';
|
||||
import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol';
|
||||
import {SafeMath} from '../libraries/math/SafeMath.sol';
|
||||
import {Context} from '../dependencies/openzeppelin/contracts/Context.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
|
||||
|
||||
/**
|
||||
|
@ -181,10 +181,10 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
|
|||
_balances[recipient] = _balances[recipient].add(amount);
|
||||
|
||||
if (address(_incentivesController) != address(0)) {
|
||||
uint256 totalSupply = _totalSupply;
|
||||
_incentivesController.handleAction(sender, totalSupply, oldSenderBalance);
|
||||
uint256 currentTotalSupply = _totalSupply;
|
||||
_incentivesController.handleAction(sender, currentTotalSupply, oldSenderBalance);
|
||||
if (sender != recipient) {
|
||||
_incentivesController.handleAction(recipient, totalSupply, oldRecipientBalance);
|
||||
_incentivesController.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Context} from '@openzeppelin/contracts/GSN/Context.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {DebtTokenBase} from './base/DebtTokenBase.sol';
|
||||
import {MathUtils} from '../libraries/math/MathUtils.sol';
|
||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
|
@ -131,7 +128,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
_totalSupplyTimestamp = _timestamps[user] = uint40(block.timestamp);
|
||||
|
||||
//calculates the updated average stable rate
|
||||
_avgStableRate = vars
|
||||
vars.currentAvgStableRate = _avgStableRate = vars
|
||||
.currentAvgStableRate
|
||||
.rayMul(vars.previousSupply.wadToRay())
|
||||
.add(rate.rayMul(vars.amountInRay))
|
||||
|
@ -142,7 +139,15 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
// transfer event to track balances
|
||||
emit Transfer(address(0), user, amount);
|
||||
|
||||
emit Mint(user, amount, previousBalance, currentBalance, balanceIncrease, vars.newStableRate);
|
||||
emit Mint(
|
||||
user,
|
||||
amount,
|
||||
previousBalance,
|
||||
currentBalance,
|
||||
balanceIncrease,
|
||||
vars.newStableRate,
|
||||
vars.currentAvgStableRate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,17 +163,18 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
) = _calculateBalanceIncrease(user);
|
||||
|
||||
uint256 previousSupply = totalSupply();
|
||||
uint256 newStableRate = 0;
|
||||
|
||||
//since the total supply and each single user debt accrue separately,
|
||||
//there might be accumulation errors so that the last borrower repaying
|
||||
//might actually try to repay more than the available debt supply.
|
||||
//in this case we simply set the total supply and the avg stable rate to 0
|
||||
if (previousSupply <= amount) {
|
||||
_avgStableRate = 0;
|
||||
newStableRate = _avgStableRate = 0;
|
||||
_totalSupply = 0;
|
||||
} else {
|
||||
uint256 nextSupply = _totalSupply = previousSupply.sub(amount);
|
||||
_avgStableRate = _avgStableRate
|
||||
newStableRate = _avgStableRate = _avgStableRate
|
||||
.rayMul(previousSupply.wadToRay())
|
||||
.sub(_usersData[user].rayMul(amount.wadToRay()))
|
||||
.rayDiv(nextSupply.wadToRay());
|
||||
|
@ -193,14 +199,13 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
// transfer event to track balances
|
||||
emit Transfer(user, address(0), amount);
|
||||
|
||||
emit Burn(user, amount, previousBalance, currentBalance, balanceIncrease);
|
||||
emit Burn(user, amount, previousBalance, currentBalance, balanceIncrease, newStableRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates the increase in balance since the last user interaction
|
||||
* @param user The address of the user for which the interest is being accumulated
|
||||
* @return The previous principal balance, the new principal balance, the balance increase
|
||||
* and the new user index
|
||||
* @return The previous principal balance, the new principal balance and the balance increase
|
||||
**/
|
||||
function _calculateBalanceIncrease(address user)
|
||||
internal
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Context} from '@openzeppelin/contracts/GSN/Context.sol';
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {DebtTokenBase} from './base/DebtTokenBase.sol';
|
||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
import {IVariableDebtToken} from './interfaces/IVariableDebtToken.sol';
|
||||
|
@ -46,7 +43,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET));
|
||||
return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,12 +51,14 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
|
|||
* @param user the user receiving the debt
|
||||
* @param amount the amount of debt being minted
|
||||
* @param index the variable debt index of the reserve
|
||||
* @return true if the the previous balance of the user is 0
|
||||
**/
|
||||
function mint(
|
||||
address user,
|
||||
uint256 amount,
|
||||
uint256 index
|
||||
) external override onlyLendingPool {
|
||||
) external override onlyLendingPool returns (bool) {
|
||||
uint256 previousBalance = super.balanceOf(user);
|
||||
uint256 amountScaled = amount.rayDiv(index);
|
||||
require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
|
||||
|
||||
|
@ -67,6 +66,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
|
|||
|
||||
emit Transfer(address(0), user, amount);
|
||||
emit Mint(user, amount, index);
|
||||
|
||||
return previousBalance == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +102,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
|
|||
* @return the total supply
|
||||
**/
|
||||
function totalSupply() public virtual override view returns (uint256) {
|
||||
return super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET));
|
||||
return
|
||||
super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {Context} from '@openzeppelin/contracts/GSN/Context.sol';
|
||||
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
|
||||
} from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {IncentivizedERC20} from '../IncentivizedERC20.sol';
|
||||
import {Errors} from '../../libraries/helpers/Errors.sol';
|
||||
|
||||
|
@ -18,8 +15,8 @@ import {Errors} from '../../libraries/helpers/Errors.sol';
|
|||
*/
|
||||
|
||||
abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
||||
address internal immutable UNDERLYING_ASSET;
|
||||
ILendingPool internal immutable POOL;
|
||||
address public immutable UNDERLYING_ASSET_ADDRESS;
|
||||
ILendingPool public immutable POOL;
|
||||
mapping(address => uint256) internal _usersData;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +39,7 @@ abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
|||
address incentivesController
|
||||
) public IncentivizedERC20(name, symbol, 18, incentivesController) {
|
||||
POOL = ILendingPool(pool);
|
||||
UNDERLYING_ASSET = underlyingAssetAddress;
|
||||
UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,10 +58,6 @@ abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
|||
_setDecimals(decimals);
|
||||
}
|
||||
|
||||
function underlyingAssetAddress() public view returns (address) {
|
||||
return UNDERLYING_ASSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Being non transferrable, the debt token does not implement any of the
|
||||
* standard ERC20 functions for transfer and allowance.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {IERC20} from '../../interfaces/IERC20.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
|
||||
|
||||
interface IAToken is IERC20, IScaledBalanceToken {
|
||||
|
|
|
@ -21,7 +21,7 @@ interface IScaledBalanceToken {
|
|||
address user,
|
||||
uint256 amount,
|
||||
uint256 index
|
||||
) external;
|
||||
) external returns (bool);
|
||||
|
||||
/**
|
||||
* @dev returns the principal balance of the user. The principal balance is the last
|
||||
|
|
|
@ -21,6 +21,7 @@ interface IStableDebtToken {
|
|||
* @param currentBalance the current balance of the user
|
||||
* @param balanceIncrease the debt increase since the last update
|
||||
* @param newRate the rate of the debt after the minting
|
||||
* @param avgStableRate the new average stable rate after the minting
|
||||
**/
|
||||
event Mint(
|
||||
address indexed user,
|
||||
|
@ -28,7 +29,8 @@ interface IStableDebtToken {
|
|||
uint256 previousBalance,
|
||||
uint256 currentBalance,
|
||||
uint256 balanceIncrease,
|
||||
uint256 newRate
|
||||
uint256 newRate,
|
||||
uint256 avgStableRate
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -38,13 +40,15 @@ interface IStableDebtToken {
|
|||
* @param previousBalance the previous balance of the user
|
||||
* @param currentBalance the current balance of the user
|
||||
* @param balanceIncrease the debt increase since the last update
|
||||
* @param avgStableRate the new average stable rate after the minting
|
||||
**/
|
||||
event Burn(
|
||||
address indexed user,
|
||||
uint256 amount,
|
||||
uint256 previousBalance,
|
||||
uint256 currentBalance,
|
||||
uint256 balanceIncrease
|
||||
uint256 balanceIncrease,
|
||||
uint256 avgStableRate
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
13
contracts/tokenization/interfaces/ITokenConfiguration.sol
Normal file
13
contracts/tokenization/interfaces/ITokenConfiguration.sol
Normal file
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.6;
|
||||
|
||||
/**
|
||||
* @title ITokenConfiguration
|
||||
* @author Aave
|
||||
* @dev common interface between aTokens and debt tokens to fetch the
|
||||
* token configuration
|
||||
**/
|
||||
interface ITokenConfiguration {
|
||||
function UNDERLYING_ASSET_ADDRESS() external view returns (address);
|
||||
|
||||
function POOL() external view returns (address);
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x58F132FBB86E21545A4Bace3C19f1C05d86d7A22",
|
||||
"address": "0xDFbeeed692AA81E7f86E72F7ACbEA2A1C4d63544",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xa4bcDF64Cdd5451b6ac3743B414124A6299B65FF",
|
||||
"address": "0x5191aA68c7dB195181Dd2441dBE23A48EA24b040",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -27,7 +27,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x20e080B395341B3b617E893c281c7E999C942276",
|
||||
"address": "0xF9a2E6D57c691f3aa5269858178a13Ef06378579",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
|
@ -37,7 +37,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x5A0773Ff307Bf7C71a832dBB5312237fD3437f9F",
|
||||
"address": "0xa89E20284Bd638F31b0011D0fC754Fc9d2fa73e3",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -45,7 +45,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x00219a2958f758122106Bb8A801AFc1B70897663",
|
||||
"address": "0xf3266d89e6742fAE2C35D05eD549cd4e117300a7",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
|
@ -73,10 +73,10 @@
|
|||
"address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x65e0Cd5B8904A02f2e00BC6f58bf881998D54BDe"
|
||||
"address": "0x9Ec55627757348b322c8dD0865D704649bFa0c7b"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x50C9d3aD9399c1EEf6DDeadF8e57fF69994F552e"
|
||||
"address": "0x1aae278bCcdb95817c7A546d752fC662F09b6DBa"
|
||||
}
|
||||
},
|
||||
"LendingPoolDataProvider": {
|
||||
|
@ -89,10 +89,10 @@
|
|||
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x5d12dDe3286D94E0d85F9D3B01B7099cfA0aBCf1"
|
||||
"address": "0x3EE716e38f21e5FC16BFDB773db24D63C637A5d8"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x6d1e69bB0578699dd955Eefbf23aAC65c0DA5cE7"
|
||||
"address": "0x8E05A3054cb736258FaF4638D07058cE6e294d2C"
|
||||
}
|
||||
},
|
||||
"PriceOracle": {
|
||||
|
@ -101,7 +101,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x1750499D05Ed1674d822430FB960d5F6731fDf64",
|
||||
"address": "0x5889354f21A1C8D8D2f82669d778f6Dab778B519",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -115,7 +115,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89",
|
||||
"address": "0xC452C5244F701108B4e8E8BCe693160046b30332",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -129,7 +129,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7B6C3e5486D9e6959441ab554A889099eed76290",
|
||||
"address": "0x0B63c002cb44B2e5e580C3B3560a27F4101D95c0",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -147,7 +147,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xD83D2773a7873ae2b5f8Fb92097e20a8C64F691E",
|
||||
"address": "0xCeB290A2C6614BF23B2faa0f0B8067F29C48DB0F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -165,12 +165,16 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x626FdE749F9d499d3777320CAf29484B624ab84a",
|
||||
"address": "0x7C95b1ad025F0C9aB14192f87bF2aD53889bE4F7",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x626FdE749F9d499d3777320CAf29484B624ab84a",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x47341CE48FfE1cbD91991578B880a18c45cdB5CA",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
"LendingPoolLiquidationManager": {
|
||||
|
@ -223,7 +227,7 @@
|
|||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA"
|
||||
"address": "0x9c91aEaD98b1354C7B0EAfb8ff539d0796c79894"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA"
|
||||
|
@ -231,11 +235,11 @@
|
|||
},
|
||||
"WalletBalanceProvider": {
|
||||
"buidlerevm": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4",
|
||||
"address": "0xDf73fC454FA018051D4a1509e63D11530A59DE10",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xDf73fC454FA018051D4a1509e63D11530A59DE10",
|
||||
"address": "0x145b7B6368Df63e7F3497b0A948B30fC1A4d5E55",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -249,7 +253,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F",
|
||||
"address": "0x010e948B9B7D30771E23346C0B17a4D5Ff04e300",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -263,7 +267,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x8858eeB3DfffA017D4BCE9801D340D36Cf895CCf",
|
||||
"address": "0x79094eDB848047e87a4B8a64ab5Ee2f527791bC0",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -277,7 +281,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x0078371BDeDE8aAc7DeBfFf451B74c5EDB385Af7",
|
||||
"address": "0xEE0A69d0Bb1312685870Dd7E20AcAD66b6f6264F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -291,7 +295,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xf4e77E5Da47AC3125140c470c71cBca77B5c638c",
|
||||
"address": "0x631B367fBE1dbB934bC039aAA0C9eC2EE5943fd5",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -305,7 +309,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x3619DbE27d7c1e7E91aA738697Ae7Bc5FC3eACA5",
|
||||
"address": "0xf55Af78B3f3059fACF166Aa338FFe059A14e75F6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -319,7 +323,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x038B86d9d8FAFdd0a02ebd1A476432877b0107C8",
|
||||
"address": "0xD5A0587aAEB195028909E98930B391dFB3f9F589",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -333,7 +337,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x1A1FEe7EeD918BD762173e4dc5EfDB8a78C924A8",
|
||||
"address": "0xaD3AdbC18E4AD090034A6C74Eda61f4310dce313",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -347,7 +351,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e",
|
||||
"address": "0x25a88BbA9c8D2a46e3Ff4bFe98712DF7A1044fB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -361,7 +365,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xc4905364b78a742ccce7B890A89514061E47068D",
|
||||
"address": "0x16d1802cd7cfcb67955BBBa26bAae1cE559B5F5B",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -375,7 +379,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe",
|
||||
"address": "0xE58d8c88f5A670f16BE8F7864707170F43e943A6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -389,7 +393,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x8B5B7a6055E54a36fF574bbE40cf2eA68d5554b3",
|
||||
"address": "0xfdAF4f6e47e854c05bE158993d32872e784F0502",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -403,7 +407,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xEcc0a6dbC0bb4D51E4F84A315a9e5B0438cAD4f0",
|
||||
"address": "0x92edC13A10036A3C50396f2B63148a3e9a8D589e",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -417,7 +421,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x20Ce94F404343aD2752A2D01b43fa407db9E0D00",
|
||||
"address": "0xE5C277cDb7E10372918Ac54Ce54022910A24FE88",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -431,7 +435,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x1d80315fac6aBd3EfeEbE97dEc44461ba7556160",
|
||||
"address": "0xF5742a599a0F4520089cbf2EBBa66Bb4F471B85F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -445,7 +449,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x2D8553F9ddA85A9B3259F6Bf26911364B85556F5",
|
||||
"address": "0x380EF388e13D8cAdeACef6eF682C7B7D85865076",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -459,7 +463,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x52d3b94181f8654db2530b0fEe1B19173f519C52",
|
||||
"address": "0xC89577DED8441e52C17C13D527b85b225C5c8311",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -473,7 +477,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xd15468525c35BDBC1eD8F2e09A00F8a173437f2f",
|
||||
"address": "0xD4b06774A717Ff5A7c20c8712e31c6BbfFcb1F01",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -487,7 +491,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7e35Eaf7e8FBd7887ad538D4A38Df5BbD073814a",
|
||||
"address": "0xbe66dC9DFEe580ED968403e35dF7b5159f873df8",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -501,7 +505,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x5bcb88A0d20426e451332eE6C4324b0e663c50E0",
|
||||
"address": "0x93AfC6Df4bB8F62F2493B19e577f8382c0BA9EBC",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -515,7 +519,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x3521eF8AaB0323004A6dD8b03CE890F4Ea3A13f5",
|
||||
"address": "0x75Ded61646B5945BdDd0CD9a9Db7c8288DA6F810",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -529,7 +533,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x53369fd4680FfE3DfF39Fc6DDa9CfbfD43daeA2E",
|
||||
"address": "0xdE7c40e675bF1aA45c18cCbaEb9662B16b0Ddf7E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -543,7 +547,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xB00cC45B4a7d3e1FEE684cFc4417998A1c183e6d",
|
||||
"address": "0xEcb928A3c079a1696Aa5244779eEc3dE1717fACd",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -557,58 +561,81 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x58F132FBB86E21545A4Bace3C19f1C05d86d7A22",
|
||||
"address": "0xDFbeeed692AA81E7f86E72F7ACbEA2A1C4d63544",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"LendingPoolAddressesProviderRegistry": {
|
||||
"buidlerevm": {
|
||||
"address": "0x5A0773Ff307Bf7C71a832dBB5312237fD3437f9F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"LendingPool": {
|
||||
"buidlerevm": {
|
||||
"address": "0xe7536f450378748E1BD4645D3c77ec38e0F3ba28"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x987223924D2DD6c6efB601756850f3886ECbceF6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0xfF28b837352d9531bAb6dFF3650D7831192117F7",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
"LendingPoolConfigurator": {
|
||||
"buidlerevm": {
|
||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xaca5aCeB6f44845d07Fd339a51F0bd52Bb3D8D1A",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x58F132FBB86E21545A4Bace3C19f1C05d86d7A22",
|
||||
"address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x0EDc241FdA0dF39EB1B9eB1236217BBe72Ab911D",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
"PriceOracle": {
|
||||
"buidlerevm": {
|
||||
"address": "0x1750499D05Ed1674d822430FB960d5F6731fDf64",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x9bD0Bec44106D8Ea8fFb6296d7A84742a290E064",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"ChainlinkProxyPriceProvider": {
|
||||
"buidlerevm": {
|
||||
"address": "0x7B6C3e5486D9e6959441ab554A889099eed76290",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x293f5BcC66762c28a5d3Bd8512a799D457F5296D",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
"AToken": {
|
||||
"localhost": {
|
||||
"address": "0x00f126cCA2266bFb634Ed6DB17c4C74fb8cA5177",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"AaveProtocolTestHelpers": {
|
||||
"buidlerevm": {
|
||||
"address": "0xe7536f450378748E1BD4645D3c77ec38e0F3ba28"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||
"address": "0x689d0586F88cDEa1E1003F838DD943415E4EB1e5"
|
||||
}
|
||||
},
|
||||
"StableDebtToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"VariableDebtToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"AToken": {
|
||||
"localhost": {
|
||||
"address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"DefaultReserveInterestRateStrategy": {
|
||||
"buidlerevm": {
|
||||
"address": "0x5f7134cd38C826a7649f9Cc47dda24d834DD2967",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
|
@ -616,15 +643,19 @@
|
|||
"coverage": {
|
||||
"address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0xf303Ae6F24C29D94E367fdb5C7aE04D32BEbF13E",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
}
|
||||
},
|
||||
"MockAToken": {
|
||||
"StableDebtToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0x392E5355a0e88Bd394F717227c752670fb3a8020",
|
||||
"address": "0x3bDA11B584dDff7F66E0cFe1da1562c92B45db60",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xa89E20284Bd638F31b0011D0fC754Fc9d2fa73e3",
|
||||
"address": "0xbF538F34cb100bAeEE55aa1F036D33F03b03d900",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -632,13 +663,13 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"WETH": {
|
||||
"VariableDebtToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0xf784709d2317D872237C4bC22f867d1BAe2913AB",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xf784709d2317D872237C4bC22f867d1BAe2913AB",
|
||||
"address": "0xff1B1B810F5DCe853a9b1819DE220D532D1CFeF2",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -646,13 +677,13 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"MockStableDebtToken": {
|
||||
"AToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460",
|
||||
"address": "0x392E5355a0e88Bd394F717227c752670fb3a8020",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xaA935993065F2dDB1d13623B1941C7AEE3A60F23",
|
||||
"address": "0x7436d6adaA697413F00cb63E1A2A854bF2Aec5A1",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -660,13 +691,13 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"MockVariableDebtToken": {
|
||||
"MockFlashLoanReceiver": {
|
||||
"buidlerevm": {
|
||||
"address": "0xEBAB67ee3ef604D5c250A53b4b8fcbBC6ec3007C",
|
||||
"address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x35A2624888e207e4B3434E9a9E250bF6Ee68FeA3",
|
||||
"address": "0x2A7BE996B8801ED21f2f45148791D402811A2106",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -674,7 +705,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"MockSwapAdapter": {
|
||||
"WalletBalanceProvider": {
|
||||
"buidlerevm": {
|
||||
"address": "0xDf73fC454FA018051D4a1509e63D11530A59DE10"
|
||||
},
|
||||
|
@ -682,7 +713,17 @@
|
|||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||
"address": "0x48FAde2E719B770E1783d03466dAEe98b5183538"
|
||||
}
|
||||
},
|
||||
"MockFlashRepayAdapter": {
|
||||
"buidlerevm": {
|
||||
"address": "0xDf73fC454FA018051D4a1509e63D11530A59DE10"
|
||||
}
|
||||
},
|
||||
"MockFlashLiquiditySwapAdapter": {
|
||||
"buidlerevm": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ version: '3.5'
|
|||
|
||||
services:
|
||||
contracts-env:
|
||||
env_file:
|
||||
- .env
|
||||
build:
|
||||
context: ./
|
||||
working_dir: /src
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Contract, Signer, utils, ethers} from 'ethers';
|
||||
import {CommonsConfig} from '../config/commons';
|
||||
import {getDb, BRE} from './misc-utils';
|
||||
import {getDb, BRE, waitForTx} from './misc-utils';
|
||||
import {
|
||||
tEthereumAddress,
|
||||
eContractid,
|
||||
|
@ -47,7 +47,6 @@ const {
|
|||
|
||||
export type MockTokenMap = {[symbol: string]: MintableERC20};
|
||||
import {ZERO_ADDRESS} from './constants';
|
||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
||||
import {signTypedData_v4, TypedData} from 'eth-sig-util';
|
||||
import {fromRpcSig, ECDSASignature} from 'ethereumjs-util';
|
||||
import {SignerWithAddress} from '../test/helpers/make-suite';
|
||||
|
@ -101,7 +100,7 @@ export const deployContract = async <ContractType extends Contract>(
|
|||
const contract = (await (await BRE.ethers.getContractFactory(contractName)).deploy(
|
||||
...args
|
||||
)) as ContractType;
|
||||
|
||||
await waitForTx(contract.deployTransaction);
|
||||
await registerContractInJsonDb(<eContractid>contractName, contract);
|
||||
return contract;
|
||||
};
|
||||
|
@ -205,6 +204,7 @@ export const deployLendingPool = async (verify?: boolean) => {
|
|||
);
|
||||
const factory = await linkLibrariesToArtifact(lendingPoolArtifact);
|
||||
const lendingPool = await factory.deploy();
|
||||
await waitForTx(lendingPool.deployTransaction);
|
||||
const instance = (await lendingPool.deployed()) as LendingPool;
|
||||
if (verify) {
|
||||
await verifyContract(eContractid.LendingPool, instance.address, []);
|
||||
|
@ -321,8 +321,6 @@ export const deployWalletBalancerProvider = async (
|
|||
}
|
||||
return instance;
|
||||
};
|
||||
export const deployMockSwapAdapter = async (addressesProvider: tEthereumAddress) =>
|
||||
await deployContract<MockSwapAdapter>(eContractid.MockSwapAdapter, [addressesProvider]);
|
||||
|
||||
export const deployAaveProtocolTestHelpers = async (
|
||||
addressesProvider: tEthereumAddress,
|
||||
|
@ -548,14 +546,6 @@ export const getMockFlashLoanReceiver = async (address?: tEthereumAddress) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const getMockSwapAdapter = async (address?: tEthereumAddress) => {
|
||||
return await getContract<MockSwapAdapter>(
|
||||
eContractid.MockSwapAdapter,
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.MockSwapAdapter}.${BRE.network.name}`).value()).address
|
||||
);
|
||||
};
|
||||
|
||||
export const getLendingRateOracle = async (address?: tEthereumAddress) => {
|
||||
return await getContract<LendingRateOracle>(
|
||||
eContractid.LendingRateOracle,
|
||||
|
@ -798,7 +788,7 @@ export const initReserves = async (
|
|||
stableRateSlope2,
|
||||
},
|
||||
] = (Object.entries(reservesParams) as [string, IReserveParams][])[reserveParamIndex];
|
||||
console.log('deploy def reserve');
|
||||
console.log('deploy the interest rate strategy for ', assetSymbol);
|
||||
const rateStrategyContract = await deployDefaultReserveInterestRateStrategy(
|
||||
[
|
||||
lendingPoolAddressesProvider.address,
|
||||
|
@ -811,7 +801,7 @@ export const initReserves = async (
|
|||
verify
|
||||
);
|
||||
|
||||
console.log('deploy stable deb totken ', assetSymbol);
|
||||
console.log('deploy the stable debt totken for ', assetSymbol);
|
||||
const stableDebtToken = await deployStableDebtToken(
|
||||
[
|
||||
`Aave stable debt bearing ${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`,
|
||||
|
@ -823,7 +813,7 @@ export const initReserves = async (
|
|||
verify
|
||||
);
|
||||
|
||||
console.log('deploy var deb totken ', assetSymbol);
|
||||
console.log('deploy the variable debt totken for ', assetSymbol);
|
||||
const variableDebtToken = await deployVariableDebtToken(
|
||||
[
|
||||
`Aave variable debt bearing ${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`,
|
||||
|
@ -835,7 +825,7 @@ export const initReserves = async (
|
|||
verify
|
||||
);
|
||||
|
||||
console.log('deploy a token ', assetSymbol);
|
||||
console.log('deploy the aToken for ', assetSymbol);
|
||||
const aToken = await deployGenericAToken(
|
||||
[
|
||||
lendingPool.address,
|
||||
|
@ -855,9 +845,8 @@ export const initReserves = async (
|
|||
}
|
||||
}
|
||||
|
||||
console.log('init reserve currency ', assetSymbol);
|
||||
console.log('initialize the reserve ', assetSymbol);
|
||||
await lendingPoolConfigurator.initReserve(
|
||||
tokenAddress,
|
||||
aToken.address,
|
||||
stableDebtToken.address,
|
||||
variableDebtToken.address,
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import {exit} from 'process';
|
||||
import fs from 'fs';
|
||||
import globby from 'globby';
|
||||
import {file} from 'tmp-promise';
|
||||
import {BRE} from './misc-utils';
|
||||
|
||||
const listSolidityFiles = (dir: string) => globby(`${dir}/**/*.sol`);
|
||||
|
||||
const fatalErrors = [
|
||||
`The address provided as argument contains a contract, but its bytecode`,
|
||||
`Daily limit of 100 source code submissions reached`,
|
||||
];
|
||||
|
||||
export const SUPPORTED_ETHERSCAN_NETWORKS = ['main', 'ropsten', 'kovan'];
|
||||
|
||||
export const getEtherscanPath = async (contractName: string) => {
|
||||
const compilerInput = await BRE.run('compile:get-compiler-input');
|
||||
const paths = Object.keys(compilerInput.sources);
|
||||
const paths = await listSolidityFiles(BRE.config.paths.sources);
|
||||
const path = paths.find((p) => p.includes(contractName));
|
||||
if (!path) {
|
||||
throw new Error(
|
||||
|
@ -79,12 +86,22 @@ export const runTaskWithRetry = async (
|
|||
cleanup();
|
||||
} else {
|
||||
cleanup();
|
||||
console.error('[ERROR] Errors after all the retries, check the logs for more information.');
|
||||
console.error(
|
||||
'[ETHERSCAN][ERROR] Errors after all the retries, check the logs for more information.'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
counter--;
|
||||
console.info(`[INFO] Retrying attemps: ${counter}.`);
|
||||
console.error('[ERROR]', error.message);
|
||||
console.info(`[ETHERSCAN][[INFO] Retrying attemps: ${counter}.`);
|
||||
console.error('[ETHERSCAN][[ERROR]', error.message);
|
||||
|
||||
if (fatalErrors.some((fatalError) => error.message.includes(fatalError))) {
|
||||
console.error(
|
||||
'[ETHERSCAN][[ERROR] Fatal error detected, skip retries and resume deployment.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await runTaskWithRetry(task, params, counter, msDelay, cleanup);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import {iMultiPoolsAssets, IReserveParams, tEthereumAddress} from './types';
|
|||
import {LendingPool} from '../types/LendingPool';
|
||||
import {LendingPoolConfigurator} from '../types/LendingPoolConfigurator';
|
||||
import {AaveProtocolTestHelpers} from '../types/AaveProtocolTestHelpers';
|
||||
import {waitForTx} from './misc-utils';
|
||||
|
||||
export const enableReservesToBorrow = async (
|
||||
reservesParams: iMultiPoolsAssets<IReserveParams>,
|
||||
|
@ -29,7 +30,14 @@ export const enableReservesToBorrow = async (
|
|||
continue;
|
||||
}
|
||||
|
||||
await lendingPoolConfigurator.enableBorrowingOnReserve(tokenAddress, stableBorrowRateEnabled);
|
||||
console.log('Enabling borrowing on reserve ', assetSymbol);
|
||||
|
||||
await waitForTx(
|
||||
await lendingPoolConfigurator.enableBorrowingOnReserve(
|
||||
tokenAddress,
|
||||
stableBorrowRateEnabled
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`Enabling reserve for borrowings for ${assetSymbol} failed with error ${e}. Skipped.`
|
||||
|
@ -66,11 +74,15 @@ export const enableReservesAsCollateral = async (
|
|||
}
|
||||
|
||||
try {
|
||||
await lendingPoolConfigurator.enableReserveAsCollateral(
|
||||
tokenAddress,
|
||||
baseLTVAsCollateral,
|
||||
liquidationThreshold,
|
||||
liquidationBonus
|
||||
console.log(`Enabling reserve ${assetSymbol} as collateral`);
|
||||
|
||||
await waitForTx(
|
||||
await lendingPoolConfigurator.enableReserveAsCollateral(
|
||||
tokenAddress,
|
||||
baseLTVAsCollateral,
|
||||
liquidationThreshold,
|
||||
liquidationBonus
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
|
|
|
@ -41,7 +41,7 @@ export const increaseTime = async (secondsToIncrease: number) => {
|
|||
await BRE.ethers.provider.send('evm_mine', []);
|
||||
};
|
||||
|
||||
export const waitForTx = async (tx: ContractTransaction) => await tx.wait();
|
||||
export const waitForTx = async (tx: ContractTransaction) => await tx.wait(1);
|
||||
|
||||
export const filterMapBy = (raw: {[key: string]: any}, fn: (key: string) => boolean) =>
|
||||
Object.keys(raw)
|
||||
|
|
|
@ -30,7 +30,7 @@ export const setInitialMarketRatesInRatesOracle = async (
|
|||
const [, assetAddress] = (Object.entries(assetsAddresses) as [string, string][])[
|
||||
assetAddressIndex
|
||||
];
|
||||
await lendingRateOracleInstance.setMarketBorrowRate(assetAddress, borrowRate);
|
||||
await waitForTx(await lendingRateOracleInstance.setMarketBorrowRate(assetAddress, borrowRate));
|
||||
console.log('added Market Borrow Rate for: ', assetSymbol);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -44,7 +44,6 @@ export enum eContractid {
|
|||
LendingPoolCollateralManager = 'LendingPoolCollateralManager',
|
||||
InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy',
|
||||
MockFlashLoanReceiver = 'MockFlashLoanReceiver',
|
||||
MockSwapAdapter = 'MockSwapAdapter',
|
||||
WalletBalanceProvider = 'WalletBalanceProvider',
|
||||
AToken = 'AToken',
|
||||
MockAToken = 'MockAToken',
|
||||
|
@ -106,6 +105,7 @@ export enum ProtocolErrors {
|
|||
|
||||
//require error messages - LendingPoolAddressesProviderRegistry
|
||||
PROVIDER_NOT_REGISTERED = '37', // 'Provider is not registered'
|
||||
INVALID_ADDRESSES_PROVIDER_ID = '68',
|
||||
|
||||
//return error messages - LendingPoolCollateralManager
|
||||
HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '38', // 'Health factor is not below the threshold'
|
||||
|
@ -344,6 +344,7 @@ export interface ICommonConfiguration {
|
|||
ReserveAssets: iParamsPerNetwork<SymbolMap<tEthereumAddress>>;
|
||||
ReservesConfig: iMultiPoolsAssets<IReserveParams>;
|
||||
ATokenDomainSeparator: iParamsPerNetwork<string>;
|
||||
ProxyPriceProvider: iParamsPerNetwork<tEthereumAddress>;
|
||||
}
|
||||
|
||||
export interface IAaveConfiguration extends ICommonConfiguration {
|
||||
|
|
7321
package-lock.json
generated
7321
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,7 @@
|
|||
"buidler:kovan": "buidler --network kovan",
|
||||
"buidler:ropsten": "buidler--network ropsten",
|
||||
"buidler:main": "buidler --network main",
|
||||
"buidler:docker": "buidler --network buidlerevm_docker",
|
||||
"buidler help": "buidler help",
|
||||
"compile": "SKIP_LOAD=true buidler compile",
|
||||
"types-gen": "typechain --target ethers-v5 --outDir ./types './artifacts/*.json'",
|
||||
|
@ -15,6 +16,8 @@
|
|||
"test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts",
|
||||
"aave:evm:dev:migration": "buidler aave:dev",
|
||||
"aave:evm:full:migration": "buidler aave:full",
|
||||
"aave:docker:dev:migration": "npm run buidler:docker -- aave:dev",
|
||||
"aave:docker:full:migration": "npm run buidler:docker -- aave:full",
|
||||
"aave:kovan:dev:migration": "npm run buidler:kovan -- aave:dev --verify",
|
||||
"aave:kovan:full:migration": "npm run buidler:kovan -- aave:full --verify",
|
||||
"aave:ropsten:dev:migration": "npm run buidler:ropsten -- aave:dev --verify",
|
||||
|
@ -36,6 +39,7 @@
|
|||
"test-liquidate": "buidler test test/__setup.spec.ts test/liquidation-atoken.spec.ts",
|
||||
"test-pausable": "buidler test test/__setup.spec.ts test/pausable-functions.spec.ts",
|
||||
"test-permit": "buidler test test/__setup.spec.ts test/atoken-permit.spec.ts",
|
||||
"test-subgraph:scenarios": "buidler --network buidlerevm_docker test test/__setup.spec.ts test/subgraph-scenarios.spec.ts",
|
||||
"dev:coverage": "buidler coverage --network coverage",
|
||||
"dev:deployment": "buidler dev-deployment",
|
||||
"dev:deployExample": "buidler deploy-Example",
|
||||
|
@ -59,7 +63,7 @@
|
|||
"@types/mocha": "7.0.2",
|
||||
"@types/node": "14.0.5",
|
||||
"bignumber.js": "9.0.0",
|
||||
"buidler-gas-reporter": "^0.1.3",
|
||||
"buidler-gas-reporter": "^0.1.4",
|
||||
"buidler-typechain": "0.1.1",
|
||||
"chai": "4.2.0",
|
||||
"chai-bignumber": "3.0.0",
|
||||
|
@ -68,6 +72,7 @@
|
|||
"ethereum-waffle": "3.0.2",
|
||||
"ethereumjs-util": "7.0.2",
|
||||
"ethers": "5.0.8",
|
||||
"globby": "^11.0.1",
|
||||
"husky": "^4.2.5",
|
||||
"lowdb": "1.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
|
|
|
@ -37,4 +37,11 @@ task(
|
|||
ProviderId
|
||||
)
|
||||
);
|
||||
|
||||
//register the proxy price provider on the addressesProvider
|
||||
const proxyProvider = getParamPerNetwork(poolConfig.ProxyPriceProvider, network);
|
||||
|
||||
if (proxyProvider && proxyProvider !== '') {
|
||||
await waitForTx(await addressesProvider.setPriceOracle(proxyProvider));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,10 +24,7 @@ task('aave:full', 'Deploy development enviroment')
|
|||
console.log('2. Deploy lending pool');
|
||||
await localBRE.run('full:deploy-lending-pool', {verify});
|
||||
|
||||
console.log('3. Deploy oracles');
|
||||
await localBRE.run('full:deploy-oracles', {verify, pool: POOL_NAME});
|
||||
|
||||
console.log('4. Initialize lending pool');
|
||||
console.log('3. Initialize lending pool');
|
||||
await localBRE.run('full:initialize-lending-pool', {verify, pool: POOL_NAME});
|
||||
|
||||
console.log('\nFinished migrations');
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
registerContractInJsonDb,
|
||||
getPairsTokenAggregator,
|
||||
initReserves,
|
||||
deployMockSwapAdapter,
|
||||
deployLendingRateOracle,
|
||||
} from '../helpers/contracts-helpers';
|
||||
import {Signer} from 'ethers';
|
||||
|
@ -239,9 +238,6 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
|||
const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address);
|
||||
await insertContractAddressInDb(eContractid.MockFlashLoanReceiver, mockFlashLoanReceiver.address);
|
||||
|
||||
const mockSwapAdapter = await deployMockSwapAdapter(addressesProvider.address);
|
||||
await insertContractAddressInDb(eContractid.MockSwapAdapter, mockSwapAdapter.address);
|
||||
|
||||
await deployWalletBalancerProvider(addressesProvider.address);
|
||||
|
||||
console.timeEnd('setup');
|
||||
|
|
|
@ -18,6 +18,15 @@ makeSuite('AddressesProviderRegistry', (testEnv: TestEnv) => {
|
|||
);
|
||||
});
|
||||
|
||||
it('tries to register an addresses provider with id 0', async () => {
|
||||
const {users, registry} = testEnv;
|
||||
const {INVALID_ADDRESSES_PROVIDER_ID} = ProtocolErrors;
|
||||
|
||||
await expect(registry.registerAddressesProvider(users[2].address, '0')).to.be.revertedWith(
|
||||
INVALID_ADDRESSES_PROVIDER_ID
|
||||
);
|
||||
});
|
||||
|
||||
it('Registers a new mock addresses provider', async () => {
|
||||
const {users, registry} = testEnv;
|
||||
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
import {makeSuite, TestEnv} from './helpers/make-suite';
|
||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
||||
import {getMockSwapAdapter} from '../helpers/contracts-helpers';
|
||||
import {ProtocolErrors} from '../helpers/types';
|
||||
import {ethers} from 'ethers';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL} from '../helpers/constants';
|
||||
import {getContractsData, getTxCostAndTimestamp} from './helpers/actions';
|
||||
import {calcExpectedATokenBalance} from './helpers/utils/calculations';
|
||||
import {waitForTx} from '../helpers/misc-utils';
|
||||
import {advanceBlock, timeLatest} from '../helpers/misc-utils';
|
||||
|
||||
const {expect} = require('chai');
|
||||
|
||||
makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => {
|
||||
let _mockSwapAdapter = {} as MockSwapAdapter;
|
||||
const {
|
||||
HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
|
||||
NO_UNFREEZED_RESERVE,
|
||||
NO_ACTIVE_RESERVE,
|
||||
INVALID_EQUAL_ASSETS_TO_SWAP,
|
||||
} = ProtocolErrors;
|
||||
|
||||
before(async () => {
|
||||
_mockSwapAdapter = await getMockSwapAdapter();
|
||||
});
|
||||
|
||||
it('Should not allow to swap if from equal to', async () => {
|
||||
const {pool, weth} = testEnv;
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
weth.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(INVALID_EQUAL_ASSETS_TO_SWAP);
|
||||
});
|
||||
|
||||
it('Should not allow to swap if from or to reserves are not active', async () => {
|
||||
const {pool, weth, dai, configurator} = testEnv;
|
||||
|
||||
await configurator.deactivateReserve(weth.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_ACTIVE_RESERVE);
|
||||
await configurator.activateReserve(weth.address);
|
||||
|
||||
await configurator.deactivateReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_ACTIVE_RESERVE);
|
||||
|
||||
//cleanup state
|
||||
await configurator.activateReserve(dai.address);
|
||||
});
|
||||
|
||||
it('Deposits WETH into the reserve', async () => {
|
||||
const {pool, weth, users} = testEnv;
|
||||
const amountToDeposit = ethers.utils.parseEther('1');
|
||||
|
||||
for (const signer of [weth.signer, users[2].signer]) {
|
||||
const connectedWETH = weth.connect(signer);
|
||||
await connectedWETH.mint(amountToDeposit);
|
||||
await connectedWETH.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool
|
||||
.connect(signer)
|
||||
.deposit(weth.address, amountToDeposit, await signer.getAddress(), '0');
|
||||
}
|
||||
});
|
||||
|
||||
it('User tries to swap more then he can, revert expected', async () => {
|
||||
const {pool, weth, dai} = testEnv;
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('1.1'),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith('55');
|
||||
});
|
||||
|
||||
it('User tries to swap more then available on the reserve', async () => {
|
||||
const {pool, weth, dai, users, aEth, deployer} = testEnv;
|
||||
|
||||
await pool.borrow(weth.address, ethers.utils.parseEther('0.1'), 1, 0, deployer.address);
|
||||
await pool.connect(users[2].signer).withdraw(weth.address, ethers.utils.parseEther('1'));
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('1'),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith('55');
|
||||
});
|
||||
|
||||
it('User tries to swap correct amount', async () => {
|
||||
const {pool, weth, dai, aEth, aDai, helpersContract} = testEnv;
|
||||
const userAddress = await pool.signer.getAddress();
|
||||
const amountToSwap = ethers.utils.parseEther('0.25');
|
||||
|
||||
const amountToReturn = ethers.utils.parseEther('0.5');
|
||||
await _mockSwapAdapter.setAmountToReturn(amountToReturn);
|
||||
|
||||
const {
|
||||
reserveData: wethReserveDataBefore,
|
||||
userData: wethUserDataBefore,
|
||||
} = await getContractsData(weth.address, userAddress, testEnv);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
userAddress,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const reserveBalanceWETHBefore = await weth.balanceOf(aEth.address);
|
||||
const reserveBalanceDAIBefore = await dai.balanceOf(aDai.address);
|
||||
|
||||
const txReceipt = await waitForTx(
|
||||
await pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
amountToSwap,
|
||||
'0x10'
|
||||
)
|
||||
);
|
||||
const {txTimestamp} = await getTxCostAndTimestamp(txReceipt);
|
||||
const userATokenBalanceWETHAfter = await aEth.balanceOf(userAddress);
|
||||
const userATokenBalanceDAIAfter = await aDai.balanceOf(userAddress);
|
||||
|
||||
const reserveBalanceWETHAfter = await weth.balanceOf(aEth.address);
|
||||
const reserveBalanceDAIAfter = await dai.balanceOf(aDai.address);
|
||||
|
||||
expect(userATokenBalanceWETHAfter.toString()).to.be.equal(
|
||||
calcExpectedATokenBalance(wethReserveDataBefore, wethUserDataBefore, txTimestamp)
|
||||
.minus(amountToSwap.toString())
|
||||
.toString(),
|
||||
'was burned incorrect amount of user funds'
|
||||
);
|
||||
expect(userATokenBalanceDAIAfter.toString()).to.be.equal(
|
||||
calcExpectedATokenBalance(daiReserveDataBefore, daiUserDataBefore, txTimestamp)
|
||||
.plus(amountToReturn.toString())
|
||||
.toString(),
|
||||
'was minted incorrect amount of user funds'
|
||||
);
|
||||
|
||||
expect(reserveBalanceWETHAfter.toString()).to.be.equal(
|
||||
reserveBalanceWETHBefore.sub(amountToSwap).toString(),
|
||||
'was sent incorrect amount if reserve funds'
|
||||
);
|
||||
expect(reserveBalanceDAIAfter.toString()).to.be.equal(
|
||||
reserveBalanceDAIBefore.add(amountToReturn).toString(),
|
||||
'was received incorrect amount if reserve funds'
|
||||
);
|
||||
expect(
|
||||
(await helpersContract.getUserReserveData(dai.address, userAddress)).usageAsCollateralEnabled
|
||||
).to.be.equal(true, 'usage as collateral was not enabled on destination reserve for the user');
|
||||
});
|
||||
|
||||
it('User tries to drop HF below one', async () => {
|
||||
const {pool, weth, dai, deployer} = testEnv;
|
||||
const amountToSwap = ethers.utils.parseEther('0.3');
|
||||
|
||||
const amountToReturn = ethers.utils.parseEther('0.5');
|
||||
await _mockSwapAdapter.setAmountToReturn(amountToReturn);
|
||||
|
||||
await pool.borrow(weth.address, ethers.utils.parseEther('0.3'), 1, 0, deployer.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(_mockSwapAdapter.address, weth.address, dai.address, amountToSwap, '0x10')
|
||||
).to.be.revertedWith(HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD);
|
||||
});
|
||||
|
||||
it('Should set usage as collateral to false if no leftovers after swap', async () => {
|
||||
const {pool, weth, dai, users} = testEnv;
|
||||
const userAddress = await pool.signer.getAddress();
|
||||
|
||||
// add more liquidity to allow user 0 to swap everything he has
|
||||
await weth.connect(users[2].signer).mint(ethers.utils.parseEther('1'));
|
||||
await pool
|
||||
.connect(users[2].signer)
|
||||
.deposit(weth.address, ethers.utils.parseEther('1'), users[2].address, '0');
|
||||
|
||||
// cleanup borrowings, to be abe to swap whole weth
|
||||
const amountToRepay = ethers.utils.parseEther('0.5');
|
||||
await weth.mint(amountToRepay);
|
||||
await pool.repay(weth.address, amountToRepay, '1', userAddress);
|
||||
const txTimestamp = (await timeLatest()).plus(100);
|
||||
|
||||
const {
|
||||
reserveData: wethReserveDataBefore,
|
||||
userData: wethUserDataBefore,
|
||||
} = await getContractsData(weth.address, userAddress, testEnv);
|
||||
const amountToSwap = calcExpectedATokenBalance(
|
||||
wethReserveDataBefore,
|
||||
wethUserDataBefore,
|
||||
txTimestamp.plus('1')
|
||||
);
|
||||
|
||||
await advanceBlock(txTimestamp.toNumber());
|
||||
|
||||
await pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
amountToSwap.toString(),
|
||||
'0x10'
|
||||
);
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
userAddress,
|
||||
testEnv
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.equal(
|
||||
false,
|
||||
'usageAsCollateralEnabled are not set to false'
|
||||
);
|
||||
});
|
||||
it('Should not allow to swap if to reserve are freezed', async () => {
|
||||
const {pool, weth, dai, configurator} = testEnv;
|
||||
|
||||
await configurator.freezeReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_UNFREEZED_RESERVE);
|
||||
|
||||
//cleanup state
|
||||
await configurator.unfreezeReserve(dai.address);
|
||||
});
|
||||
});
|
|
@ -1,932 +0,0 @@
|
|||
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL, oneEther} from '../helpers/constants';
|
||||
import {ethers} from 'ethers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import {
|
||||
calcExpectedVariableDebtTokenBalance,
|
||||
calcExpectedStableDebtTokenBalance,
|
||||
} from './helpers/utils/calculations';
|
||||
import {getContractsData} from './helpers/actions';
|
||||
import {timeLatest, BRE, increaseTime, waitForTx} from '../helpers/misc-utils';
|
||||
import {ProtocolErrors} from '../helpers/types';
|
||||
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
|
||||
import {expectRepayWithCollateralEvent} from './repay-with-collateral.spec';
|
||||
|
||||
const {expect} = require('chai');
|
||||
const {parseUnits, parseEther} = ethers.utils;
|
||||
|
||||
makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEnv) => {
|
||||
const {INVALID_HF, COLLATERAL_CANNOT_BE_LIQUIDATED, IS_PAUSED} = ProtocolErrors;
|
||||
|
||||
it('User 1 provides some liquidity for others to borrow', async () => {
|
||||
const {pool, weth, dai, usdc, deployer} = testEnv;
|
||||
|
||||
await weth.mint(parseEther('200'));
|
||||
await weth.approve(pool.address, parseEther('200'));
|
||||
await pool.deposit(weth.address, parseEther('200'), deployer.address, 0);
|
||||
await dai.mint(parseEther('20000'));
|
||||
await dai.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
||||
await usdc.mint(parseEther('20000'));
|
||||
await usdc.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(usdc.address, parseEther('20000'), deployer.address, 0);
|
||||
});
|
||||
|
||||
it('User 5 liquidate User 3 collateral, all his variable debt and part of the stable', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
const amountToDeposit = parseEther('20');
|
||||
const amountToBorrow = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, amountToDeposit);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0, user.address);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
// Set HF below 1
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(60).toFixed(0)
|
||||
);
|
||||
const userGlobalDataPrior = await pool.getUserAccountData(user.address);
|
||||
expect(userGlobalDataPrior.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const amountToRepay = parseUnits('80', 6);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const txReceipt = await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance(
|
||||
usdcUserDataBefore.principalStableDebt,
|
||||
usdcUserDataBefore.stableBorrowRate,
|
||||
usdcUserDataBefore.stableRateLastUpdated,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentStableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
const stableDebtRepaid = new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.abs();
|
||||
|
||||
expect(usdcUserDataAfter.currentStableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_STABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
const eventsEmitted = txReceipt.events || [];
|
||||
|
||||
expectRepayWithCollateralEvent(
|
||||
eventsEmitted,
|
||||
pool.address,
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address
|
||||
);
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 3 deposits WETH and borrows USDC at Variable', async () => {
|
||||
const {pool, weth, usdc, users, oracle} = testEnv;
|
||||
const user = users[2];
|
||||
const amountToDeposit = parseEther('10');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
||||
usdc.address,
|
||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
||||
.div(usdcPrice.toString())
|
||||
.multipliedBy(0.95)
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.15).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
const amountToRepay = usdcReserveDataBefore.totalVariableDebt.dividedBy(2).toFixed(0);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.almostEqual(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('Revert expected. User 5 tries to liquidate an User 3 collateral a currency he havent borrow', async () => {
|
||||
const {pool, weth, dai, users, oracle, mockSwapAdapter, usdc} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(6.4).toFixed(0)
|
||||
);
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('40');
|
||||
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 5 liquidates all the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.35).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
const amountToRepay = usdcReserveDataBefore.totalVariableDebt.toFixed(0);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
|
||||
const {pool, weth, dai, users, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
const amountToDeposit = ethers.utils.parseEther('2');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
||||
dai.address,
|
||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
||||
.div(daiPrice.toString())
|
||||
.multipliedBy(0.9)
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
// Sets DAI Price higher to decrease health factor below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(1.4).toFixed(0)
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.toString();
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(true));
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('53');
|
||||
|
||||
// Resets DAI Price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
// Resets mock
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(false));
|
||||
});
|
||||
|
||||
it('User 5 tries to liquidate User 2 DAI Variable loan using his WETH collateral, with good HF', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
const {reserveData: daiReserveDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
// First half
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.dividedBy(2).toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('38');
|
||||
});
|
||||
it('User 5 liquidates User 2 DAI Variable loan using his WETH collateral, half the amount', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
// Sets DAI Price higher to decrease health factor below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(1.4).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
// First half
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt
|
||||
.multipliedBy(0.6)
|
||||
.toFixed(0)
|
||||
.toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
|
||||
// Resets DAI price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
});
|
||||
|
||||
it('User 2 tries to repay remaining DAI Variable loan using his WETH collateral', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await increaseTime(1000);
|
||||
// Repay the remaining DAI
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const receipt = await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = (await BRE.ethers.provider.getBlock(receipt.blockNumber))
|
||||
.timestamp;
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
).to.be.bignumber.equal(wethUserDataAfter.currentATokenBalance);
|
||||
});
|
||||
|
||||
it('Liquidator tries to repay 4 user a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[3];
|
||||
const liquidator = users[5];
|
||||
|
||||
const amountToDepositWeth = parseEther('0.1');
|
||||
const amountToDepositDAI = parseEther('500');
|
||||
const amountToBorrowVariable = parseUnits('80', '6');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDepositWeth);
|
||||
await dai.connect(user.signer).mint(amountToDepositDAI);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
const amountToRepay = amountToBorrowVariable;
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
// Set HF below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(0.1).toFixed(0)
|
||||
);
|
||||
const userGlobalDataPrior = await pool.getUserAccountData(user.address);
|
||||
expect(userGlobalDataPrior.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
// Execute liquidation
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralConfig = await helpersContract.getReserveConfigurationData(weth.address);
|
||||
|
||||
const collateralDecimals = collateralConfig.decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
const collateralLiquidationBonus = collateralConfig.liquidationBonus.toString();
|
||||
|
||||
const expectedDebtCovered = new BigNumber(collateralPrice.toString())
|
||||
.times(new BigNumber(wethUserDataBefore.currentATokenBalance.toString()))
|
||||
.times(new BigNumber(10).pow(principalDecimals))
|
||||
.div(
|
||||
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(collateralLiquidationBonus).div(10000).toString())
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(expectedDebtCovered.toString())
|
||||
.plus(expectedVariableDebtIncrease),
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(0);
|
||||
|
||||
// Resets DAI Price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
});
|
||||
|
||||
it('User 4 deposits WETH, LEND and DAI, then borrows USDC at Variable, then disables WETH as collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users} = testEnv;
|
||||
const user = users[4];
|
||||
const amountWETHToDeposit = parseEther('10');
|
||||
const amountDAIToDeposit = parseEther('100');
|
||||
|
||||
const amountToBorrow = parseUnits('75', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
||||
|
||||
await dai.connect(user.signer).mint(amountDAIToDeposit);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('Liquidator tries to liquidate User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[4];
|
||||
const liquidator = users[5];
|
||||
|
||||
const amountToRepay = parseUnits('65', 6);
|
||||
|
||||
// User 5 Disable WETH as collateral
|
||||
await pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
expect(wethUserDataBefore.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
//drop the price to set the HF below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(0.9).toFixed(0)
|
||||
);
|
||||
|
||||
// Liquidator should NOT be able to liquidate himself with WETH, even if is disabled
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith(COLLATERAL_CANNOT_BE_LIQUIDATED);
|
||||
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
});
|
|
@ -48,8 +48,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -77,8 +77,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
const reserveDataBefore = await helpersContract.getReserveData(weth.address);
|
||||
const txResult = await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1000720000000000000',
|
||||
[weth.address],
|
||||
['1000720000000000000'],
|
||||
0,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -108,13 +108,13 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
|
||||
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
|
||||
});
|
||||
|
||||
it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => {
|
||||
|
@ -128,8 +128,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -148,8 +148,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
4,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -176,8 +176,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
2,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -193,22 +193,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
||||
|
||||
expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt');
|
||||
});
|
||||
|
||||
it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => {
|
||||
const {pool, weth} = testEnv;
|
||||
|
||||
await expect(
|
||||
pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1', //1 wei loan
|
||||
2,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(REQUESTED_AMOUNT_TOO_SMALL);
|
||||
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
|
||||
});
|
||||
|
||||
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
|
||||
|
@ -217,8 +202,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
await expect(
|
||||
pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1004415000000000000', //slightly higher than the available liquidity
|
||||
[weth.address],
|
||||
['1004415000000000000'], //slightly higher than the available liquidity
|
||||
2,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -231,7 +216,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
const {pool, deployer, weth} = testEnv;
|
||||
|
||||
await expect(
|
||||
pool.flashLoan(deployer.address, weth.address, '1000000000000000000', 2, '0x10', '0')
|
||||
pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], 2, '0x10', '0')
|
||||
).to.be.reverted;
|
||||
});
|
||||
|
||||
|
@ -257,8 +242,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
usdc.address,
|
||||
flashloanAmount,
|
||||
[usdc.address],
|
||||
[flashloanAmount],
|
||||
0,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -297,7 +282,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
await expect(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0')
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
[usdc.address],
|
||||
[flashloanAmount],
|
||||
2,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
||||
});
|
||||
|
||||
|
@ -320,7 +312,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
await pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0');
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], 2, '0x10', '0');
|
||||
const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(
|
||||
usdc.address
|
||||
);
|
||||
|
@ -332,7 +324,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
const callerDebt = await usdcDebtToken.balanceOf(caller.address);
|
||||
|
||||
expect(callerDebt.toString()).to.be.equal('500450000', 'Invalid user debt');
|
||||
expect(callerDebt.toString()).to.be.equal('500000000', 'Invalid user debt');
|
||||
});
|
||||
|
||||
it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => {
|
||||
|
@ -355,8 +347,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
await expect(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 0, '0x10', '0')
|
||||
).to.be.revertedWith('ERC20: transfer amount exceeds allowance');
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 0, '0x10', '0')
|
||||
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
|
||||
});
|
||||
|
||||
it('Caller takes a WETH flashloan with mode = 1', async () => {
|
||||
|
@ -370,7 +362,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
await pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 1, '0x10', '0');
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0');
|
||||
|
||||
const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
|
||||
|
||||
|
@ -381,6 +373,6 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
||||
|
||||
expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt');
|
||||
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
getMintableErc20,
|
||||
getLendingPoolConfiguratorProxy,
|
||||
getPriceOracle,
|
||||
getMockSwapAdapter,
|
||||
getLendingPoolAddressesProviderRegistry,
|
||||
} from '../../helpers/contracts-helpers';
|
||||
import {tEthereumAddress} from '../../helpers/types';
|
||||
|
@ -25,7 +24,6 @@ import bignumberChai from 'chai-bignumber';
|
|||
import {almostEqual} from './almost-equal';
|
||||
import {PriceOracle} from '../../types/PriceOracle';
|
||||
import {LendingPoolAddressesProvider} from '../../types/LendingPoolAddressesProvider';
|
||||
import {MockSwapAdapter} from '../../types/MockSwapAdapter';
|
||||
import {LendingPoolAddressesProviderRegistry} from '../../types/LendingPoolAddressesProviderRegistry';
|
||||
chai.use(bignumberChai());
|
||||
chai.use(almostEqual());
|
||||
|
@ -48,7 +46,6 @@ export interface TestEnv {
|
|||
usdc: MintableERC20;
|
||||
lend: MintableERC20;
|
||||
addressesProvider: LendingPoolAddressesProvider;
|
||||
mockSwapAdapter: MockSwapAdapter;
|
||||
registry: LendingPoolAddressesProviderRegistry;
|
||||
}
|
||||
|
||||
|
@ -73,7 +70,6 @@ const testEnv: TestEnv = {
|
|||
usdc: {} as MintableERC20,
|
||||
lend: {} as MintableERC20,
|
||||
addressesProvider: {} as LendingPoolAddressesProvider,
|
||||
mockSwapAdapter: {} as MockSwapAdapter,
|
||||
registry: {} as LendingPoolAddressesProviderRegistry,
|
||||
} as TestEnv;
|
||||
|
||||
|
@ -134,8 +130,6 @@ export async function initializeMakeSuite() {
|
|||
testEnv.usdc = await getMintableErc20(usdcAddress);
|
||||
testEnv.lend = await getMintableErc20(lendAddress);
|
||||
testEnv.weth = await getMintableErc20(wethAddress);
|
||||
|
||||
testEnv.mockSwapAdapter = await getMockSwapAdapter();
|
||||
}
|
||||
|
||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
||||
|
|
|
@ -342,6 +342,20 @@
|
|||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Users 1 tries to withdraw 0.05 WETH, which does not bring the HF below 1",
|
||||
"actions": [
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"amount": "0.05",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1153,11 +1153,12 @@ const calcLinearInterest = (
|
|||
currentTimestamp: BigNumber,
|
||||
lastUpdateTimestamp: BigNumber
|
||||
) => {
|
||||
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp).wadToRay();
|
||||
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp);
|
||||
|
||||
const timeDelta = timeDifference.rayDiv(new BigNumber(ONE_YEAR).wadToRay());
|
||||
|
||||
const cumulatedInterest = rate.rayMul(timeDelta).plus(RAY);
|
||||
const cumulatedInterest = rate
|
||||
.multipliedBy(timeDifference)
|
||||
.dividedBy(new BigNumber(ONE_YEAR))
|
||||
.plus(RAY);
|
||||
|
||||
return cumulatedInterest;
|
||||
};
|
||||
|
|
|
@ -155,22 +155,6 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Swap liquidity', async () => {
|
||||
const {pool, dai, weth, users, configurator} = testEnv;
|
||||
|
||||
const user = users[1];
|
||||
// Pause the pool
|
||||
await configurator.setPoolPause(true);
|
||||
|
||||
// Try to execute liquidation
|
||||
await expect(
|
||||
pool.connect(user.signer).swapLiquidity(user.address, dai.address, weth.address, '1', '0x')
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause the pool
|
||||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Repay', async () => {
|
||||
const {pool, dai, users, configurator} = testEnv;
|
||||
|
||||
|
@ -187,32 +171,6 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Repay with collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle, configurator} = testEnv;
|
||||
const user = users[6];
|
||||
const liquidator = users[5];
|
||||
|
||||
// Pause the pool
|
||||
await configurator.setPoolPause(true);
|
||||
|
||||
// Try to execute liquidation
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
'1',
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause the pool
|
||||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Flash loan', async () => {
|
||||
const {dai, pool, weth, users, configurator} = testEnv;
|
||||
|
||||
|
@ -228,7 +186,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await expect(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 1, '0x10', '0')
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0')
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause pool
|
||||
|
@ -317,7 +275,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
});
|
||||
|
||||
it('SwapBorrowRateMode', async () => {
|
||||
const {pool, weth, dai, usdc, users, configurator, mockSwapAdapter} = testEnv;
|
||||
const {pool, weth, dai, usdc, users, configurator} = testEnv;
|
||||
const user = users[1];
|
||||
const amountWETHToDeposit = parseEther('10');
|
||||
const amountDAIToDeposit = parseEther('120');
|
||||
|
|
|
@ -1,682 +0,0 @@
|
|||
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL} from '../helpers/constants';
|
||||
import {ethers} from 'ethers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import {
|
||||
calcExpectedVariableDebtTokenBalance,
|
||||
calcExpectedStableDebtTokenBalance,
|
||||
} from './helpers/utils/calculations';
|
||||
import {getContractsData} from './helpers/actions';
|
||||
import {timeLatest, waitForTx} from '../helpers/misc-utils';
|
||||
import {ProtocolErrors, tEthereumAddress} from '../helpers/types';
|
||||
|
||||
const {expect} = require('chai');
|
||||
const {parseUnits, parseEther} = ethers.utils;
|
||||
|
||||
export const expectRepayWithCollateralEvent = (
|
||||
events: ethers.Event[],
|
||||
pool: tEthereumAddress,
|
||||
collateral: tEthereumAddress,
|
||||
borrowing: tEthereumAddress,
|
||||
user: tEthereumAddress
|
||||
) => {
|
||||
if (!events || events.length < 16) {
|
||||
expect(false, 'INVALID_EVENTS_LENGTH_ON_REPAY_COLLATERAL');
|
||||
}
|
||||
|
||||
const repayWithCollateralEvent = events[15];
|
||||
|
||||
expect(repayWithCollateralEvent.address).to.be.equal(pool);
|
||||
expect(`0x${repayWithCollateralEvent.topics[1].slice(26)}`.toLowerCase()).to.be.equal(
|
||||
collateral.toLowerCase()
|
||||
);
|
||||
expect(`0x${repayWithCollateralEvent.topics[2].slice(26)}`).to.be.equal(borrowing.toLowerCase());
|
||||
expect(`0x${repayWithCollateralEvent.topics[3].slice(26)}`.toLowerCase()).to.be.equal(
|
||||
user.toLowerCase()
|
||||
);
|
||||
};
|
||||
|
||||
makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||
const {IS_PAUSED} = ProtocolErrors;
|
||||
it("It's not possible to repayWithCollateral() on a non-active collateral or a non active principal", async () => {
|
||||
const {configurator, weth, pool, users, dai, mockSwapAdapter} = testEnv;
|
||||
const user = users[1];
|
||||
await configurator.deactivateReserve(weth.address);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
parseEther('100'),
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('2');
|
||||
|
||||
await configurator.activateReserve(weth.address);
|
||||
|
||||
await configurator.deactivateReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
parseEther('100'),
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('2');
|
||||
|
||||
await configurator.activateReserve(dai.address);
|
||||
});
|
||||
|
||||
it('User 1 provides some liquidity for others to borrow', async () => {
|
||||
const {pool, weth, dai, usdc, deployer} = testEnv;
|
||||
|
||||
await weth.mint(parseEther('200'));
|
||||
await weth.approve(pool.address, parseEther('200'));
|
||||
await pool.deposit(weth.address, parseEther('200'), deployer.address, 0);
|
||||
await dai.mint(parseEther('20000'));
|
||||
await dai.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
||||
await usdc.mint(parseEther('20000'));
|
||||
await usdc.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(usdc.address, parseEther('20000'), deployer.address, 0);
|
||||
});
|
||||
|
||||
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
|
||||
const {pool, weth, dai, users} = testEnv;
|
||||
const user = users[1];
|
||||
const amountToDeposit = ethers.utils.parseEther('1');
|
||||
const amountToBorrow = ethers.utils.parseEther('20');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const amountToRepay = parseEther('10');
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(true));
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('53');
|
||||
});
|
||||
|
||||
it('User 2 tries to repay his DAI Variable loan using his WETH collateral. First half the amount, after that, the rest', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const amountToRepay = parseEther('10');
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(false));
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('User 3 deposits WETH and borrows USDC at Variable', async () => {
|
||||
const {pool, weth, usdc, users} = testEnv;
|
||||
const user = users[2];
|
||||
const amountToDeposit = parseEther('10');
|
||||
const amountToBorrow = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('Revert expected. User 3 tries to repay with his collateral a currency he havent borrow', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('40');
|
||||
});
|
||||
|
||||
it('User 3 tries to repay with his collateral all his variable debt and part of the stable', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToDeposit = parseEther('20');
|
||||
const amountToBorrowStable = parseUnits('40', 6);
|
||||
const amountToBorrowVariable = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0, user.address);
|
||||
|
||||
const amountToRepay = parseUnits('80', 6);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const txReceipt = await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance(
|
||||
usdcUserDataBefore.principalStableDebt,
|
||||
usdcUserDataBefore.stableBorrowRate,
|
||||
usdcUserDataBefore.stableRateLastUpdated,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentStableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
const stableDebtRepaid = new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.abs();
|
||||
|
||||
expect(usdcUserDataAfter.currentStableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_STABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
const eventsEmitted = txReceipt.events || [];
|
||||
|
||||
expectRepayWithCollateralEvent(
|
||||
eventsEmitted,
|
||||
pool.address,
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('User 4 tries to repay a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[3];
|
||||
|
||||
const amountToDepositWeth = parseEther('0.1');
|
||||
const amountToDepositDAI = parseEther('500');
|
||||
const amountToBorrowVariable = parseEther('80');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDepositWeth);
|
||||
await dai.connect(user.signer).mint(amountToDepositDAI);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
const amountToRepay = parseEther('80');
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralConfig = await helpersContract.getReserveConfigurationData(weth.address);
|
||||
|
||||
const collateralDecimals = collateralConfig.decimals.toString();
|
||||
const collateralLiquidationBonus = collateralConfig.liquidationBonus.toString();
|
||||
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedDebtCovered = new BigNumber(collateralPrice.toString())
|
||||
.times(new BigNumber(wethUserDataBefore.currentATokenBalance.toString()))
|
||||
.times(new BigNumber(10).pow(principalDecimals))
|
||||
.div(
|
||||
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(collateralLiquidationBonus).div(10000).toString())
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(expectedDebtCovered.toString())
|
||||
.plus(expectedVariableDebtIncrease),
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.almostEqual(0);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
|
||||
it('User 5 deposits WETH and DAI, then borrows USDC at Variable, then disables WETH as collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users} = testEnv;
|
||||
const user = users[4];
|
||||
const amountWETHToDeposit = parseEther('10');
|
||||
const amountDAIToDeposit = parseEther('120');
|
||||
const amountToBorrow = parseUnits('65', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
||||
|
||||
await dai.connect(user.signer).mint(amountDAIToDeposit);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 5 tries to repay his USDC loan by swapping his WETH collateral, should not revert even with WETH collateral disabled', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[4];
|
||||
|
||||
const amountToRepay = parseUnits('65', 6);
|
||||
|
||||
// Disable WETH as collateral
|
||||
await pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
expect(wethUserDataBefore.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
// User 5 should be able to liquidate himself with WETH, even if is disabled
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
expect(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
});
|
32
test/subgraph-scenarios.spec.ts
Normal file
32
test/subgraph-scenarios.spec.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import {configuration as actionsConfiguration} from './helpers/actions';
|
||||
import {configuration as calculationsConfiguration} from './helpers/utils/calculations';
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import {makeSuite} from './helpers/make-suite';
|
||||
import {getReservesConfigByPool} from '../helpers/configuration';
|
||||
import {AavePools, iAavePoolAssets, IReserveParams} from '../helpers/types';
|
||||
import {executeStory} from './helpers/scenario-engine';
|
||||
|
||||
makeSuite('Subgraph scenario tests', async (testEnv) => {
|
||||
let story: any;
|
||||
let scenario;
|
||||
before('Initializing configuration', async () => {
|
||||
const scenario = require(`./helpers/scenarios/borrow-repay-stable`);
|
||||
story = scenario.stories[0];
|
||||
// Sets BigNumber for this suite, instead of globally
|
||||
BigNumber.config({DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN});
|
||||
|
||||
actionsConfiguration.skipIntegrityCheck = false; //set this to true to execute solidity-coverage
|
||||
|
||||
calculationsConfiguration.reservesParams = <iAavePoolAssets<IReserveParams>>(
|
||||
getReservesConfigByPool(AavePools.proto)
|
||||
);
|
||||
});
|
||||
after('Reset', () => {
|
||||
// Reset BigNumber
|
||||
BigNumber.config({DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP});
|
||||
});
|
||||
it('deposit-borrow', async () => {
|
||||
await executeStory(story, testEnv);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user