mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
- Added contracts from v1 of the protocol.
This commit is contained in:
parent
5ccf06c1a8
commit
b889cb25b6
15
contracts/configuration/AddressStorage.sol
Normal file
15
contracts/configuration/AddressStorage.sol
Normal file
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
contract AddressStorage {
|
||||
mapping(bytes32 => address) private addresses;
|
||||
|
||||
function getAddress(bytes32 _key) public view returns (address) {
|
||||
return addresses[_key];
|
||||
}
|
||||
|
||||
function _setAddress(bytes32 _key, address _value) internal {
|
||||
addresses[_key] = _value;
|
||||
}
|
||||
|
||||
}
|
239
contracts/configuration/LendingPoolAddressesProvider.sol
Normal file
239
contracts/configuration/LendingPoolAddressesProvider.sol
Normal file
|
@ -0,0 +1,239 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "../libraries/openzeppelin-upgradeability/InitializableAdminUpgradeabilityProxy.sol";
|
||||
|
||||
import "./AddressStorage.sol";
|
||||
import "../interfaces/ILendingPoolAddressesProvider.sol";
|
||||
|
||||
/**
|
||||
* @title LendingPoolAddressesProvider contract
|
||||
* @notice Is the main registry of the protocol. All the different components of the protocol are accessible
|
||||
* through the addresses provider.
|
||||
* @author Aave
|
||||
**/
|
||||
|
||||
contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider, AddressStorage {
|
||||
//events
|
||||
event LendingPoolUpdated(address indexed newAddress);
|
||||
event LendingPoolCoreUpdated(address indexed newAddress);
|
||||
event LendingPoolParametersProviderUpdated(address indexed newAddress);
|
||||
event LendingPoolManagerUpdated(address indexed newAddress);
|
||||
event LendingPoolConfiguratorUpdated(address indexed newAddress);
|
||||
event LendingPoolLiquidationManagerUpdated(address indexed newAddress);
|
||||
event LendingPoolDataProviderUpdated(address indexed newAddress);
|
||||
event EthereumAddressUpdated(address indexed newAddress);
|
||||
event PriceOracleUpdated(address indexed newAddress);
|
||||
event LendingRateOracleUpdated(address indexed newAddress);
|
||||
event FeeProviderUpdated(address indexed newAddress);
|
||||
event TokenDistributorUpdated(address indexed newAddress);
|
||||
|
||||
event ProxyCreated(bytes32 id, address indexed newAddress);
|
||||
|
||||
bytes32 private constant LENDING_POOL = "LENDING_POOL";
|
||||
bytes32 private constant LENDING_POOL_CORE = "LENDING_POOL_CORE";
|
||||
bytes32 private constant LENDING_POOL_CONFIGURATOR = "LENDING_POOL_CONFIGURATOR";
|
||||
bytes32 private constant LENDING_POOL_PARAMETERS_PROVIDER = "PARAMETERS_PROVIDER";
|
||||
bytes32 private constant LENDING_POOL_MANAGER = "LENDING_POOL_MANAGER";
|
||||
bytes32 private constant LENDING_POOL_LIQUIDATION_MANAGER = "LIQUIDATION_MANAGER";
|
||||
bytes32 private constant LENDING_POOL_FLASHLOAN_PROVIDER = "FLASHLOAN_PROVIDER";
|
||||
bytes32 private constant DATA_PROVIDER = "DATA_PROVIDER";
|
||||
bytes32 private constant ETHEREUM_ADDRESS = "ETHEREUM_ADDRESS";
|
||||
bytes32 private constant PRICE_ORACLE = "PRICE_ORACLE";
|
||||
bytes32 private constant LENDING_RATE_ORACLE = "LENDING_RATE_ORACLE";
|
||||
bytes32 private constant FEE_PROVIDER = "FEE_PROVIDER";
|
||||
bytes32 private constant WALLET_BALANCE_PROVIDER = "WALLET_BALANCE_PROVIDER";
|
||||
bytes32 private constant TOKEN_DISTRIBUTOR = "TOKEN_DISTRIBUTOR";
|
||||
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPool proxy
|
||||
* @return the lending pool proxy address
|
||||
**/
|
||||
function getLendingPool() public override view returns (address) {
|
||||
return getAddress(LENDING_POOL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the lending pool
|
||||
* @param _pool the new lending pool implementation
|
||||
**/
|
||||
function setLendingPoolImpl(address _pool) public override onlyOwner {
|
||||
updateImplInternal(LENDING_POOL, _pool);
|
||||
emit LendingPoolUpdated(_pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPoolCore proxy
|
||||
* @return the lending pool core proxy address
|
||||
*/
|
||||
function getLendingPoolCore() public override view returns (address payable) {
|
||||
address payable core = payable(getAddress(LENDING_POOL_CORE));
|
||||
return core;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the lending pool core
|
||||
* @param _lendingPoolCore the new lending pool core implementation
|
||||
**/
|
||||
function setLendingPoolCoreImpl(address _lendingPoolCore) public override onlyOwner {
|
||||
updateImplInternal(LENDING_POOL_CORE, _lendingPoolCore);
|
||||
emit LendingPoolCoreUpdated(_lendingPoolCore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPoolConfigurator proxy
|
||||
* @return the lending pool configurator proxy address
|
||||
**/
|
||||
function getLendingPoolConfigurator() public override view returns (address) {
|
||||
return getAddress(LENDING_POOL_CONFIGURATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the lending pool configurator
|
||||
* @param _configurator the new lending pool configurator implementation
|
||||
**/
|
||||
function setLendingPoolConfiguratorImpl(address _configurator) public override onlyOwner {
|
||||
updateImplInternal(LENDING_POOL_CONFIGURATOR, _configurator);
|
||||
emit LendingPoolConfiguratorUpdated(_configurator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPoolDataProvider proxy
|
||||
* @return the lending pool data provider proxy address
|
||||
*/
|
||||
function getLendingPoolDataProvider() public override view returns (address) {
|
||||
return getAddress(DATA_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the lending pool data provider
|
||||
* @param _provider the new lending pool data provider implementation
|
||||
**/
|
||||
function setLendingPoolDataProviderImpl(address _provider) public override onlyOwner {
|
||||
updateImplInternal(DATA_PROVIDER, _provider);
|
||||
emit LendingPoolDataProviderUpdated(_provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPoolParametersProvider proxy
|
||||
* @return the address of the Lending pool parameters provider proxy
|
||||
**/
|
||||
function getLendingPoolParametersProvider() public override view returns (address) {
|
||||
return getAddress(LENDING_POOL_PARAMETERS_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the lending pool parameters provider
|
||||
* @param _parametersProvider the new lending pool parameters provider implementation
|
||||
**/
|
||||
function setLendingPoolParametersProviderImpl(address _parametersProvider) public override onlyOwner {
|
||||
updateImplInternal(LENDING_POOL_PARAMETERS_PROVIDER, _parametersProvider);
|
||||
emit LendingPoolParametersProviderUpdated(_parametersProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the FeeProvider proxy
|
||||
* @return the address of the Fee provider proxy
|
||||
**/
|
||||
function getFeeProvider() public override view returns (address) {
|
||||
return getAddress(FEE_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the implementation of the FeeProvider proxy
|
||||
* @param _feeProvider the new lending pool fee provider implementation
|
||||
**/
|
||||
function setFeeProviderImpl(address _feeProvider) public override onlyOwner {
|
||||
updateImplInternal(FEE_PROVIDER, _feeProvider);
|
||||
emit FeeProviderUpdated(_feeProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address of the LendingPoolLiquidationManager. Since the manager is used
|
||||
* through delegateCall within the LendingPool contract, the proxy contract pattern does not work properly hence
|
||||
* the addresses are changed directly.
|
||||
* @return the address of the Lending pool liquidation manager
|
||||
**/
|
||||
|
||||
function getLendingPoolLiquidationManager() public override view returns (address) {
|
||||
return getAddress(LENDING_POOL_LIQUIDATION_MANAGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the address of the Lending pool liquidation manager
|
||||
* @param _manager the new lending pool liquidation manager address
|
||||
**/
|
||||
function setLendingPoolLiquidationManager(address _manager) public override onlyOwner {
|
||||
_setAddress(LENDING_POOL_LIQUIDATION_MANAGER, _manager);
|
||||
emit LendingPoolLiquidationManagerUpdated(_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev the functions below are storing specific addresses that are outside the context of the protocol
|
||||
* hence the upgradable proxy pattern is not used
|
||||
**/
|
||||
|
||||
|
||||
function getLendingPoolManager() public override view returns (address) {
|
||||
return getAddress(LENDING_POOL_MANAGER);
|
||||
}
|
||||
|
||||
function setLendingPoolManager(address _lendingPoolManager) public override onlyOwner {
|
||||
_setAddress(LENDING_POOL_MANAGER, _lendingPoolManager);
|
||||
emit LendingPoolManagerUpdated(_lendingPoolManager);
|
||||
}
|
||||
|
||||
function getPriceOracle() public override view returns (address) {
|
||||
return getAddress(PRICE_ORACLE);
|
||||
}
|
||||
|
||||
function setPriceOracle(address _priceOracle) public override onlyOwner {
|
||||
_setAddress(PRICE_ORACLE, _priceOracle);
|
||||
emit PriceOracleUpdated(_priceOracle);
|
||||
}
|
||||
|
||||
function getLendingRateOracle() public override view returns (address) {
|
||||
return getAddress(LENDING_RATE_ORACLE);
|
||||
}
|
||||
|
||||
function setLendingRateOracle(address _lendingRateOracle) public override onlyOwner {
|
||||
_setAddress(LENDING_RATE_ORACLE, _lendingRateOracle);
|
||||
emit LendingRateOracleUpdated(_lendingRateOracle);
|
||||
}
|
||||
|
||||
|
||||
function getTokenDistributor() public override view returns (address) {
|
||||
return getAddress(TOKEN_DISTRIBUTOR);
|
||||
}
|
||||
|
||||
function setTokenDistributor(address _tokenDistributor) public override onlyOwner {
|
||||
_setAddress(TOKEN_DISTRIBUTOR, _tokenDistributor);
|
||||
emit TokenDistributorUpdated(_tokenDistributor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev internal function to update the implementation of a specific component of the protocol
|
||||
* @param _id the id of the contract to be updated
|
||||
* @param _newAddress the address of the new implementation
|
||||
**/
|
||||
function updateImplInternal(bytes32 _id, address _newAddress) internal {
|
||||
address payable proxyAddress = address(uint160(getAddress(_id)));
|
||||
|
||||
InitializableAdminUpgradeabilityProxy proxy = InitializableAdminUpgradeabilityProxy(proxyAddress);
|
||||
bytes memory params = abi.encodeWithSignature("initialize(address)", address(this));
|
||||
|
||||
if (proxyAddress == address(0)) {
|
||||
proxy = new InitializableAdminUpgradeabilityProxy();
|
||||
proxy.initialize(_newAddress, address(this), params);
|
||||
_setAddress(_id, address(proxy));
|
||||
emit ProxyCreated(_id, address(proxy));
|
||||
} else {
|
||||
proxy.upgradeToAndCall(_newAddress, params);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "../interfaces/ILendingPoolAddressesProviderRegistry.sol";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @title LendingPoolAddressesProviderRegistry contract
|
||||
* @notice contains the list of active addresses providers
|
||||
* @author Aave
|
||||
**/
|
||||
|
||||
contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesProviderRegistry {
|
||||
//events
|
||||
event AddressesProviderRegistered(address indexed newAddress);
|
||||
event AddressesProviderUnregistered(address indexed newAddress);
|
||||
|
||||
mapping(address => uint256) addressesProviders;
|
||||
address[] addressesProvidersList;
|
||||
|
||||
/**
|
||||
* @dev returns if an addressesProvider is registered or not
|
||||
* @param _provider the addresses provider
|
||||
* @return true if the addressesProvider is registered, false otherwise
|
||||
**/
|
||||
function isAddressesProviderRegistered(address _provider) external override view returns(uint256) {
|
||||
return addressesProviders[_provider];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the list of active addressesProviders
|
||||
* @return the list of addressesProviders
|
||||
**/
|
||||
function getAddressesProvidersList() external override view returns(address[] memory) {
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
return activeProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev adds a lending pool to the list of registered lending pools
|
||||
* @param _provider the pool address to be registered
|
||||
**/
|
||||
function registerAddressesProvider(address _provider, uint256 _id) public override onlyOwner {
|
||||
addressesProviders[_provider] = _id;
|
||||
addToAddressesProvidersListInternal(_provider);
|
||||
emit AddressesProviderRegistered(_provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev removes a lending pool from the list of registered lending pools
|
||||
* @param _provider the pool address to be unregistered
|
||||
**/
|
||||
function unregisterAddressesProvider(address _provider) public override onlyOwner {
|
||||
require(addressesProviders[_provider] > 0, "Provider is not registered");
|
||||
addressesProviders[_provider] = 0;
|
||||
emit AddressesProviderUnregistered(_provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev adds to the list of the addresses providers, if it wasn't already added before
|
||||
* @param _provider the pool address to be added
|
||||
**/
|
||||
function addToAddressesProvidersListInternal(address _provider) internal {
|
||||
|
||||
for(uint256 i = 0; i < addressesProvidersList.length; i++) {
|
||||
|
||||
if(addressesProvidersList[i] == _provider){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addressesProvidersList.push(_provider);
|
||||
}
|
||||
}
|
54
contracts/configuration/LendingPoolParametersProvider.sol
Normal file
54
contracts/configuration/LendingPoolParametersProvider.sol
Normal file
|
@ -0,0 +1,54 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
import "./UintStorage.sol";
|
||||
|
||||
/**
|
||||
* @title LendingPoolParametersProvider
|
||||
* @author Aave
|
||||
* @notice stores the configuration parameters of the Lending Pool contract
|
||||
**/
|
||||
|
||||
contract LendingPoolParametersProvider is VersionedInitializable {
|
||||
|
||||
uint256 private constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25;
|
||||
uint256 private constant REBALANCE_DOWN_RATE_DELTA = (1e27)/5;
|
||||
uint256 private constant FLASHLOAN_FEE_TOTAL = 9;
|
||||
uint256 private constant FLASHLOAN_FEE_PROTOCOL = 3000;
|
||||
|
||||
uint256 constant private DATA_PROVIDER_REVISION = 0x2;
|
||||
|
||||
function getRevision() internal override pure returns(uint256) {
|
||||
return DATA_PROVIDER_REVISION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev initializes the LendingPoolParametersProvider after it's added to the proxy
|
||||
* @param _addressesProvider the address of the LendingPoolAddressesProvider
|
||||
*/
|
||||
function initialize(address _addressesProvider) public initializer {
|
||||
}
|
||||
/**
|
||||
* @dev returns the maximum stable rate borrow size, in percentage of the available liquidity.
|
||||
**/
|
||||
function getMaxStableRateBorrowSizePercent() external pure returns (uint256) {
|
||||
return MAX_STABLE_RATE_BORROW_SIZE_PERCENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the delta between the current stable rate and the user stable rate at
|
||||
* which the borrow position of the user will be rebalanced (scaled down)
|
||||
**/
|
||||
function getRebalanceDownRateDelta() external pure returns (uint256) {
|
||||
return REBALANCE_DOWN_RATE_DELTA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the fee applied to a flashloan and the portion to redirect to the protocol, in basis points.
|
||||
**/
|
||||
function getFlashLoanFeesInBips() external pure returns (uint256, uint256) {
|
||||
return (FLASHLOAN_FEE_TOTAL, FLASHLOAN_FEE_PROTOCOL);
|
||||
}
|
||||
}
|
15
contracts/configuration/UintStorage.sol
Normal file
15
contracts/configuration/UintStorage.sol
Normal file
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
contract UintStorage {
|
||||
mapping(bytes32 => uint256) private uints;
|
||||
|
||||
function getUint(bytes32 _key) public view returns (uint256) {
|
||||
return uints[_key];
|
||||
}
|
||||
|
||||
function _setUint(bytes32 _key, uint256 _value) internal {
|
||||
uints[_key] = _value;
|
||||
}
|
||||
|
||||
}
|
52
contracts/fees/FeeProvider.sol
Normal file
52
contracts/fees/FeeProvider.sol
Normal file
|
@ -0,0 +1,52 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
import "../interfaces/IFeeProvider.sol";
|
||||
import "../libraries/WadRayMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title FeeProvider contract
|
||||
* @notice Implements calculation for the fees applied by the protocol
|
||||
* @author Aave
|
||||
**/
|
||||
contract FeeProvider is IFeeProvider, VersionedInitializable {
|
||||
using WadRayMath for uint256;
|
||||
|
||||
// percentage of the fee to be calculated on the loan amount
|
||||
uint256 public originationFeePercentage;
|
||||
|
||||
|
||||
uint256 constant public FEE_PROVIDER_REVISION = 0x1;
|
||||
|
||||
function getRevision() internal override pure returns(uint256) {
|
||||
return FEE_PROVIDER_REVISION;
|
||||
}
|
||||
/**
|
||||
* @dev initializes the FeeProvider after it's added to the proxy
|
||||
* @param _addressesProvider the address of the LendingPoolAddressesProvider
|
||||
*/
|
||||
function initialize(address _addressesProvider) public initializer {
|
||||
/// @notice origination fee is set as default as 25 basis points of the loan amount (0.0025%)
|
||||
originationFeePercentage = 0.0025 * 1e18;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the origination fee for every loan executed on the platform.
|
||||
* @param _user can be used in the future to apply discount to the origination fee based on the
|
||||
* _user account (eg. stake AAVE tokens in the lending pool, or deposit > 1M USD etc.)
|
||||
* @param _amount the amount of the loan
|
||||
**/
|
||||
function calculateLoanOriginationFee(address _user, uint256 _amount) external override view returns (uint256) {
|
||||
return _amount.wadMul(originationFeePercentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the origination fee percentage
|
||||
**/
|
||||
function getLoanOriginationFeePercentage() external override view returns (uint256) {
|
||||
return originationFeePercentage;
|
||||
}
|
||||
|
||||
}
|
45
contracts/fees/MockKyberProxy.sol
Normal file
45
contracts/fees/MockKyberProxy.sol
Normal file
|
@ -0,0 +1,45 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
import "../mocks/tokens/MintableERC20.sol";
|
||||
|
||||
/// @title MockKyberProxy
|
||||
/// @author Aave
|
||||
/// @notice Mock contract to simulate the behaviour of the Kyber DEX
|
||||
/// - Receives ETH/tokens
|
||||
/// - Mints the tokenToBurn
|
||||
/// - Sends back the tokenToBurn
|
||||
contract MockKyberProxy {
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeERC20 for MintableERC20;
|
||||
|
||||
/// @notice The token which the msg.sender of tradeWithHint will burn
|
||||
MintableERC20 public tokenToBurn;
|
||||
|
||||
constructor(MintableERC20 _tokenToBurn) public {
|
||||
tokenToBurn = _tokenToBurn;
|
||||
}
|
||||
|
||||
/// @notice Simulates the function with the same name on the Kyber Proxy contract
|
||||
function tradeWithHint(
|
||||
IERC20 _fromToken,
|
||||
uint256 _amount,
|
||||
IERC20 _toToken,
|
||||
address _receiver,
|
||||
uint256 _maxAmount,
|
||||
uint minConversionRate,
|
||||
address _referral,
|
||||
bytes calldata _filtering
|
||||
) external payable returns(uint256) {
|
||||
require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()");
|
||||
if (address(_fromToken) != EthAddressLib.ethAddress()) {
|
||||
_fromToken.safeTransferFrom(msg.sender, address(this), _amount);
|
||||
}
|
||||
tokenToBurn.safeTransfer(msg.sender, 1 ether);
|
||||
return 1 ether;
|
||||
}
|
||||
}
|
63
contracts/fees/MockOneSplit.sol
Normal file
63
contracts/fees/MockOneSplit.sol
Normal file
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
import "../mocks/tokens/MintableERC20.sol";
|
||||
|
||||
import "../interfaces/IOneSplit.sol";
|
||||
|
||||
contract MockOneSplit is IOneSplit {
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeERC20 for MintableERC20;
|
||||
|
||||
MintableERC20 public tokenToBurn;
|
||||
|
||||
constructor(MintableERC20 _tokenToBurn) public {
|
||||
tokenToBurn = _tokenToBurn;
|
||||
}
|
||||
|
||||
function getExpectedReturn(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 parts,
|
||||
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
|
||||
)
|
||||
public
|
||||
override
|
||||
view
|
||||
returns(
|
||||
uint256 returnAmount,
|
||||
uint256[] memory distribution // [Uniswap, Kyber, Bancor, Oasis]
|
||||
) {
|
||||
return (0, new uint256[](0));
|
||||
}
|
||||
|
||||
function swap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 minReturn,
|
||||
uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis]
|
||||
uint256 disableFlags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
|
||||
) public override payable {
|
||||
}
|
||||
|
||||
function goodSwap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 minReturn,
|
||||
uint256 parts,
|
||||
uint256 disableFlags
|
||||
) public override payable {
|
||||
require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()");
|
||||
if (address(fromToken) != EthAddressLib.ethAddress()) {
|
||||
fromToken.safeTransferFrom(msg.sender, address(this), amount);
|
||||
}
|
||||
tokenToBurn.safeTransfer(msg.sender, 10000 ether);
|
||||
}
|
||||
}
|
79
contracts/fees/OneSplitAdapter.sol
Normal file
79
contracts/fees/OneSplitAdapter.sol
Normal file
|
@ -0,0 +1,79 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
import "../interfaces/IOneSplit.sol";
|
||||
import "../interfaces/IPriceOracleGetter.sol";
|
||||
import "../interfaces/IExchangeAdapter.sol";
|
||||
|
||||
/// @title OneSplitAdapter
|
||||
/// @author Aave
|
||||
/// @notice Implements the logic to exchange assets through 1Split
|
||||
/// The hardcoded parameters are:
|
||||
/// 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476: ONE_SPLIT. The address of the 1Split exchange
|
||||
/// 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4: AAVE_PRICES_PROVIDER. Contract providing prices of the assets
|
||||
/// in the Aave protocol, in token/ETH.
|
||||
/// 512 : MULTI_PATH_ETH_FLAG. By using this flag on OneSplit, the swap sequence will introduce a step to ETH
|
||||
/// in the middle, resulting in a sequence like FROM-to-ETH -> ETH-to-TO.
|
||||
/// This is optimal for cases where the pair FROM/TO is not liquid enough in the
|
||||
/// underlying exchanges used by OneSplit, reducing this way the slippage.
|
||||
/// 10: SPLIT_PARTS. It defines in how many chunks the amount to swap will be splitted to then
|
||||
/// divide the chunks amongst the underlying exchanges.
|
||||
/// For example, using 10 as SPLIT_PARTS and having 4 underlying exchanges on 1Split,
|
||||
/// the division amongst could look like [4,4,0,2].
|
||||
|
||||
contract OneSplitAdapter is IExchangeAdapter {
|
||||
using SafeMath for uint256;
|
||||
|
||||
event OneSplitAdapterSetup(address oneSplit, address priceOracle, uint256 splitParts);
|
||||
|
||||
constructor() public {
|
||||
emit OneSplitAdapterSetup(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4, 10);
|
||||
}
|
||||
|
||||
/// @notice "Infinite" approval for all the tokens initialized
|
||||
/// @param _tokens the list of token addresses to approve
|
||||
function approveExchange(IERC20[] calldata _tokens) external override {
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
if (address(_tokens[i]) != EthAddressLib.ethAddress()) {
|
||||
_tokens[i].safeApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Exchanges _amount of _from token (or ETH) to _to token (or ETH)
|
||||
/// - Uses EthAddressLib.ethAddress() as the reference on 1Split of ETH
|
||||
/// @param _from The asset to exchange from
|
||||
/// @param _to The asset to exchange to
|
||||
/// @param _amount The amount to exchange
|
||||
/// @param _maxSlippage Max slippage acceptable, taken into account after the goodSwap()
|
||||
function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external override returns(uint256) {
|
||||
uint256 _value = (_from == EthAddressLib.ethAddress()) ? _amount : 0;
|
||||
|
||||
uint256 _fromAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_from);
|
||||
uint256 _toAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_to);
|
||||
uint256 _toBalanceBefore = IERC20(_to).balanceOf(address(this));
|
||||
|
||||
IOneSplit(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476).goodSwap{value: _value}(
|
||||
IERC20(_from),
|
||||
IERC20(_to),
|
||||
_amount,
|
||||
0,
|
||||
10,
|
||||
512
|
||||
);
|
||||
|
||||
uint256 _toReceivedAmount = IERC20(_to).balanceOf(address(this)).sub(_toBalanceBefore);
|
||||
|
||||
require(
|
||||
(_toAssetPriceInWei.mul(_toReceivedAmount).mul(100))
|
||||
.div(_fromAssetPriceInWei.mul(_amount)) >= (100 - _maxSlippage),
|
||||
"INVALID_SLIPPAGE"
|
||||
);
|
||||
|
||||
emit Exchange(_from, _to, 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, _amount, _toReceivedAmount);
|
||||
return _toReceivedAmount;
|
||||
}
|
||||
}
|
218
contracts/fees/TokenDistributor.sol
Normal file
218
contracts/fees/TokenDistributor.sol
Normal file
|
@ -0,0 +1,218 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
import "../interfaces/IKyberNetworkProxyInterface.sol";
|
||||
import "../interfaces/IExchangeAdapter.sol";
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
|
||||
|
||||
/// @title TokenDistributor
|
||||
/// @author Aave
|
||||
/// @notice Receives tokens and manages the distribution amongst receivers
|
||||
/// The usage is as follows:
|
||||
/// - The distribution addresses and percentages are set up on construction
|
||||
/// - The Kyber Proxy is approved for a list of tokens in construction, which will be later burnt
|
||||
/// - At any moment, anyone can call distribute() with a list of token addresses in order to distribute
|
||||
/// the accumulated token amounts and/or ETH in this contract to all the receivers with percentages
|
||||
/// - If the address(0) is used as receiver, this contract will trade in Kyber to tokenToBurn (LEND)
|
||||
/// and burn it (sending to address(0) the tokenToBurn)
|
||||
contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
struct Distribution {
|
||||
address[] receivers;
|
||||
uint256[] percentages;
|
||||
}
|
||||
|
||||
event DistributionUpdated(address[] receivers, uint256[] percentages);
|
||||
event Distributed(address receiver, uint256 percentage, uint256 amount);
|
||||
event Setup(address tokenToBurn, IExchangeAdapter exchangeAdapter, address _recipientBurn);
|
||||
event Burn(uint256 amount);
|
||||
|
||||
uint256 public constant IMPLEMENTATION_REVISION = 0x3;
|
||||
|
||||
/// @notice DEPRECATED
|
||||
uint256 public constant MAX_UINT = 2**256 - 1;
|
||||
|
||||
/// @notice DEPRECATED
|
||||
uint256 public constant MAX_UINT_MINUS_ONE = (2**256 - 1) - 1;
|
||||
|
||||
/// @notice DEPRECATED
|
||||
uint256 public constant MIN_CONVERSION_RATE = 1;
|
||||
|
||||
/// @notice DEPRECATED
|
||||
address public constant KYBER_ETH_MOCK_ADDRESS = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
|
||||
|
||||
/// @notice Defines how tokens and ETH are distributed on each call to .distribute()
|
||||
Distribution private distribution;
|
||||
|
||||
/// @notice Instead of using 100 for percentages, higher base to have more precision in the distribution
|
||||
uint256 public constant DISTRIBUTION_BASE = 10000;
|
||||
|
||||
/// @notice DEPRECATED
|
||||
IKyberNetworkProxyInterface public kyberProxy;
|
||||
|
||||
/// @notice The address of the token to burn (LEND token)
|
||||
address public tokenToBurn;
|
||||
|
||||
/// @notice Address to send tokens to "burn".
|
||||
/// Because of limitations on OZ ERC20, on dev it's needed to use the 0x00000...1 address instead of address(0)
|
||||
/// So this param needs to be received on construction
|
||||
address public recipientBurn;
|
||||
|
||||
/// @notice Smart contract implementing the logic to interact with a particular exchange.
|
||||
/// Will be called by DELEGATECALL
|
||||
IExchangeAdapter public exchangeAdapter;
|
||||
|
||||
/// @notice Called by the proxy when setting this contract as implementation
|
||||
function initialize(
|
||||
address _recipientBurn,
|
||||
address _tokenToBurn,
|
||||
IExchangeAdapter _exchangeAdapter,
|
||||
address[] memory _receivers,
|
||||
uint256[] memory _percentages,
|
||||
IERC20[] memory _tokens
|
||||
) public initializer {
|
||||
recipientBurn = _recipientBurn;
|
||||
tokenToBurn = _tokenToBurn;
|
||||
exchangeAdapter = _exchangeAdapter;
|
||||
internalSetTokenDistribution(_receivers, _percentages);
|
||||
approveExchange(_tokens);
|
||||
emit Setup(_tokenToBurn, _exchangeAdapter, _recipientBurn);
|
||||
}
|
||||
|
||||
/// @notice In order to receive ETH transfers
|
||||
receive() external payable {}
|
||||
|
||||
/// @notice "Infinite" approval for all the tokens initialized
|
||||
/// @param _tokens List of IERC20 to approve
|
||||
function approveExchange(IERC20[] memory _tokens) public {
|
||||
(bool _success, ) = address(exchangeAdapter).delegatecall(
|
||||
abi.encodeWithSelector(exchangeAdapter.approveExchange.selector, _tokens)
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Distributes the whole balance of a list of _tokens balances in this contract
|
||||
/// @param _tokens list of ERC20 tokens to distribute
|
||||
function distribute(IERC20[] memory _tokens) public {
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
uint256 _balanceToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress())
|
||||
? _tokens[i].balanceOf(address(this))
|
||||
: address(this).balance;
|
||||
if (_balanceToDistribute <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
internalDistributeTokenWithAmount(_tokens[i], _balanceToDistribute);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Distributes specific amounts of a list of _tokens
|
||||
/// @param _tokens list of ERC20 tokens to distribute
|
||||
/// @param _amounts list of amounts to distribute per token
|
||||
function distributeWithAmounts(IERC20[] memory _tokens, uint256[] memory _amounts) public {
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
internalDistributeTokenWithAmount(_tokens[i], _amounts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Distributes specific total balance's percentages of a list of _tokens
|
||||
/// @param _tokens list of ERC20 tokens to distribute
|
||||
/// @param _percentages list of percentages to distribute per token
|
||||
function distributeWithPercentages(IERC20[] memory _tokens, uint256[] memory _percentages) public {
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
uint256 _amountToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress())
|
||||
? _tokens[i].balanceOf(address(this)).mul(_percentages[i]).div(100)
|
||||
: address(this).balance.mul(_percentages[i]).div(100);
|
||||
if (_amountToDistribute <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
internalDistributeTokenWithAmount(_tokens[i], _amountToDistribute);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Sets _receivers addresses with _percentages for each one
|
||||
/// @param _receivers Array of addresses receiving a percentage of the distribution, both user addresses
|
||||
/// or contracts
|
||||
/// @param _percentages Array of percentages each _receivers member will get
|
||||
function internalSetTokenDistribution(address[] memory _receivers, uint256[] memory _percentages) internal {
|
||||
require(_receivers.length == _percentages.length, "Array lengths should be equal");
|
||||
|
||||
distribution = Distribution({receivers: _receivers, percentages: _percentages});
|
||||
emit DistributionUpdated(_receivers, _percentages);
|
||||
}
|
||||
|
||||
/// @notice Distributes a specific amount of a token owned by this contract
|
||||
/// @param _token The ERC20 token to distribute
|
||||
/// @param _amountToDistribute The specific amount to distribute
|
||||
function internalDistributeTokenWithAmount(IERC20 _token, uint256 _amountToDistribute) internal {
|
||||
address _tokenAddress = address(_token);
|
||||
Distribution memory _distribution = distribution;
|
||||
for (uint256 j = 0; j < _distribution.receivers.length; j++) {
|
||||
uint256 _amount = _amountToDistribute.mul(_distribution.percentages[j]).div(DISTRIBUTION_BASE);
|
||||
|
||||
//avoid transfers/burns of 0 tokens
|
||||
if(_amount == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_distribution.receivers[j] != address(0)) {
|
||||
if (_tokenAddress != EthAddressLib.ethAddress()) {
|
||||
_token.safeTransfer(_distribution.receivers[j], _amount);
|
||||
} else {
|
||||
//solium-disable-next-line
|
||||
(bool _success,) = _distribution.receivers[j].call{value: _amount}("");
|
||||
require(_success, "Reverted ETH transfer");
|
||||
}
|
||||
emit Distributed(_distribution.receivers[j], _distribution.percentages[j], _amount);
|
||||
} else {
|
||||
uint256 _amountToBurn = _amount;
|
||||
// If the token to burn is already tokenToBurn, we don't trade, burning directly
|
||||
if (_tokenAddress != tokenToBurn) {
|
||||
(bool _success, bytes memory _result) = address(exchangeAdapter).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
exchangeAdapter.exchange.selector,
|
||||
_tokenAddress,
|
||||
tokenToBurn,
|
||||
_amount,
|
||||
10
|
||||
)
|
||||
);
|
||||
require(_success, "ERROR_ON_EXCHANGE");
|
||||
_amountToBurn = abi.decode(_result, (uint256));
|
||||
}
|
||||
internalBurn(_amountToBurn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Internal function to send _amount of tokenToBurn to the 0x0 address
|
||||
/// @param _amount The amount to burn
|
||||
function internalBurn(uint256 _amount) internal {
|
||||
require(IERC20(tokenToBurn).transfer(recipientBurn, _amount), "INTERNAL_BURN. Reverted transfer to recipientBurn address");
|
||||
emit Burn(_amount);
|
||||
}
|
||||
|
||||
/// @notice Returns the receivers and percentages of the contract Distribution
|
||||
/// @return receivers array of addresses and percentages array on uints
|
||||
function getDistribution() public view returns(address[] memory receivers, uint256[] memory percentages) {
|
||||
receivers = distribution.receivers;
|
||||
percentages = distribution.percentages;
|
||||
}
|
||||
|
||||
/// @notice Gets the revision number of the contract
|
||||
/// @return The revision numeric reference
|
||||
function getRevision() internal override pure returns (uint256) {
|
||||
return IMPLEMENTATION_REVISION;
|
||||
}
|
||||
|
||||
}
|
52
contracts/flashloan/base/FlashLoanReceiverBase.sol
Normal file
52
contracts/flashloan/base/FlashLoanReceiverBase.sol
Normal file
|
@ -0,0 +1,52 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import "../interfaces/IFlashLoanReceiver.sol";
|
||||
import "../../interfaces/ILendingPoolAddressesProvider.sol";
|
||||
import "../../libraries/EthAddressLib.sol";
|
||||
|
||||
abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
|
||||
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeMath for uint256;
|
||||
|
||||
ILendingPoolAddressesProvider public addressesProvider;
|
||||
|
||||
constructor(ILendingPoolAddressesProvider _provider) public {
|
||||
addressesProvider = _provider;
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
function transferFundsBackToPoolInternal(address _reserve, uint256 _amount) internal {
|
||||
|
||||
address payable core = addressesProvider.getLendingPoolCore();
|
||||
|
||||
transferInternal(core,_reserve, _amount);
|
||||
}
|
||||
|
||||
function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal {
|
||||
if(_reserve == EthAddressLib.ethAddress()) {
|
||||
//solium-disable-next-line
|
||||
_destination.call{value: _amount}("");
|
||||
return;
|
||||
}
|
||||
|
||||
IERC20(_reserve).safeTransfer(_destination, _amount);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) {
|
||||
if(_reserve == EthAddressLib.ethAddress()) {
|
||||
|
||||
return _target.balance;
|
||||
}
|
||||
|
||||
return IERC20(_reserve).balanceOf(_target);
|
||||
|
||||
}
|
||||
}
|
13
contracts/flashloan/interfaces/IFlashLoanReceiver.sol
Normal file
13
contracts/flashloan/interfaces/IFlashLoanReceiver.sol
Normal file
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
* @title IFlashLoanReceiver interface
|
||||
* @notice Interface for the Aave fee IFlashLoanReceiver.
|
||||
* @author Aave
|
||||
* @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract
|
||||
**/
|
||||
interface IFlashLoanReceiver {
|
||||
|
||||
function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external;
|
||||
}
|
13
contracts/interfaces/IChainlinkAggregator.sol
Normal file
13
contracts/interfaces/IChainlinkAggregator.sol
Normal file
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface IChainlinkAggregator {
|
||||
function latestAnswer() external view returns (int256);
|
||||
function latestTimestamp() external view returns (uint256);
|
||||
function latestRound() external view returns (uint256);
|
||||
function getAnswer(uint256 roundId) external view returns (int256);
|
||||
function getTimestamp(uint256 roundId) external view returns (uint256);
|
||||
|
||||
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
|
||||
event NewRound(uint256 indexed roundId, address indexed startedBy);
|
||||
}
|
10
contracts/interfaces/IERC20Detailed.sol
Normal file
10
contracts/interfaces/IERC20Detailed.sol
Normal file
|
@ -0,0 +1,10 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
interface IERC20Detailed is IERC20 {
|
||||
function name() external view returns(string memory);
|
||||
function symbol() external view returns(string memory);
|
||||
function decimals() external view returns(uint8);
|
||||
}
|
24
contracts/interfaces/IExchangeAdapter.sol
Normal file
24
contracts/interfaces/IExchangeAdapter.sol
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
import "../libraries/UintConstants.sol";
|
||||
|
||||
interface IExchangeAdapter {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
event Exchange(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
address indexed platform,
|
||||
uint256 fromAmount,
|
||||
uint256 toAmount
|
||||
);
|
||||
|
||||
function approveExchange(IERC20[] calldata _tokens) external;
|
||||
|
||||
function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external returns(uint256);
|
||||
}
|
12
contracts/interfaces/IFeeProvider.sol
Normal file
12
contracts/interfaces/IFeeProvider.sol
Normal file
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
* @title IFeeProvider interface
|
||||
* @notice Interface for the Aave fee provider.
|
||||
**/
|
||||
|
||||
interface IFeeProvider {
|
||||
function calculateLoanOriginationFee(address _user, uint256 _amount) external view returns (uint256);
|
||||
function getLoanOriginationFeePercentage() external view returns (uint256);
|
||||
}
|
23
contracts/interfaces/IKyberNetworkProxyInterface.sol
Normal file
23
contracts/interfaces/IKyberNetworkProxyInterface.sol
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
interface IKyberNetworkProxyInterface {
|
||||
function maxGasPrice() external view returns(uint);
|
||||
function getUserCapInWei(address user) external view returns(uint);
|
||||
function getUserCapInTokenWei(address user, IERC20 token) external view returns(uint);
|
||||
function enabled() external view returns(bool);
|
||||
function info(bytes32 id) external view returns(uint);
|
||||
function getExpectedRate(IERC20 src, IERC20 dest, uint srcQty)
|
||||
external view returns (uint expectedRate, uint slippageRate);
|
||||
function tradeWithHint(
|
||||
IERC20 src,
|
||||
uint srcAmount,
|
||||
IERC20 dest,
|
||||
address destAddress,
|
||||
uint maxDestAmount,
|
||||
uint minConversionRate,
|
||||
address walletId,
|
||||
bytes calldata hint) external payable returns(uint);
|
||||
}
|
45
contracts/interfaces/ILendingPoolAddressesProvider.sol
Normal file
45
contracts/interfaces/ILendingPoolAddressesProvider.sol
Normal file
|
@ -0,0 +1,45 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
@title ILendingPoolAddressesProvider interface
|
||||
@notice provides the interface to fetch the LendingPoolCore address
|
||||
*/
|
||||
|
||||
interface ILendingPoolAddressesProvider {
|
||||
|
||||
function getLendingPool() external view returns (address);
|
||||
function setLendingPoolImpl(address _pool) external;
|
||||
|
||||
function getLendingPoolCore() external view returns (address payable);
|
||||
function setLendingPoolCoreImpl(address _lendingPoolCore) external;
|
||||
|
||||
function getLendingPoolConfigurator() external view returns (address);
|
||||
function setLendingPoolConfiguratorImpl(address _configurator) external;
|
||||
|
||||
function getLendingPoolDataProvider() external view returns (address);
|
||||
function setLendingPoolDataProviderImpl(address _provider) external;
|
||||
|
||||
function getLendingPoolParametersProvider() external view returns (address);
|
||||
function setLendingPoolParametersProviderImpl(address _parametersProvider) external;
|
||||
|
||||
function getTokenDistributor() external view returns (address);
|
||||
function setTokenDistributor(address _tokenDistributor) external;
|
||||
|
||||
|
||||
function getFeeProvider() external view returns (address);
|
||||
function setFeeProviderImpl(address _feeProvider) external;
|
||||
|
||||
function getLendingPoolLiquidationManager() external view returns (address);
|
||||
function setLendingPoolLiquidationManager(address _manager) external;
|
||||
|
||||
function getLendingPoolManager() external view returns (address);
|
||||
function setLendingPoolManager(address _lendingPoolManager) external;
|
||||
|
||||
function getPriceOracle() external view returns (address);
|
||||
function setPriceOracle(address _priceOracle) external;
|
||||
|
||||
function getLendingRateOracle() external view returns (address);
|
||||
function setLendingRateOracle(address _lendingRateOracle) external;
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
* @title ILendingPoolAddressesProvider interface
|
||||
* @notice provides the interface to fetch the LendingPoolCore address
|
||||
**/
|
||||
|
||||
interface ILendingPoolAddressesProviderRegistry {
|
||||
|
||||
function getAddressesProvidersList() external view returns (address[] memory);
|
||||
function isAddressesProviderRegistered(address _provider) external view returns (uint256);
|
||||
|
||||
function registerAddressesProvider(address _provider, uint256 _id) external;
|
||||
function unregisterAddressesProvider(address _provider) external;
|
||||
}
|
19
contracts/interfaces/ILendingRateOracle.sol
Normal file
19
contracts/interfaces/ILendingRateOracle.sol
Normal file
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
* @title ILendingRateOracle interface
|
||||
* @notice Interface for the Aave borrow rate oracle. Provides the average market borrow rate to be used as a base for the stable borrow rate calculations
|
||||
**/
|
||||
|
||||
interface ILendingRateOracle {
|
||||
/**
|
||||
@dev returns the market borrow rate in ray
|
||||
**/
|
||||
function getMarketBorrowRate(address _asset) external view returns (uint256);
|
||||
|
||||
/**
|
||||
@dev sets the market borrow rate. Rate value must be in ray
|
||||
**/
|
||||
function setMarketBorrowRate(address _asset, uint256 _rate) external;
|
||||
}
|
60
contracts/interfaces/IOneSplit.sol
Normal file
60
contracts/interfaces/IOneSplit.sol
Normal file
|
@ -0,0 +1,60 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
|
||||
abstract contract IOneSplitView {
|
||||
// disableFlags = FLAG_UNISWAP + FLAG_KYBER + ...
|
||||
uint256 public constant FLAG_UNISWAP = 0x01;
|
||||
uint256 public constant FLAG_KYBER = 0x02;
|
||||
uint256 public constant FLAG_KYBER_UNISWAP_RESERVE = 0x100000000; // Turned off by default
|
||||
uint256 public constant FLAG_KYBER_OASIS_RESERVE = 0x200000000; // Turned off by default
|
||||
uint256 public constant FLAG_KYBER_BANCOR_RESERVE = 0x400000000; // Turned off by default
|
||||
uint256 public constant FLAG_BANCOR = 0x04;
|
||||
uint256 public constant FLAG_OASIS = 0x08;
|
||||
uint256 public constant FLAG_COMPOUND = 0x10;
|
||||
uint256 public constant FLAG_FULCRUM = 0x20;
|
||||
uint256 public constant FLAG_CHAI = 0x40;
|
||||
uint256 public constant FLAG_AAVE = 0x80;
|
||||
uint256 public constant FLAG_SMART_TOKEN = 0x100;
|
||||
uint256 public constant FLAG_MULTI_PATH_ETH = 0x200; // Turned off by default
|
||||
uint256 public constant FLAG_BDAI = 0x400;
|
||||
uint256 public constant FLAG_IEARN = 0x800;
|
||||
|
||||
function getExpectedReturn(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 parts,
|
||||
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
|
||||
)
|
||||
public
|
||||
virtual
|
||||
view
|
||||
returns(
|
||||
uint256 returnAmount,
|
||||
uint256[] memory distribution // [Uniswap, Kyber, Bancor, Oasis]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
abstract contract IOneSplit is IOneSplitView {
|
||||
function swap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 minReturn,
|
||||
uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis]
|
||||
uint256 disableFlags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
|
||||
) public virtual payable;
|
||||
|
||||
function goodSwap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 amount,
|
||||
uint256 minReturn,
|
||||
uint256 parts,
|
||||
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
|
||||
) public virtual payable;
|
||||
}
|
18
contracts/interfaces/IPriceOracle.sol
Normal file
18
contracts/interfaces/IPriceOracle.sol
Normal file
|
@ -0,0 +1,18 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/************
|
||||
@title IPriceOracle interface
|
||||
@notice Interface for the Aave price oracle.*/
|
||||
interface IPriceOracle {
|
||||
/***********
|
||||
@dev returns the asset price in ETH
|
||||
*/
|
||||
function getAssetPrice(address _asset) external view returns (uint256);
|
||||
|
||||
/***********
|
||||
@dev sets the asset price, in wei
|
||||
*/
|
||||
function setAssetPrice(address _asset, uint256 _price) external;
|
||||
|
||||
}
|
16
contracts/interfaces/IPriceOracleGetter.sol
Normal file
16
contracts/interfaces/IPriceOracleGetter.sol
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
* @title IPriceOracleGetter interface
|
||||
* @notice Interface for the Aave price oracle.
|
||||
**/
|
||||
|
||||
interface IPriceOracleGetter {
|
||||
/**
|
||||
* @dev returns the asset price in ETH
|
||||
* @param _asset the address of the asset
|
||||
* @return the ETH price of the asset
|
||||
**/
|
||||
function getAssetPrice(address _asset) external view returns (uint256);
|
||||
}
|
30
contracts/interfaces/IReserveInterestRateStrategy.sol
Normal file
30
contracts/interfaces/IReserveInterestRateStrategy.sol
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
/**
|
||||
@title IReserveInterestRateStrategyInterface interface
|
||||
@notice Interface for the calculation of the interest rates.
|
||||
*/
|
||||
|
||||
interface IReserveInterestRateStrategy {
|
||||
|
||||
/**
|
||||
* @dev returns the base variable borrow rate, in rays
|
||||
*/
|
||||
|
||||
function getBaseVariableBorrowRate() external view returns (uint256);
|
||||
/**
|
||||
* @dev calculates the liquidity, stable, and variable rates depending on the current utilization rate
|
||||
* and the base parameters
|
||||
*
|
||||
*/
|
||||
function calculateInterestRates(
|
||||
address _reserve,
|
||||
uint256 _utilizationRate,
|
||||
uint256 _totalBorrowsStable,
|
||||
uint256 _totalBorrowsVariable,
|
||||
uint256 _averageStableBorrowRate)
|
||||
external
|
||||
view
|
||||
returns (uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate);
|
||||
}
|
9
contracts/interfaces/IUniswapExchange.sol
Normal file
9
contracts/interfaces/IUniswapExchange.sol
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface IUniswapExchange {
|
||||
event TokenPurchase(address indexed buyer, uint256 indexed eth_sold, uint256 indexed tokens_bought);
|
||||
event EthPurchase(address indexed buyer, uint256 indexed tokens_sold, uint256 indexed eth_bought);
|
||||
event AddLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount);
|
||||
event RemoveLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount);
|
||||
}
|
203
contracts/lendingpool/DefaultReserveInterestRateStrategy.sol
Normal file
203
contracts/lendingpool/DefaultReserveInterestRateStrategy.sol
Normal file
|
@ -0,0 +1,203 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "../interfaces/IReserveInterestRateStrategy.sol";
|
||||
import "../libraries/WadRayMath.sol";
|
||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||
import "./LendingPoolCore.sol";
|
||||
import "../interfaces/ILendingRateOracle.sol";
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
|
||||
/**
|
||||
* @title DefaultReserveInterestRateStrategy contract
|
||||
* @notice implements the calculation of the interest rates depending on the reserve parameters.
|
||||
* @dev if there is need to update the calculation of the interest rates for a specific reserve,
|
||||
* a new version of this contract will be deployed.
|
||||
* @author Aave
|
||||
**/
|
||||
contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
|
||||
using WadRayMath for uint256;
|
||||
using SafeMath for uint256;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @dev this constant represents the utilization rate at which the pool aims to obtain most competitive borrow rates
|
||||
* expressed in ray
|
||||
**/
|
||||
uint256 public constant OPTIMAL_UTILIZATION_RATE = 0.8 * 1e27;
|
||||
|
||||
/**
|
||||
* @dev this constant represents the excess utilization rate above the optimal. It's always equal to
|
||||
* 1-optimal utilization rate. Added as a constant here for gas optimizations
|
||||
* expressed in ray
|
||||
**/
|
||||
|
||||
uint256 public constant EXCESS_UTILIZATION_RATE = 0.2 * 1e27;
|
||||
|
||||
LendingPoolAddressesProvider public addressesProvider;
|
||||
|
||||
|
||||
//base variable borrow rate when Utilization rate = 0. Expressed in ray
|
||||
uint256 public baseVariableBorrowRate;
|
||||
|
||||
//slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
|
||||
uint256 public variableRateSlope1;
|
||||
|
||||
//slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
|
||||
uint256 public variableRateSlope2;
|
||||
|
||||
//slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
|
||||
uint256 public stableRateSlope1;
|
||||
|
||||
//slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
|
||||
uint256 public stableRateSlope2;
|
||||
address public reserve;
|
||||
|
||||
constructor(
|
||||
address _reserve,
|
||||
LendingPoolAddressesProvider _provider,
|
||||
uint256 _baseVariableBorrowRate,
|
||||
uint256 _variableRateSlope1,
|
||||
uint256 _variableRateSlope2,
|
||||
uint256 _stableRateSlope1,
|
||||
uint256 _stableRateSlope2
|
||||
) public {
|
||||
addressesProvider = _provider;
|
||||
baseVariableBorrowRate = _baseVariableBorrowRate;
|
||||
variableRateSlope1 = _variableRateSlope1;
|
||||
variableRateSlope2 = _variableRateSlope2;
|
||||
stableRateSlope1 = _stableRateSlope1;
|
||||
stableRateSlope2 = _stableRateSlope2;
|
||||
reserve = _reserve;
|
||||
}
|
||||
|
||||
/**
|
||||
@dev accessors
|
||||
*/
|
||||
|
||||
function getBaseVariableBorrowRate() external override view returns (uint256) {
|
||||
return baseVariableBorrowRate;
|
||||
}
|
||||
|
||||
function getVariableRateSlope1() external view returns (uint256) {
|
||||
return variableRateSlope1;
|
||||
}
|
||||
|
||||
function getVariableRateSlope2() external view returns (uint256) {
|
||||
return variableRateSlope2;
|
||||
}
|
||||
|
||||
function getStableRateSlope1() external view returns (uint256) {
|
||||
return stableRateSlope1;
|
||||
}
|
||||
|
||||
function getStableRateSlope2() external view returns (uint256) {
|
||||
return stableRateSlope2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the interest rates depending on the available liquidity and the total borrowed.
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _availableLiquidity the liquidity available in the reserve
|
||||
* @param _totalBorrowsStable the total borrowed from the reserve a stable rate
|
||||
* @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate
|
||||
* @param _averageStableBorrowRate the weighted average of all the stable rate borrows
|
||||
* @return currentLiquidityRate calculated from the input parameters TODO: add description
|
||||
* @return currentStableBorrowRate calculated from the input parameters TODO: add description
|
||||
* @return currentVariableBorrowRate calculated from the input parameters TODO: add description
|
||||
**/
|
||||
function calculateInterestRates(
|
||||
address _reserve,
|
||||
uint256 _availableLiquidity,
|
||||
uint256 _totalBorrowsStable,
|
||||
uint256 _totalBorrowsVariable,
|
||||
uint256 _averageStableBorrowRate
|
||||
)
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (
|
||||
uint256 currentLiquidityRate,
|
||||
uint256 currentStableBorrowRate,
|
||||
uint256 currentVariableBorrowRate
|
||||
)
|
||||
{
|
||||
uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable);
|
||||
|
||||
uint256 utilizationRate = (totalBorrows == 0 && _availableLiquidity == 0)
|
||||
? 0
|
||||
: totalBorrows.rayDiv(_availableLiquidity.add(totalBorrows));
|
||||
|
||||
currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle())
|
||||
.getMarketBorrowRate(_reserve);
|
||||
|
||||
if (utilizationRate > OPTIMAL_UTILIZATION_RATE) {
|
||||
uint256 excessUtilizationRateRatio = utilizationRate
|
||||
.sub(OPTIMAL_UTILIZATION_RATE)
|
||||
.rayDiv(EXCESS_UTILIZATION_RATE);
|
||||
|
||||
currentStableBorrowRate = currentStableBorrowRate.add(stableRateSlope1).add(
|
||||
stableRateSlope2.rayMul(excessUtilizationRateRatio)
|
||||
);
|
||||
|
||||
currentVariableBorrowRate = baseVariableBorrowRate.add(variableRateSlope1).add(
|
||||
variableRateSlope2.rayMul(excessUtilizationRateRatio)
|
||||
);
|
||||
} else {
|
||||
currentStableBorrowRate = currentStableBorrowRate.add(
|
||||
stableRateSlope1.rayMul(
|
||||
utilizationRate.rayDiv(
|
||||
OPTIMAL_UTILIZATION_RATE
|
||||
)
|
||||
)
|
||||
);
|
||||
currentVariableBorrowRate = baseVariableBorrowRate.add(
|
||||
utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE).rayMul(variableRateSlope1)
|
||||
);
|
||||
}
|
||||
|
||||
currentLiquidityRate = getOverallBorrowRateInternal(
|
||||
_totalBorrowsStable,
|
||||
_totalBorrowsVariable,
|
||||
currentVariableBorrowRate,
|
||||
_averageStableBorrowRate
|
||||
)
|
||||
.rayMul(utilizationRate);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the overall borrow rate as the weighted average between the total variable borrows and total stable borrows.
|
||||
* @param _totalBorrowsStable the total borrowed from the reserve a stable rate
|
||||
* @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate
|
||||
* @param _currentVariableBorrowRate the current variable borrow rate
|
||||
* @param _currentAverageStableBorrowRate the weighted average of all the stable rate borrows
|
||||
* @return the weighted averaged borrow rate
|
||||
**/
|
||||
function getOverallBorrowRateInternal(
|
||||
uint256 _totalBorrowsStable,
|
||||
uint256 _totalBorrowsVariable,
|
||||
uint256 _currentVariableBorrowRate,
|
||||
uint256 _currentAverageStableBorrowRate
|
||||
) internal pure returns (uint256) {
|
||||
uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable);
|
||||
|
||||
if (totalBorrows == 0) return 0;
|
||||
|
||||
uint256 weightedVariableRate = _totalBorrowsVariable.wadToRay().rayMul(
|
||||
_currentVariableBorrowRate
|
||||
);
|
||||
|
||||
uint256 weightedStableRate = _totalBorrowsStable.wadToRay().rayMul(
|
||||
_currentAverageStableBorrowRate
|
||||
);
|
||||
|
||||
uint256 overallBorrowRate = weightedVariableRate.add(weightedStableRate).rayDiv(
|
||||
totalBorrows.wadToRay()
|
||||
);
|
||||
|
||||
return overallBorrowRate;
|
||||
}
|
||||
}
|
1008
contracts/lendingpool/LendingPool.sol
Normal file
1008
contracts/lendingpool/LendingPool.sol
Normal file
File diff suppressed because it is too large
Load Diff
451
contracts/lendingpool/LendingPoolConfigurator.sol
Normal file
451
contracts/lendingpool/LendingPoolConfigurator.sol
Normal file
|
@ -0,0 +1,451 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
|
||||
import "../interfaces/IERC20Detailed.sol";
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||
import "./LendingPoolCore.sol";
|
||||
import "../tokenization/AToken.sol";
|
||||
|
||||
/**
|
||||
* @title LendingPoolConfigurator contract
|
||||
* @author Aave
|
||||
* @notice Executes configuration methods on the LendingPoolCore contract. Allows to enable/disable reserves,
|
||||
* and set different protocol parameters.
|
||||
**/
|
||||
|
||||
contract LendingPoolConfigurator is VersionedInitializable {
|
||||
using SafeMath for uint256;
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is initialized.
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _aToken the address of the overlying aToken contract
|
||||
* @param _interestRateStrategyAddress the address of the interest rate strategy for the reserve
|
||||
**/
|
||||
event ReserveInitialized(
|
||||
address indexed _reserve,
|
||||
address indexed _aToken,
|
||||
address _interestRateStrategyAddress
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is removed.
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveRemoved(
|
||||
address indexed _reserve
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when borrowing is enabled on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _stableRateEnabled true if stable rate borrowing is enabled, false otherwise
|
||||
**/
|
||||
event BorrowingEnabledOnReserve(address _reserve, bool _stableRateEnabled);
|
||||
|
||||
/**
|
||||
* @dev emitted when borrowing is disabled on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event BorrowingDisabledOnReserve(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is enabled as collateral.
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _ltv the loan to value of the asset when used as collateral
|
||||
* @param _liquidationThreshold the threshold at which loans using this asset as collateral will be considered undercollateralized
|
||||
* @param _liquidationBonus the bonus liquidators receive to liquidate this asset
|
||||
**/
|
||||
event ReserveEnabledAsCollateral(
|
||||
address indexed _reserve,
|
||||
uint256 _ltv,
|
||||
uint256 _liquidationThreshold,
|
||||
uint256 _liquidationBonus
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is disabled as collateral
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveDisabledAsCollateral(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when stable rate borrowing is enabled on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event StableRateEnabledOnReserve(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when stable rate borrowing is disabled on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event StableRateDisabledOnReserve(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is activated
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveActivated(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is deactivated
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveDeactivated(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is freezed
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveFreezed(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve is unfreezed
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
event ReserveUnfreezed(address indexed _reserve);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve loan to value is updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _ltv the new value for the loan to value
|
||||
**/
|
||||
event ReserveBaseLtvChanged(address _reserve, uint256 _ltv);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve liquidation threshold is updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _threshold the new value for the liquidation threshold
|
||||
**/
|
||||
event ReserveLiquidationThresholdChanged(address _reserve, uint256 _threshold);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve liquidation bonus is updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _bonus the new value for the liquidation bonus
|
||||
**/
|
||||
event ReserveLiquidationBonusChanged(address _reserve, uint256 _bonus);
|
||||
|
||||
/**
|
||||
* @dev emitted when the reserve decimals are updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _decimals the new decimals
|
||||
**/
|
||||
event ReserveDecimalsChanged(address _reserve, uint256 _decimals);
|
||||
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve interest strategy contract is updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _strategy the new address of the interest strategy contract
|
||||
**/
|
||||
event ReserveInterestRateStrategyChanged(address _reserve, address _strategy);
|
||||
|
||||
LendingPoolAddressesProvider public poolAddressesProvider;
|
||||
/**
|
||||
* @dev only the lending pool manager can call functions affected by this modifier
|
||||
**/
|
||||
modifier onlyLendingPoolManager {
|
||||
require(
|
||||
poolAddressesProvider.getLendingPoolManager() == msg.sender,
|
||||
"The caller must be a lending pool manager"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
uint256 public constant CONFIGURATOR_REVISION = 0x3;
|
||||
|
||||
function getRevision() internal override pure returns (uint256) {
|
||||
return CONFIGURATOR_REVISION;
|
||||
}
|
||||
|
||||
function initialize(LendingPoolAddressesProvider _poolAddressesProvider) public initializer {
|
||||
poolAddressesProvider = _poolAddressesProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev initializes a reserve
|
||||
* @param _reserve the address of the reserve to be initialized
|
||||
* @param _underlyingAssetDecimals the decimals of the reserve underlying asset
|
||||
* @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve
|
||||
**/
|
||||
function initReserve(
|
||||
address _reserve,
|
||||
uint8 _underlyingAssetDecimals,
|
||||
address _interestRateStrategyAddress
|
||||
) external onlyLendingPoolManager {
|
||||
IERC20Detailed asset = IERC20Detailed(_reserve);
|
||||
|
||||
string memory aTokenName = string(abi.encodePacked("Aave Interest bearing ", asset.name()));
|
||||
string memory aTokenSymbol = string(abi.encodePacked("a", asset.symbol()));
|
||||
|
||||
initReserveWithData(
|
||||
_reserve,
|
||||
aTokenName,
|
||||
aTokenSymbol,
|
||||
_underlyingAssetDecimals,
|
||||
_interestRateStrategyAddress
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev initializes a reserve using aTokenData provided externally (useful if the underlying ERC20 contract doesn't expose name or decimals)
|
||||
* @param _reserve the address of the reserve to be initialized
|
||||
* @param _aTokenName the name of the aToken contract
|
||||
* @param _aTokenSymbol the symbol of the aToken contract
|
||||
* @param _underlyingAssetDecimals the decimals of the reserve underlying asset
|
||||
* @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve
|
||||
**/
|
||||
function initReserveWithData(
|
||||
address _reserve,
|
||||
string memory _aTokenName,
|
||||
string memory _aTokenSymbol,
|
||||
uint8 _underlyingAssetDecimals,
|
||||
address _interestRateStrategyAddress
|
||||
) public onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
|
||||
AToken aTokenInstance = new AToken(
|
||||
poolAddressesProvider,
|
||||
_reserve,
|
||||
_underlyingAssetDecimals,
|
||||
_aTokenName,
|
||||
_aTokenSymbol
|
||||
);
|
||||
core.initReserve(
|
||||
_reserve,
|
||||
address(aTokenInstance),
|
||||
_underlyingAssetDecimals,
|
||||
_interestRateStrategyAddress
|
||||
);
|
||||
|
||||
emit ReserveInitialized(
|
||||
_reserve,
|
||||
address(aTokenInstance),
|
||||
_interestRateStrategyAddress
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev removes the last added reserve in the list of the reserves
|
||||
* @param _reserveToRemove the address of the reserve
|
||||
**/
|
||||
function removeLastAddedReserve( address _reserveToRemove) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.removeLastAddedReserve(_reserveToRemove);
|
||||
emit ReserveRemoved(_reserveToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev enables borrowing on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _stableBorrowRateEnabled true if stable borrow rate needs to be enabled by default on this reserve
|
||||
**/
|
||||
function enableBorrowingOnReserve(address _reserve, bool _stableBorrowRateEnabled)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.enableBorrowingOnReserve(_reserve, _stableBorrowRateEnabled);
|
||||
emit BorrowingEnabledOnReserve(_reserve, _stableBorrowRateEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev disables borrowing on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function disableBorrowingOnReserve(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.disableBorrowingOnReserve(_reserve);
|
||||
|
||||
emit BorrowingDisabledOnReserve(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev enables a reserve to be used as collateral
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _baseLTVasCollateral the loan to value of the asset when used as collateral
|
||||
* @param _liquidationThreshold the threshold at which loans using this asset as collateral will be considered undercollateralized
|
||||
* @param _liquidationBonus the bonus liquidators receive to liquidate this asset
|
||||
**/
|
||||
function enableReserveAsCollateral(
|
||||
address _reserve,
|
||||
uint256 _baseLTVasCollateral,
|
||||
uint256 _liquidationThreshold,
|
||||
uint256 _liquidationBonus
|
||||
) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.enableReserveAsCollateral(
|
||||
_reserve,
|
||||
_baseLTVasCollateral,
|
||||
_liquidationThreshold,
|
||||
_liquidationBonus
|
||||
);
|
||||
emit ReserveEnabledAsCollateral(
|
||||
_reserve,
|
||||
_baseLTVasCollateral,
|
||||
_liquidationThreshold,
|
||||
_liquidationBonus
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev disables a reserve as collateral
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function disableReserveAsCollateral(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.disableReserveAsCollateral(_reserve);
|
||||
|
||||
emit ReserveDisabledAsCollateral(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev enable stable rate borrowing on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function enableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.enableReserveStableBorrowRate(_reserve);
|
||||
|
||||
emit StableRateEnabledOnReserve(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev disable stable rate borrowing on a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function disableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.disableReserveStableBorrowRate(_reserve);
|
||||
|
||||
emit StableRateDisabledOnReserve(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev activates a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function activateReserve(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.activateReserve(_reserve);
|
||||
|
||||
emit ReserveActivated(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev deactivates a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function deactivateReserve(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
require(core.getReserveTotalLiquidity(_reserve) == 0, "The liquidity of the reserve needs to be 0");
|
||||
core.deactivateReserve(_reserve);
|
||||
|
||||
emit ReserveDeactivated(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev freezes a reserve. A freezed reserve doesn't accept any new deposit, borrow or rate swap, but can accept repayments, liquidations, rate rebalances and redeems
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function freezeReserve(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.freezeReserve(_reserve);
|
||||
|
||||
emit ReserveFreezed(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev unfreezes a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
**/
|
||||
function unfreezeReserve(address _reserve) external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.unfreezeReserve(_reserve);
|
||||
|
||||
emit ReserveUnfreezed(_reserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve loan to value is updated
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _ltv the new value for the loan to value
|
||||
**/
|
||||
function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.setReserveBaseLTVasCollateral(_reserve, _ltv);
|
||||
emit ReserveBaseLtvChanged(_reserve, _ltv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the liquidation threshold of a reserve.
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _threshold the new value for the liquidation threshold
|
||||
**/
|
||||
function setReserveLiquidationThreshold(address _reserve, uint256 _threshold)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.setReserveLiquidationThreshold(_reserve, _threshold);
|
||||
emit ReserveLiquidationThresholdChanged(_reserve, _threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the liquidation bonus of a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _bonus the new value for the liquidation bonus
|
||||
**/
|
||||
function setReserveLiquidationBonus(address _reserve, uint256 _bonus)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.setReserveLiquidationBonus(_reserve, _bonus);
|
||||
emit ReserveLiquidationBonusChanged(_reserve, _bonus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev updates the reserve decimals
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _decimals the new number of decimals
|
||||
**/
|
||||
function setReserveDecimals(address _reserve, uint256 _decimals)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.setReserveDecimals(_reserve, _decimals);
|
||||
emit ReserveDecimalsChanged(_reserve, _decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev sets the interest rate strategy of a reserve
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _rateStrategyAddress the new address of the interest strategy contract
|
||||
**/
|
||||
function setReserveInterestRateStrategyAddress(address _reserve, address _rateStrategyAddress)
|
||||
external
|
||||
onlyLendingPoolManager
|
||||
{
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.setReserveInterestRateStrategyAddress(_reserve, _rateStrategyAddress);
|
||||
emit ReserveInterestRateStrategyChanged(_reserve, _rateStrategyAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev refreshes the lending pool core configuration to update the cached address
|
||||
**/
|
||||
function refreshLendingPoolCoreConfiguration() external onlyLendingPoolManager {
|
||||
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
|
||||
core.refreshConfiguration();
|
||||
}
|
||||
}
|
1802
contracts/lendingpool/LendingPoolCore.sol
Normal file
1802
contracts/lendingpool/LendingPoolCore.sol
Normal file
File diff suppressed because it is too large
Load Diff
482
contracts/lendingpool/LendingPoolDataProvider.sol
Normal file
482
contracts/lendingpool/LendingPoolDataProvider.sol
Normal file
|
@ -0,0 +1,482 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
|
||||
import "../libraries/CoreLibrary.sol";
|
||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||
import "../libraries/WadRayMath.sol";
|
||||
import "../interfaces/IPriceOracleGetter.sol";
|
||||
import "../interfaces/IFeeProvider.sol";
|
||||
import "../tokenization/AToken.sol";
|
||||
|
||||
import "./LendingPoolCore.sol";
|
||||
|
||||
/**
|
||||
* @title LendingPoolDataProvider contract
|
||||
* @author Aave
|
||||
* @notice Implements functions to fetch data from the core, and aggregate them in order to allow computation
|
||||
* on the compounded balances and the account balances in ETH
|
||||
**/
|
||||
contract LendingPoolDataProvider is VersionedInitializable {
|
||||
using SafeMath for uint256;
|
||||
using WadRayMath for uint256;
|
||||
|
||||
LendingPoolCore public core;
|
||||
LendingPoolAddressesProvider public addressesProvider;
|
||||
|
||||
/**
|
||||
* @dev specifies the health factor threshold at which the user position is liquidated.
|
||||
* 1e18 by default, if the health factor drops below 1e18, the loan can be liquidated.
|
||||
**/
|
||||
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
|
||||
|
||||
uint256 public constant DATA_PROVIDER_REVISION = 0x1;
|
||||
|
||||
function getRevision() internal override pure returns (uint256) {
|
||||
return DATA_PROVIDER_REVISION;
|
||||
}
|
||||
|
||||
function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer {
|
||||
addressesProvider = _addressesProvider;
|
||||
core = LendingPoolCore(_addressesProvider.getLendingPoolCore());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev struct to hold calculateUserGlobalData() local computations
|
||||
**/
|
||||
struct UserGlobalDataLocalVars {
|
||||
uint256 reserveUnitPrice;
|
||||
uint256 tokenUnit;
|
||||
uint256 compoundedLiquidityBalance;
|
||||
uint256 compoundedBorrowBalance;
|
||||
uint256 reserveDecimals;
|
||||
uint256 baseLtv;
|
||||
uint256 liquidationThreshold;
|
||||
uint256 originationFee;
|
||||
bool usageAsCollateralEnabled;
|
||||
bool userUsesReserveAsCollateral;
|
||||
address currentReserve;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the user data across the reserves.
|
||||
* this includes the total liquidity/collateral/borrow balances in ETH,
|
||||
* the average Loan To Value, the average Liquidation Ratio, and the Health factor.
|
||||
* @param _user the address of the user
|
||||
* @return totalLiquidityBalanceETH of the user in ETH
|
||||
* @return totalCollateralBalanceETH of the user in ETH
|
||||
* @return totalBorrowBalanceETH of the user in ETH
|
||||
* @return totalFeesETH of the user in ETH
|
||||
* @return currentLtv average Ltv
|
||||
* @return currentLiquidationThreshold liquidation threshold
|
||||
* @return healthFactor health factor
|
||||
* @return healthFactorBelowThreshold is the health factor below threshold
|
||||
**/
|
||||
function calculateUserGlobalData(address _user)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
uint256 totalLiquidityBalanceETH,
|
||||
uint256 totalCollateralBalanceETH,
|
||||
uint256 totalBorrowBalanceETH,
|
||||
uint256 totalFeesETH,
|
||||
uint256 currentLtv,
|
||||
uint256 currentLiquidationThreshold,
|
||||
uint256 healthFactor,
|
||||
bool healthFactorBelowThreshold
|
||||
)
|
||||
{
|
||||
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
|
||||
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
|
||||
UserGlobalDataLocalVars memory vars;
|
||||
|
||||
address[] memory reserves = core.getReserves();
|
||||
|
||||
for (uint256 i = 0; i < reserves.length; i++) {
|
||||
vars.currentReserve = reserves[i];
|
||||
|
||||
(
|
||||
vars.compoundedLiquidityBalance,
|
||||
vars.compoundedBorrowBalance,
|
||||
vars.originationFee,
|
||||
vars.userUsesReserveAsCollateral
|
||||
) = core.getUserBasicReserveData(vars.currentReserve, _user);
|
||||
|
||||
if (vars.compoundedLiquidityBalance == 0 && vars.compoundedBorrowBalance == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//fetch reserve data
|
||||
(
|
||||
vars.reserveDecimals,
|
||||
vars.baseLtv,
|
||||
vars.liquidationThreshold,
|
||||
vars.usageAsCollateralEnabled
|
||||
) = core.getReserveConfiguration(vars.currentReserve);
|
||||
|
||||
vars.tokenUnit = 10 ** vars.reserveDecimals;
|
||||
vars.reserveUnitPrice = oracle.getAssetPrice(vars.currentReserve);
|
||||
|
||||
//liquidity and collateral balance
|
||||
if (vars.compoundedLiquidityBalance > 0) {
|
||||
uint256 liquidityBalanceETH = vars
|
||||
.reserveUnitPrice
|
||||
.mul(vars.compoundedLiquidityBalance)
|
||||
.div(vars.tokenUnit);
|
||||
totalLiquidityBalanceETH = totalLiquidityBalanceETH.add(liquidityBalanceETH);
|
||||
|
||||
if (vars.usageAsCollateralEnabled && vars.userUsesReserveAsCollateral) {
|
||||
totalCollateralBalanceETH = totalCollateralBalanceETH.add(liquidityBalanceETH);
|
||||
currentLtv = currentLtv.add(liquidityBalanceETH.mul(vars.baseLtv));
|
||||
currentLiquidationThreshold = currentLiquidationThreshold.add(
|
||||
liquidityBalanceETH.mul(vars.liquidationThreshold)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (vars.compoundedBorrowBalance > 0) {
|
||||
totalBorrowBalanceETH = totalBorrowBalanceETH.add(
|
||||
vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit)
|
||||
);
|
||||
totalFeesETH = totalFeesETH.add(
|
||||
vars.originationFee.mul(vars.reserveUnitPrice).div(vars.tokenUnit)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
currentLtv = totalCollateralBalanceETH > 0 ? currentLtv.div(totalCollateralBalanceETH) : 0;
|
||||
currentLiquidationThreshold = totalCollateralBalanceETH > 0
|
||||
? currentLiquidationThreshold.div(totalCollateralBalanceETH)
|
||||
: 0;
|
||||
|
||||
healthFactor = calculateHealthFactorFromBalancesInternal(
|
||||
totalCollateralBalanceETH,
|
||||
totalBorrowBalanceETH,
|
||||
totalFeesETH,
|
||||
currentLiquidationThreshold
|
||||
);
|
||||
healthFactorBelowThreshold = healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
|
||||
|
||||
}
|
||||
|
||||
struct balanceDecreaseAllowedLocalVars {
|
||||
uint256 decimals;
|
||||
uint256 collateralBalanceETH;
|
||||
uint256 borrowBalanceETH;
|
||||
uint256 totalFeesETH;
|
||||
uint256 currentLiquidationThreshold;
|
||||
uint256 reserveLiquidationThreshold;
|
||||
uint256 amountToDecreaseETH;
|
||||
uint256 collateralBalancefterDecrease;
|
||||
uint256 liquidationThresholdAfterDecrease;
|
||||
uint256 healthFactorAfterDecrease;
|
||||
bool reserveUsageAsCollateralEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev check if a specific balance decrease is allowed (i.e. doesn't bring the user borrow position health factor under 1e18)
|
||||
* @param _reserve the address of the reserve
|
||||
* @param _user the address of the user
|
||||
* @param _amount the amount to decrease
|
||||
* @return true if the decrease of the balance is allowed
|
||||
**/
|
||||
|
||||
function balanceDecreaseAllowed(address _reserve, address _user, uint256 _amount)
|
||||
external
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
|
||||
balanceDecreaseAllowedLocalVars memory vars;
|
||||
|
||||
(
|
||||
vars.decimals,
|
||||
,
|
||||
vars.reserveLiquidationThreshold,
|
||||
vars.reserveUsageAsCollateralEnabled
|
||||
) = core.getReserveConfiguration(_reserve);
|
||||
|
||||
if (
|
||||
!vars.reserveUsageAsCollateralEnabled ||
|
||||
!core.isUserUseReserveAsCollateralEnabled(_reserve, _user)
|
||||
) {
|
||||
return true; //if reserve is not used as collateral, no reasons to block the transfer
|
||||
}
|
||||
|
||||
(
|
||||
,
|
||||
vars.collateralBalanceETH,
|
||||
vars.borrowBalanceETH,
|
||||
vars.totalFeesETH,
|
||||
,
|
||||
vars.currentLiquidationThreshold,
|
||||
,
|
||||
|
||||
) = calculateUserGlobalData(_user);
|
||||
|
||||
if (vars.borrowBalanceETH == 0) {
|
||||
return true; //no borrows - no reasons to block the transfer
|
||||
}
|
||||
|
||||
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
|
||||
vars.amountToDecreaseETH = oracle.getAssetPrice(_reserve).mul(_amount).div(
|
||||
10 ** vars.decimals
|
||||
);
|
||||
|
||||
vars.collateralBalancefterDecrease = vars.collateralBalanceETH.sub(
|
||||
vars.amountToDecreaseETH
|
||||
);
|
||||
|
||||
//if there is a borrow, there can't be 0 collateral
|
||||
if (vars.collateralBalancefterDecrease == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.liquidationThresholdAfterDecrease = vars
|
||||
.collateralBalanceETH
|
||||
.mul(vars.currentLiquidationThreshold)
|
||||
.sub(vars.amountToDecreaseETH.mul(vars.reserveLiquidationThreshold))
|
||||
.div(vars.collateralBalancefterDecrease);
|
||||
|
||||
uint256 healthFactorAfterDecrease = calculateHealthFactorFromBalancesInternal(
|
||||
vars.collateralBalancefterDecrease,
|
||||
vars.borrowBalanceETH,
|
||||
vars.totalFeesETH,
|
||||
vars.liquidationThresholdAfterDecrease
|
||||
);
|
||||
|
||||
return healthFactorAfterDecrease > HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice calculates the amount of collateral needed in ETH to cover a new borrow.
|
||||
* @param _reserve the reserve from which the user wants to borrow
|
||||
* @param _amount the amount the user wants to borrow
|
||||
* @param _fee the fee for the amount that the user needs to cover
|
||||
* @param _userCurrentBorrowBalanceTH the current borrow balance of the user (before the borrow)
|
||||
* @param _userCurrentLtv the average ltv of the user given his current collateral
|
||||
* @return the total amount of collateral in ETH to cover the current borrow balance + the new amount + fee
|
||||
**/
|
||||
function calculateCollateralNeededInETH(
|
||||
address _reserve,
|
||||
uint256 _amount,
|
||||
uint256 _fee,
|
||||
uint256 _userCurrentBorrowBalanceTH,
|
||||
uint256 _userCurrentFeesETH,
|
||||
uint256 _userCurrentLtv
|
||||
) external view returns (uint256) {
|
||||
uint256 reserveDecimals = core.getReserveDecimals(_reserve);
|
||||
|
||||
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
|
||||
uint256 requestedBorrowAmountETH = oracle
|
||||
.getAssetPrice(_reserve)
|
||||
.mul(_amount.add(_fee))
|
||||
.div(10 ** reserveDecimals); //price is in ether
|
||||
|
||||
//add the current already borrowed amount to the amount requested to calculate the total collateral needed.
|
||||
uint256 collateralNeededInETH = _userCurrentBorrowBalanceTH
|
||||
.add(_userCurrentFeesETH)
|
||||
.add(requestedBorrowAmountETH)
|
||||
.mul(100)
|
||||
.div(_userCurrentLtv); //LTV is calculated in percentage
|
||||
|
||||
return collateralNeededInETH;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the
|
||||
* average Loan To Value.
|
||||
* @param collateralBalanceETH the total collateral balance
|
||||
* @param borrowBalanceETH the total borrow balance
|
||||
* @param totalFeesETH the total fees
|
||||
* @param ltv the average loan to value
|
||||
* @return the amount available to borrow in ETH for the user
|
||||
**/
|
||||
|
||||
function calculateAvailableBorrowsETHInternal(
|
||||
uint256 collateralBalanceETH,
|
||||
uint256 borrowBalanceETH,
|
||||
uint256 totalFeesETH,
|
||||
uint256 ltv
|
||||
) internal view returns (uint256) {
|
||||
uint256 availableBorrowsETH = collateralBalanceETH.mul(ltv).div(100); //ltv is in percentage
|
||||
|
||||
if (availableBorrowsETH < borrowBalanceETH) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
availableBorrowsETH = availableBorrowsETH.sub(borrowBalanceETH.add(totalFeesETH));
|
||||
//calculate fee
|
||||
uint256 borrowFee = IFeeProvider(addressesProvider.getFeeProvider())
|
||||
.calculateLoanOriginationFee(msg.sender, availableBorrowsETH);
|
||||
return availableBorrowsETH.sub(borrowFee);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates the health factor from the corresponding balances
|
||||
* @param collateralBalanceETH the total collateral balance in ETH
|
||||
* @param borrowBalanceETH the total borrow balance in ETH
|
||||
* @param totalFeesETH the total fees in ETH
|
||||
* @param liquidationThreshold the avg liquidation threshold
|
||||
**/
|
||||
function calculateHealthFactorFromBalancesInternal(
|
||||
uint256 collateralBalanceETH,
|
||||
uint256 borrowBalanceETH,
|
||||
uint256 totalFeesETH,
|
||||
uint256 liquidationThreshold
|
||||
) internal pure returns (uint256) {
|
||||
if (borrowBalanceETH == 0) return uint256(-1);
|
||||
|
||||
return
|
||||
(collateralBalanceETH.mul(liquidationThreshold).div(100)).wadDiv(
|
||||
borrowBalanceETH.add(totalFeesETH)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the health factor liquidation threshold
|
||||
**/
|
||||
function getHealthFactorLiquidationThreshold() public pure returns (uint256) {
|
||||
return HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev accessory functions to fetch data from the lendingPoolCore
|
||||
**/
|
||||
function getReserveConfigurationData(address _reserve)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 ltv,
|
||||
uint256 liquidationThreshold,
|
||||
uint256 liquidationBonus,
|
||||
address rateStrategyAddress,
|
||||
bool usageAsCollateralEnabled,
|
||||
bool borrowingEnabled,
|
||||
bool stableBorrowRateEnabled,
|
||||
bool isActive
|
||||
)
|
||||
{
|
||||
(, ltv, liquidationThreshold, usageAsCollateralEnabled) = core.getReserveConfiguration(
|
||||
_reserve
|
||||
);
|
||||
stableBorrowRateEnabled = core.getReserveIsStableBorrowRateEnabled(_reserve);
|
||||
borrowingEnabled = core.isReserveBorrowingEnabled(_reserve);
|
||||
isActive = core.getReserveIsActive(_reserve);
|
||||
liquidationBonus = core.getReserveLiquidationBonus(_reserve);
|
||||
|
||||
rateStrategyAddress = core.getReserveInterestRateStrategyAddress(_reserve);
|
||||
}
|
||||
|
||||
function getReserveData(address _reserve)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 totalLiquidity,
|
||||
uint256 availableLiquidity,
|
||||
uint256 totalBorrowsStable,
|
||||
uint256 totalBorrowsVariable,
|
||||
uint256 liquidityRate,
|
||||
uint256 variableBorrowRate,
|
||||
uint256 stableBorrowRate,
|
||||
uint256 averageStableBorrowRate,
|
||||
uint256 utilizationRate,
|
||||
uint256 liquidityIndex,
|
||||
uint256 variableBorrowIndex,
|
||||
address aTokenAddress,
|
||||
uint40 lastUpdateTimestamp
|
||||
)
|
||||
{
|
||||
totalLiquidity = core.getReserveTotalLiquidity(_reserve);
|
||||
availableLiquidity = core.getReserveAvailableLiquidity(_reserve);
|
||||
totalBorrowsStable = core.getReserveTotalBorrowsStable(_reserve);
|
||||
totalBorrowsVariable = core.getReserveTotalBorrowsVariable(_reserve);
|
||||
liquidityRate = core.getReserveCurrentLiquidityRate(_reserve);
|
||||
variableBorrowRate = core.getReserveCurrentVariableBorrowRate(_reserve);
|
||||
stableBorrowRate = core.getReserveCurrentStableBorrowRate(_reserve);
|
||||
averageStableBorrowRate = core.getReserveCurrentAverageStableBorrowRate(_reserve);
|
||||
utilizationRate = core.getReserveUtilizationRate(_reserve);
|
||||
liquidityIndex = core.getReserveLiquidityCumulativeIndex(_reserve);
|
||||
variableBorrowIndex = core.getReserveVariableBorrowsCumulativeIndex(_reserve);
|
||||
aTokenAddress = core.getReserveATokenAddress(_reserve);
|
||||
lastUpdateTimestamp = core.getReserveLastUpdate(_reserve);
|
||||
}
|
||||
|
||||
function getUserAccountData(address _user)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 totalLiquidityETH,
|
||||
uint256 totalCollateralETH,
|
||||
uint256 totalBorrowsETH,
|
||||
uint256 totalFeesETH,
|
||||
uint256 availableBorrowsETH,
|
||||
uint256 currentLiquidationThreshold,
|
||||
uint256 ltv,
|
||||
uint256 healthFactor
|
||||
)
|
||||
{
|
||||
(
|
||||
totalLiquidityETH,
|
||||
totalCollateralETH,
|
||||
totalBorrowsETH,
|
||||
totalFeesETH,
|
||||
ltv,
|
||||
currentLiquidationThreshold,
|
||||
healthFactor,
|
||||
|
||||
) = calculateUserGlobalData(_user);
|
||||
|
||||
availableBorrowsETH = calculateAvailableBorrowsETHInternal(
|
||||
totalCollateralETH,
|
||||
totalBorrowsETH,
|
||||
totalFeesETH,
|
||||
ltv
|
||||
);
|
||||
}
|
||||
|
||||
function getUserReserveData(address _reserve, address _user)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 currentATokenBalance,
|
||||
uint256 currentBorrowBalance,
|
||||
uint256 principalBorrowBalance,
|
||||
uint256 borrowRateMode,
|
||||
uint256 borrowRate,
|
||||
uint256 liquidityRate,
|
||||
uint256 originationFee,
|
||||
uint256 variableBorrowIndex,
|
||||
uint256 lastUpdateTimestamp,
|
||||
bool usageAsCollateralEnabled
|
||||
)
|
||||
{
|
||||
currentATokenBalance = AToken(core.getReserveATokenAddress(_reserve)).balanceOf(_user);
|
||||
CoreLibrary.InterestRateMode mode = core.getUserCurrentBorrowRateMode(_reserve, _user);
|
||||
(principalBorrowBalance, currentBorrowBalance, ) = core.getUserBorrowBalances(
|
||||
_reserve,
|
||||
_user
|
||||
);
|
||||
|
||||
//default is 0, if mode == CoreLibrary.InterestRateMode.NONE
|
||||
if (mode == CoreLibrary.InterestRateMode.STABLE) {
|
||||
borrowRate = core.getUserCurrentStableBorrowRate(_reserve, _user);
|
||||
} else if (mode == CoreLibrary.InterestRateMode.VARIABLE) {
|
||||
borrowRate = core.getReserveCurrentVariableBorrowRate(_reserve);
|
||||
}
|
||||
|
||||
borrowRateMode = uint256(mode);
|
||||
liquidityRate = core.getReserveCurrentLiquidityRate(_reserve);
|
||||
originationFee = core.getUserOriginationFee(_reserve, _user);
|
||||
variableBorrowIndex = core.getUserVariableBorrowCumulativeIndex(_reserve, _user);
|
||||
lastUpdateTimestamp = core.getUserLastUpdate(_reserve, _user);
|
||||
usageAsCollateralEnabled = core.isUserUseReserveAsCollateralEnabled(_reserve, _user);
|
||||
}
|
||||
}
|
360
contracts/lendingpool/LendingPoolLiquidationManager.sol
Normal file
360
contracts/lendingpool/LendingPoolLiquidationManager.sol
Normal file
|
@ -0,0 +1,360 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
|
||||
|
||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||
import "../configuration/LendingPoolParametersProvider.sol";
|
||||
import "../tokenization/AToken.sol";
|
||||
import "../libraries/CoreLibrary.sol";
|
||||
import "../libraries/WadRayMath.sol";
|
||||
import "./LendingPoolCore.sol";
|
||||
import "./LendingPoolDataProvider.sol";
|
||||
import "../interfaces/IPriceOracleGetter.sol";
|
||||
|
||||
/**
|
||||
* @title LendingPoolLiquidationManager contract
|
||||
* @author Aave
|
||||
* @notice Implements the liquidation function.
|
||||
**/
|
||||
contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializable {
|
||||
using SafeMath for uint256;
|
||||
using WadRayMath for uint256;
|
||||
using Address for address;
|
||||
|
||||
LendingPoolAddressesProvider public addressesProvider;
|
||||
LendingPoolCore core;
|
||||
LendingPoolDataProvider dataProvider;
|
||||
LendingPoolParametersProvider parametersProvider;
|
||||
IFeeProvider feeProvider;
|
||||
address ethereumAddress;
|
||||
|
||||
uint256 constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 50;
|
||||
|
||||
/**
|
||||
* @dev emitted when a borrow fee 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 _feeLiquidated the total fee liquidated
|
||||
* @param _liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee
|
||||
* @param _timestamp the timestamp of the action
|
||||
**/
|
||||
event OriginationFeeLiquidated(
|
||||
address indexed _collateral,
|
||||
address indexed _reserve,
|
||||
address indexed _user,
|
||||
uint256 _feeLiquidated,
|
||||
uint256 _liquidatedCollateralForFee,
|
||||
uint256 _timestamp
|
||||
);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @param _timestamp the timestamp of the action
|
||||
**/
|
||||
event LiquidationCall(
|
||||
address indexed _collateral,
|
||||
address indexed _reserve,
|
||||
address indexed _user,
|
||||
uint256 _purchaseAmount,
|
||||
uint256 _liquidatedCollateralAmount,
|
||||
uint256 _accruedBorrowInterest,
|
||||
address _liquidator,
|
||||
bool _receiveAToken,
|
||||
uint256 _timestamp
|
||||
);
|
||||
|
||||
enum LiquidationErrors {
|
||||
NO_ERROR,
|
||||
NO_COLLATERAL_AVAILABLE,
|
||||
COLLATERAL_CANNOT_BE_LIQUIDATED,
|
||||
CURRRENCY_NOT_BORROWED,
|
||||
HEALTH_FACTOR_ABOVE_THRESHOLD,
|
||||
NOT_ENOUGH_LIQUIDITY
|
||||
}
|
||||
|
||||
struct LiquidationCallLocalVars {
|
||||
uint256 userCollateralBalance;
|
||||
uint256 userCompoundedBorrowBalance;
|
||||
uint256 borrowBalanceIncrease;
|
||||
uint256 maxPrincipalAmountToLiquidate;
|
||||
uint256 actualAmountToLiquidate;
|
||||
uint256 liquidationRatio;
|
||||
uint256 maxAmountCollateralToLiquidate;
|
||||
uint256 originationFee;
|
||||
uint256 feeLiquidated;
|
||||
uint256 liquidatedCollateralForFee;
|
||||
CoreLibrary.InterestRateMode borrowRateMode;
|
||||
uint256 userStableRate;
|
||||
bool isCollateralEnabled;
|
||||
bool healthFactorBelowThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev as the contract extends the VersionedInitializable contract to match the state
|
||||
* of the LendingPool contract, the getRevision() function is needed.
|
||||
*/
|
||||
function getRevision() internal override pure returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev users can invoke this function to liquidate an undercollateralized position.
|
||||
* @param _reserve the address of the collateral to liquidated
|
||||
* @param _reserve the address of the principal reserve
|
||||
* @param _user the address of the borrower
|
||||
* @param _purchaseAmount the amount of principal that the liquidator wants to repay
|
||||
* @param _receiveAToken true if the liquidators wants to receive the aTokens, false if
|
||||
* he wants to receive the underlying asset directly
|
||||
**/
|
||||
function liquidationCall(
|
||||
address _collateral,
|
||||
address _reserve,
|
||||
address _user,
|
||||
uint256 _purchaseAmount,
|
||||
bool _receiveAToken
|
||||
) external payable returns (uint256, string memory) {
|
||||
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
|
||||
LiquidationCallLocalVars memory vars;
|
||||
|
||||
(, , , , , , , vars.healthFactorBelowThreshold) = dataProvider.calculateUserGlobalData(
|
||||
_user
|
||||
);
|
||||
|
||||
if (!vars.healthFactorBelowThreshold) {
|
||||
return (
|
||||
uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
|
||||
"Health factor is not below the threshold"
|
||||
);
|
||||
}
|
||||
|
||||
vars.userCollateralBalance = core.getUserUnderlyingAssetBalance(_collateral, _user);
|
||||
|
||||
//if _user hasn't deposited this specific collateral, nothing can be liquidated
|
||||
if (vars.userCollateralBalance == 0) {
|
||||
return (
|
||||
uint256(LiquidationErrors.NO_COLLATERAL_AVAILABLE),
|
||||
"Invalid collateral to liquidate"
|
||||
);
|
||||
}
|
||||
|
||||
vars.isCollateralEnabled =
|
||||
core.isReserveUsageAsCollateralEnabled(_collateral) &&
|
||||
core.isUserUseReserveAsCollateralEnabled(_collateral, _user);
|
||||
|
||||
//if _collateral isn't enabled as collateral by _user, it cannot be liquidated
|
||||
if (!vars.isCollateralEnabled) {
|
||||
return (
|
||||
uint256(LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
|
||||
"The collateral chosen cannot be liquidated"
|
||||
);
|
||||
}
|
||||
|
||||
//if the user hasn't borrowed the specific currency defined by _reserve, it cannot be liquidated
|
||||
(, vars.userCompoundedBorrowBalance, vars.borrowBalanceIncrease) = core
|
||||
.getUserBorrowBalances(_reserve, _user);
|
||||
|
||||
if (vars.userCompoundedBorrowBalance == 0) {
|
||||
return (
|
||||
uint256(LiquidationErrors.CURRRENCY_NOT_BORROWED),
|
||||
"User did not borrow the specified currency"
|
||||
);
|
||||
}
|
||||
|
||||
//all clear - calculate the max principal amount that can be liquidated
|
||||
vars.maxPrincipalAmountToLiquidate = vars
|
||||
.userCompoundedBorrowBalance
|
||||
.mul(LIQUIDATION_CLOSE_FACTOR_PERCENT)
|
||||
.div(100);
|
||||
|
||||
vars.actualAmountToLiquidate = _purchaseAmount > vars.maxPrincipalAmountToLiquidate
|
||||
? vars.maxPrincipalAmountToLiquidate
|
||||
: _purchaseAmount;
|
||||
|
||||
(uint256 maxCollateralToLiquidate, uint256 principalAmountNeeded) = calculateAvailableCollateralToLiquidate(
|
||||
_collateral,
|
||||
_reserve,
|
||||
vars.actualAmountToLiquidate,
|
||||
vars.userCollateralBalance
|
||||
);
|
||||
|
||||
vars.originationFee = core.getUserOriginationFee(_reserve, _user);
|
||||
|
||||
//if there is a fee to liquidate, calculate the maximum amount of fee that can be liquidated
|
||||
if (vars.originationFee > 0) {
|
||||
(
|
||||
vars.liquidatedCollateralForFee,
|
||||
vars.feeLiquidated
|
||||
) = calculateAvailableCollateralToLiquidate(
|
||||
_collateral,
|
||||
_reserve,
|
||||
vars.originationFee,
|
||||
vars.userCollateralBalance.sub(maxCollateralToLiquidate)
|
||||
);
|
||||
}
|
||||
|
||||
//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 (principalAmountNeeded < vars.actualAmountToLiquidate) {
|
||||
vars.actualAmountToLiquidate = principalAmountNeeded;
|
||||
}
|
||||
|
||||
//if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve
|
||||
if (!_receiveAToken) {
|
||||
uint256 currentAvailableCollateral = core.getReserveAvailableLiquidity(_collateral);
|
||||
if (currentAvailableCollateral < maxCollateralToLiquidate) {
|
||||
return (
|
||||
uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY),
|
||||
"There isn't enough liquidity available to liquidate"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
core.updateStateOnLiquidation(
|
||||
_reserve,
|
||||
_collateral,
|
||||
_user,
|
||||
vars.actualAmountToLiquidate,
|
||||
maxCollateralToLiquidate,
|
||||
vars.feeLiquidated,
|
||||
vars.liquidatedCollateralForFee,
|
||||
vars.borrowBalanceIncrease,
|
||||
_receiveAToken
|
||||
);
|
||||
|
||||
AToken collateralAtoken = AToken(core.getReserveATokenAddress(_collateral));
|
||||
|
||||
//if liquidator reclaims the aToken, he receives the equivalent atoken amount
|
||||
if (_receiveAToken) {
|
||||
collateralAtoken.transferOnLiquidation(_user, msg.sender, maxCollateralToLiquidate);
|
||||
} else {
|
||||
//otherwise receives the underlying asset
|
||||
//burn the equivalent amount of atoken
|
||||
collateralAtoken.burnOnLiquidation(_user, maxCollateralToLiquidate);
|
||||
core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate);
|
||||
}
|
||||
|
||||
//transfers the principal currency to the pool
|
||||
core.transferToReserve{value: msg.value}(_reserve, msg.sender, vars.actualAmountToLiquidate);
|
||||
|
||||
if (vars.feeLiquidated > 0) {
|
||||
//if there is enough collateral to liquidate the fee, first transfer burn an equivalent amount of
|
||||
//aTokens of the user
|
||||
collateralAtoken.burnOnLiquidation(_user, vars.liquidatedCollateralForFee);
|
||||
|
||||
//then liquidate the fee by transferring it to the fee collection address
|
||||
core.liquidateFee(
|
||||
_collateral,
|
||||
vars.liquidatedCollateralForFee,
|
||||
addressesProvider.getTokenDistributor()
|
||||
);
|
||||
|
||||
emit OriginationFeeLiquidated(
|
||||
_collateral,
|
||||
_reserve,
|
||||
_user,
|
||||
vars.feeLiquidated,
|
||||
vars.liquidatedCollateralForFee,
|
||||
//solium-disable-next-line
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
}
|
||||
emit LiquidationCall(
|
||||
_collateral,
|
||||
_reserve,
|
||||
_user,
|
||||
vars.actualAmountToLiquidate,
|
||||
maxCollateralToLiquidate,
|
||||
vars.borrowBalanceIncrease,
|
||||
msg.sender,
|
||||
_receiveAToken,
|
||||
//solium-disable-next-line
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
return (uint256(LiquidationErrors.NO_ERROR), "No errors");
|
||||
}
|
||||
|
||||
struct AvailableCollateralToLiquidateLocalVars {
|
||||
uint256 userCompoundedBorrowBalance;
|
||||
uint256 liquidationBonus;
|
||||
uint256 collateralPrice;
|
||||
uint256 principalCurrencyPrice;
|
||||
uint256 maxAmountCollateralToLiquidate;
|
||||
uint256 principalDecimals;
|
||||
uint256 collateralDecimals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* all the checks to validate the liquidation have been performed, otherwise it might fail.
|
||||
* @param _collateral the collateral to be liquidated
|
||||
* @param _principal the principal currency to be liquidated
|
||||
* @param _purchaseAmount the amount of principal being liquidated
|
||||
* @param _userCollateralBalance the collatera balance for the specific _collateral asset of the user being liquidated
|
||||
* @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(
|
||||
address _collateral,
|
||||
address _principal,
|
||||
uint256 _purchaseAmount,
|
||||
uint256 _userCollateralBalance
|
||||
) internal view returns (uint256 collateralAmount, uint256 principalAmountNeeded) {
|
||||
collateralAmount = 0;
|
||||
principalAmountNeeded = 0;
|
||||
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
|
||||
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
|
||||
AvailableCollateralToLiquidateLocalVars memory vars;
|
||||
|
||||
vars.collateralPrice = oracle.getAssetPrice(_collateral);
|
||||
vars.principalCurrencyPrice = oracle.getAssetPrice(_principal);
|
||||
vars.liquidationBonus = core.getReserveLiquidationBonus(_collateral);
|
||||
vars.principalDecimals = core.getReserveDecimals(_principal);
|
||||
vars.collateralDecimals = core.getReserveDecimals(_collateral);
|
||||
|
||||
//this is the maximum possible amount of the selected collateral that can be liquidated, given the
|
||||
//max amount of principal currency that is available for liquidation.
|
||||
vars.maxAmountCollateralToLiquidate = vars
|
||||
.principalCurrencyPrice
|
||||
.mul(_purchaseAmount)
|
||||
.mul(10 ** vars.collateralDecimals)
|
||||
.div(vars.collateralPrice.mul(10 ** vars.principalDecimals))
|
||||
.mul(vars.liquidationBonus)
|
||||
.div(100);
|
||||
|
||||
if (vars.maxAmountCollateralToLiquidate > _userCollateralBalance) {
|
||||
collateralAmount = _userCollateralBalance;
|
||||
principalAmountNeeded = vars
|
||||
.collateralPrice
|
||||
.mul(collateralAmount)
|
||||
.mul(10 ** vars.principalDecimals)
|
||||
.div(vars.principalCurrencyPrice.mul(10 ** vars.collateralDecimals))
|
||||
.mul(100)
|
||||
.div(vars.liquidationBonus);
|
||||
} else {
|
||||
collateralAmount = vars.maxAmountCollateralToLiquidate;
|
||||
principalAmountNeeded = _purchaseAmount;
|
||||
}
|
||||
|
||||
return (collateralAmount, principalAmountNeeded);
|
||||
}
|
||||
}
|
440
contracts/libraries/CoreLibrary.sol
Normal file
440
contracts/libraries/CoreLibrary.sol
Normal file
|
@ -0,0 +1,440 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
import "./WadRayMath.sol";
|
||||
|
||||
/**
|
||||
* @title CoreLibrary library
|
||||
* @author Aave
|
||||
* @notice Defines the data structures of the reserves and the user data
|
||||
**/
|
||||
library CoreLibrary {
|
||||
using SafeMath for uint256;
|
||||
using WadRayMath for uint256;
|
||||
|
||||
enum InterestRateMode {NONE, STABLE, VARIABLE}
|
||||
|
||||
uint256 internal constant SECONDS_PER_YEAR = 365 days;
|
||||
|
||||
struct UserReserveData {
|
||||
//principal amount borrowed by the user.
|
||||
uint256 principalBorrowBalance;
|
||||
//cumulated variable borrow index for the user. Expressed in ray
|
||||
uint256 lastVariableBorrowCumulativeIndex;
|
||||
//origination fee cumulated by the user
|
||||
uint256 originationFee;
|
||||
// stable borrow rate at which the user has borrowed. Expressed in ray
|
||||
uint256 stableBorrowRate;
|
||||
uint40 lastUpdateTimestamp;
|
||||
//defines if a specific deposit should or not be used as a collateral in borrows
|
||||
bool useAsCollateral;
|
||||
}
|
||||
|
||||
struct ReserveData {
|
||||
/**
|
||||
* @dev refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
|
||||
**/
|
||||
//the liquidity index. Expressed in ray
|
||||
uint256 lastLiquidityCumulativeIndex;
|
||||
//the current supply rate. Expressed in ray
|
||||
uint256 currentLiquidityRate;
|
||||
//the total borrows of the reserve at a stable rate. Expressed in the currency decimals
|
||||
uint256 totalBorrowsStable;
|
||||
//the total borrows of the reserve at a variable rate. Expressed in the currency decimals
|
||||
uint256 totalBorrowsVariable;
|
||||
//the current variable borrow rate. Expressed in ray
|
||||
uint256 currentVariableBorrowRate;
|
||||
//the current stable borrow rate. Expressed in ray
|
||||
uint256 currentStableBorrowRate;
|
||||
//the current average stable borrow rate (weighted average of all the different stable rate loans). Expressed in ray
|
||||
uint256 currentAverageStableBorrowRate;
|
||||
//variable borrow index. Expressed in ray
|
||||
uint256 lastVariableBorrowCumulativeIndex;
|
||||
//the ltv of the reserve. Expressed in percentage (0-100)
|
||||
uint256 baseLTVasCollateral;
|
||||
//the liquidation threshold of the reserve. Expressed in percentage (0-100)
|
||||
uint256 liquidationThreshold;
|
||||
//the liquidation bonus of the reserve. Expressed in percentage
|
||||
uint256 liquidationBonus;
|
||||
//the decimals of the reserve asset
|
||||
uint256 decimals;
|
||||
/**
|
||||
* @dev address of the aToken representing the asset
|
||||
**/
|
||||
address aTokenAddress;
|
||||
/**
|
||||
* @dev address of the interest rate strategy contract
|
||||
**/
|
||||
address interestRateStrategyAddress;
|
||||
uint40 lastUpdateTimestamp;
|
||||
// borrowingEnabled = true means users can borrow from this reserve
|
||||
bool borrowingEnabled;
|
||||
// usageAsCollateralEnabled = true means users can use this reserve as collateral
|
||||
bool usageAsCollateralEnabled;
|
||||
// isStableBorrowRateEnabled = true means users can borrow at a stable rate
|
||||
bool isStableBorrowRateEnabled;
|
||||
// isActive = true means the reserve has been activated and properly configured
|
||||
bool isActive;
|
||||
// isFreezed = true means the reserve only allows repays and redeems, but not deposits, new borrowings or rate swap
|
||||
bool isFreezed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the ongoing normalized income for the reserve.
|
||||
* a value of 1e27 means there is no income. As time passes, the income is accrued.
|
||||
* A value of 2*1e27 means that the income of the reserve is double the initial amount.
|
||||
* @param _reserve the reserve object
|
||||
* @return the normalized income. expressed in ray
|
||||
**/
|
||||
function getNormalizedIncome(CoreLibrary.ReserveData storage _reserve)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
uint256 cumulated = calculateLinearInterest(
|
||||
_reserve
|
||||
.currentLiquidityRate,
|
||||
_reserve
|
||||
.lastUpdateTimestamp
|
||||
)
|
||||
.rayMul(_reserve.lastLiquidityCumulativeIndex);
|
||||
|
||||
return cumulated;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
|
||||
* a formal specification.
|
||||
* @param _self the reserve object
|
||||
**/
|
||||
function updateCumulativeIndexes(ReserveData storage _self) internal {
|
||||
uint256 totalBorrows = getTotalBorrows(_self);
|
||||
|
||||
if (totalBorrows > 0) {
|
||||
//only cumulating if there is any income being produced
|
||||
uint256 cumulatedLiquidityInterest = calculateLinearInterest(
|
||||
_self.currentLiquidityRate,
|
||||
_self.lastUpdateTimestamp
|
||||
);
|
||||
|
||||
_self.lastLiquidityCumulativeIndex = cumulatedLiquidityInterest.rayMul(
|
||||
_self.lastLiquidityCumulativeIndex
|
||||
);
|
||||
|
||||
uint256 cumulatedVariableBorrowInterest = calculateCompoundedInterest(
|
||||
_self.currentVariableBorrowRate,
|
||||
_self.lastUpdateTimestamp
|
||||
);
|
||||
_self.lastVariableBorrowCumulativeIndex = cumulatedVariableBorrowInterest.rayMul(
|
||||
_self.lastVariableBorrowCumulativeIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev accumulates a predefined amount of asset to the reserve as a fixed, one time income. Used for example to accumulate
|
||||
* the flashloan fee to the reserve, and spread it through the depositors.
|
||||
* @param _self the reserve object
|
||||
* @param _totalLiquidity the total liquidity available in the reserve
|
||||
* @param _amount the amount to accomulate
|
||||
**/
|
||||
function cumulateToLiquidityIndex(
|
||||
ReserveData storage _self,
|
||||
uint256 _totalLiquidity,
|
||||
uint256 _amount
|
||||
) internal {
|
||||
uint256 amountToLiquidityRatio = _amount.wadToRay().rayDiv(_totalLiquidity.wadToRay());
|
||||
|
||||
uint256 cumulatedLiquidity = amountToLiquidityRatio.add(WadRayMath.ray());
|
||||
|
||||
_self.lastLiquidityCumulativeIndex = cumulatedLiquidity.rayMul(
|
||||
_self.lastLiquidityCumulativeIndex
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev initializes a reserve
|
||||
* @param _self the reserve object
|
||||
* @param _aTokenAddress the address of the overlying atoken contract
|
||||
* @param _decimals the number of decimals of the underlying asset
|
||||
* @param _interestRateStrategyAddress the address of the interest rate strategy contract
|
||||
**/
|
||||
function init(
|
||||
ReserveData storage _self,
|
||||
address _aTokenAddress,
|
||||
uint256 _decimals,
|
||||
address _interestRateStrategyAddress
|
||||
) external {
|
||||
require(_self.aTokenAddress == address(0), "Reserve has already been initialized");
|
||||
|
||||
if (_self.lastLiquidityCumulativeIndex == 0) {
|
||||
//if the reserve has not been initialized yet
|
||||
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
|
||||
}
|
||||
|
||||
if (_self.lastVariableBorrowCumulativeIndex == 0) {
|
||||
_self.lastVariableBorrowCumulativeIndex = WadRayMath.ray();
|
||||
}
|
||||
|
||||
_self.aTokenAddress = _aTokenAddress;
|
||||
_self.decimals = _decimals;
|
||||
|
||||
_self.interestRateStrategyAddress = _interestRateStrategyAddress;
|
||||
_self.isActive = true;
|
||||
_self.isFreezed = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev enables borrowing on a reserve
|
||||
* @param _self the reserve object
|
||||
* @param _stableBorrowRateEnabled true if the stable borrow rate must be enabled by default, false otherwise
|
||||
**/
|
||||
function enableBorrowing(ReserveData storage _self, bool _stableBorrowRateEnabled) external {
|
||||
require(_self.borrowingEnabled == false, "Reserve is already enabled");
|
||||
|
||||
_self.borrowingEnabled = true;
|
||||
_self.isStableBorrowRateEnabled = _stableBorrowRateEnabled;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev disables borrowing on a reserve
|
||||
* @param _self the reserve object
|
||||
**/
|
||||
function disableBorrowing(ReserveData storage _self) external {
|
||||
_self.borrowingEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev enables a reserve to be used as collateral
|
||||
* @param _self the reserve object
|
||||
* @param _baseLTVasCollateral the loan to value of the asset when used as collateral
|
||||
* @param _liquidationThreshold the threshold at which loans using this asset as collateral will be considered undercollateralized
|
||||
* @param _liquidationBonus the bonus liquidators receive to liquidate this asset
|
||||
**/
|
||||
function enableAsCollateral(
|
||||
ReserveData storage _self,
|
||||
uint256 _baseLTVasCollateral,
|
||||
uint256 _liquidationThreshold,
|
||||
uint256 _liquidationBonus
|
||||
) external {
|
||||
require(
|
||||
_self.usageAsCollateralEnabled == false,
|
||||
"Reserve is already enabled as collateral"
|
||||
);
|
||||
|
||||
_self.usageAsCollateralEnabled = true;
|
||||
_self.baseLTVasCollateral = _baseLTVasCollateral;
|
||||
_self.liquidationThreshold = _liquidationThreshold;
|
||||
_self.liquidationBonus = _liquidationBonus;
|
||||
|
||||
if (_self.lastLiquidityCumulativeIndex == 0)
|
||||
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev disables a reserve as collateral
|
||||
* @param _self the reserve object
|
||||
**/
|
||||
function disableAsCollateral(ReserveData storage _self) external {
|
||||
_self.usageAsCollateralEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @dev calculates the compounded borrow balance of a user
|
||||
* @param _self the userReserve object
|
||||
* @param _reserve the reserve object
|
||||
* @return the user compounded borrow balance
|
||||
**/
|
||||
function getCompoundedBorrowBalance(
|
||||
CoreLibrary.UserReserveData storage _self,
|
||||
CoreLibrary.ReserveData storage _reserve
|
||||
) internal view returns (uint256) {
|
||||
if (_self.principalBorrowBalance == 0) return 0;
|
||||
|
||||
uint256 principalBorrowBalanceRay = _self.principalBorrowBalance.wadToRay();
|
||||
uint256 compoundedBalance = 0;
|
||||
uint256 cumulatedInterest = 0;
|
||||
|
||||
if (_self.stableBorrowRate > 0) {
|
||||
cumulatedInterest = calculateCompoundedInterest(
|
||||
_self.stableBorrowRate,
|
||||
_self.lastUpdateTimestamp
|
||||
);
|
||||
} else {
|
||||
//variable interest
|
||||
cumulatedInterest = calculateCompoundedInterest(
|
||||
_reserve
|
||||
.currentVariableBorrowRate,
|
||||
_reserve
|
||||
.lastUpdateTimestamp
|
||||
)
|
||||
.rayMul(_reserve.lastVariableBorrowCumulativeIndex)
|
||||
.rayDiv(_self.lastVariableBorrowCumulativeIndex);
|
||||
}
|
||||
|
||||
compoundedBalance = principalBorrowBalanceRay.rayMul(cumulatedInterest).rayToWad();
|
||||
|
||||
if (compoundedBalance == _self.principalBorrowBalance) {
|
||||
//solium-disable-next-line
|
||||
if (_self.lastUpdateTimestamp != block.timestamp) {
|
||||
//no interest cumulation because of the rounding - we add 1 wei
|
||||
//as symbolic cumulated interest to avoid interest free loans.
|
||||
|
||||
return _self.principalBorrowBalance.add(1 wei);
|
||||
}
|
||||
}
|
||||
|
||||
return compoundedBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev increases the total borrows at a stable rate on a specific reserve and updates the
|
||||
* average stable rate consequently
|
||||
* @param _reserve the reserve object
|
||||
* @param _amount the amount to add to the total borrows stable
|
||||
* @param _rate the rate at which the amount has been borrowed
|
||||
**/
|
||||
function increaseTotalBorrowsStableAndUpdateAverageRate(
|
||||
ReserveData storage _reserve,
|
||||
uint256 _amount,
|
||||
uint256 _rate
|
||||
) internal {
|
||||
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
|
||||
//updating reserve borrows stable
|
||||
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.add(_amount);
|
||||
|
||||
//update the average stable rate
|
||||
//weighted average of all the borrows
|
||||
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
|
||||
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
|
||||
_reserve.currentAverageStableBorrowRate
|
||||
);
|
||||
|
||||
_reserve.currentAverageStableBorrowRate = weightedLastBorrow
|
||||
.add(weightedPreviousTotalBorrows)
|
||||
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev decreases the total borrows at a stable rate on a specific reserve and updates the
|
||||
* average stable rate consequently
|
||||
* @param _reserve the reserve object
|
||||
* @param _amount the amount to substract to the total borrows stable
|
||||
* @param _rate the rate at which the amount has been repaid
|
||||
**/
|
||||
function decreaseTotalBorrowsStableAndUpdateAverageRate(
|
||||
ReserveData storage _reserve,
|
||||
uint256 _amount,
|
||||
uint256 _rate
|
||||
) internal {
|
||||
require(_reserve.totalBorrowsStable >= _amount, "Invalid amount to decrease");
|
||||
|
||||
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
|
||||
|
||||
//updating reserve borrows stable
|
||||
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.sub(_amount);
|
||||
|
||||
if (_reserve.totalBorrowsStable == 0) {
|
||||
_reserve.currentAverageStableBorrowRate = 0; //no income if there are no stable rate borrows
|
||||
return;
|
||||
}
|
||||
|
||||
//update the average stable rate
|
||||
//weighted average of all the borrows
|
||||
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
|
||||
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
|
||||
_reserve.currentAverageStableBorrowRate
|
||||
);
|
||||
|
||||
require(
|
||||
weightedPreviousTotalBorrows >= weightedLastBorrow,
|
||||
"The amounts to subtract don't match"
|
||||
);
|
||||
|
||||
_reserve.currentAverageStableBorrowRate = weightedPreviousTotalBorrows
|
||||
.sub(weightedLastBorrow)
|
||||
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev increases the total borrows at a variable rate
|
||||
* @param _reserve the reserve object
|
||||
* @param _amount the amount to add to the total borrows variable
|
||||
**/
|
||||
function increaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
|
||||
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.add(_amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev decreases the total borrows at a variable rate
|
||||
* @param _reserve the reserve object
|
||||
* @param _amount the amount to substract to the total borrows variable
|
||||
**/
|
||||
function decreaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
|
||||
require(
|
||||
_reserve.totalBorrowsVariable >= _amount,
|
||||
"The amount that is being subtracted from the variable total borrows is incorrect"
|
||||
);
|
||||
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.sub(_amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev function to calculate the interest using a linear interest rate formula
|
||||
* @param _rate the interest rate, in ray
|
||||
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
|
||||
* @return the interest rate linearly accumulated during the timeDelta, in ray
|
||||
**/
|
||||
|
||||
function calculateLinearInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
//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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev function to calculate the interest using a compounded interest rate formula
|
||||
* @param _rate the interest rate, in ray
|
||||
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
|
||||
* @return the interest rate compounded during the timeDelta, in ray
|
||||
**/
|
||||
function calculateCompoundedInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
//solium-disable-next-line
|
||||
uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp));
|
||||
|
||||
uint256 ratePerSecond = _rate.div(SECONDS_PER_YEAR);
|
||||
|
||||
return ratePerSecond.add(WadRayMath.ray()).rayPow(timeDifference);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the total borrows on the reserve
|
||||
* @param _reserve the reserve object
|
||||
* @return the total borrows (stable + variable)
|
||||
**/
|
||||
function getTotalBorrows(CoreLibrary.ReserveData storage _reserve)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _reserve.totalBorrowsStable.add(_reserve.totalBorrowsVariable);
|
||||
}
|
||||
|
||||
}
|
13
contracts/libraries/EthAddressLib.sol
Normal file
13
contracts/libraries/EthAddressLib.sol
Normal file
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
library EthAddressLib {
|
||||
|
||||
/**
|
||||
* @dev returns the address used within the protocol to identify ETH
|
||||
* @return the address assigned to ETH
|
||||
*/
|
||||
function ethAddress() internal pure returns(address) {
|
||||
return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
}
|
||||
}
|
20
contracts/libraries/UintConstants.sol
Normal file
20
contracts/libraries/UintConstants.sol
Normal file
|
@ -0,0 +1,20 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
library UintConstants {
|
||||
/**
|
||||
* @dev returns max uint256
|
||||
* @return max uint256
|
||||
*/
|
||||
function maxUint() internal pure returns(uint256) {
|
||||
return uint256(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns max uint256-1
|
||||
* @return max uint256-1
|
||||
*/
|
||||
function maxUintMinus1() internal pure returns(uint256) {
|
||||
return uint256(-1) - 1;
|
||||
}
|
||||
}
|
134
contracts/libraries/WadRayMath.sol
Normal file
134
contracts/libraries/WadRayMath.sol
Normal file
|
@ -0,0 +1,134 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
|
||||
/**
|
||||
* @title WadRayMath library
|
||||
* @author Aave
|
||||
* @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits)
|
||||
**/
|
||||
|
||||
library WadRayMath {
|
||||
using SafeMath for uint256;
|
||||
|
||||
uint256 internal constant WAD = 1e18;
|
||||
uint256 internal constant halfWAD = WAD / 2;
|
||||
|
||||
uint256 internal constant RAY = 1e27;
|
||||
uint256 internal constant halfRAY = RAY / 2;
|
||||
|
||||
uint256 internal constant WAD_RAY_RATIO = 1e9;
|
||||
|
||||
/**
|
||||
* @return one ray, 1e27
|
||||
**/
|
||||
function ray() internal pure returns (uint256) {
|
||||
return RAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return one wad, 1e18
|
||||
**/
|
||||
|
||||
function wad() internal pure returns (uint256) {
|
||||
return WAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return half ray, 1e27/2
|
||||
**/
|
||||
function halfRay() internal pure returns (uint256) {
|
||||
return halfRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return half ray, 1e18/2
|
||||
**/
|
||||
function halfWad() internal pure returns (uint256) {
|
||||
return halfWAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev multiplies two wad, rounding half up to the nearest wad
|
||||
* @param a wad
|
||||
* @param b wad
|
||||
* @return the result of a*b, in wad
|
||||
**/
|
||||
function wadMul(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return halfWAD.add(a.mul(b)).div(WAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev divides two wad, rounding half up to the nearest wad
|
||||
* @param a wad
|
||||
* @param b wad
|
||||
* @return the result of a/b, in wad
|
||||
**/
|
||||
function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
uint256 halfB = b / 2;
|
||||
|
||||
return halfB.add(a.mul(WAD)).div(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev multiplies two ray, rounding half up to the nearest ray
|
||||
* @param a ray
|
||||
* @param b ray
|
||||
* @return the result of a*b, in ray
|
||||
**/
|
||||
function rayMul(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return halfRAY.add(a.mul(b)).div(RAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev divides two ray, rounding half up to the nearest ray
|
||||
* @param a ray
|
||||
* @param b ray
|
||||
* @return the result of a/b, in ray
|
||||
**/
|
||||
function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
uint256 halfB = b / 2;
|
||||
|
||||
return halfB.add(a.mul(RAY)).div(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev casts ray down to wad
|
||||
* @param a ray
|
||||
* @return a casted to wad, rounded half up to the nearest wad
|
||||
**/
|
||||
function rayToWad(uint256 a) internal pure returns (uint256) {
|
||||
uint256 halfRatio = WAD_RAY_RATIO / 2;
|
||||
|
||||
return halfRatio.add(a).div(WAD_RAY_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev convert wad up to ray
|
||||
* @param a wad
|
||||
* @return a converted in ray
|
||||
**/
|
||||
function wadToRay(uint256 a) internal pure returns (uint256) {
|
||||
return a.mul(WAD_RAY_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates base^exp. The code uses the ModExp precompile
|
||||
* @return z base^exp, in ray
|
||||
*/
|
||||
//solium-disable-next-line
|
||||
function rayPow(uint256 x, uint256 n) internal pure returns (uint256 z) {
|
||||
|
||||
z = n % 2 != 0 ? x : RAY;
|
||||
|
||||
for (n /= 2; n != 0; n /= 2) {
|
||||
x = rayMul(x, x);
|
||||
|
||||
if (n % 2 != 0) {
|
||||
z = rayMul(z, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./BaseAdminUpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title AdminUpgradeabilityProxy
|
||||
* @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for
|
||||
* initializing the implementation, admin, and init data.
|
||||
*/
|
||||
contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
|
||||
/**
|
||||
* Contract constructor.
|
||||
* @param _logic address of the initial implementation.
|
||||
* @param _admin Address of the proxy administrator.
|
||||
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
|
||||
* 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.
|
||||
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
|
||||
*/
|
||||
constructor(address _logic, address _admin, bytes memory _data) public payable UpgradeabilityProxy(_logic, _data) {
|
||||
assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
|
||||
_setAdmin(_admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Only fall back when the sender is not the admin.
|
||||
*/
|
||||
function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
|
||||
BaseAdminUpgradeabilityProxy._willFallback();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./UpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title BaseAdminUpgradeabilityProxy
|
||||
* @dev This contract combines an upgradeability proxy with an authorization
|
||||
* mechanism for administrative tasks.
|
||||
* 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 BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
|
||||
/**
|
||||
* @dev Emitted when the administration has been transferred.
|
||||
* @param previousAdmin Address of the previous admin.
|
||||
* @param newAdmin Address of the new admin.
|
||||
*/
|
||||
event AdminChanged(address previousAdmin, address newAdmin);
|
||||
|
||||
/**
|
||||
* @dev Storage slot with the admin of the contract.
|
||||
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
|
||||
* validated in the constructor.
|
||||
*/
|
||||
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
|
||||
|
||||
/**
|
||||
* @dev Modifier to check whether the `msg.sender` is the admin.
|
||||
* If it is, it will run the function. Otherwise, it will delegate the call
|
||||
* to the implementation.
|
||||
*/
|
||||
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 Changes the admin of the proxy.
|
||||
* Only the current admin can call this function.
|
||||
* @param newAdmin Address to transfer proxy administration to.
|
||||
*/
|
||||
function changeAdmin(address newAdmin) external ifAdmin {
|
||||
require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
|
||||
emit AdminChanged(_admin(), newAdmin);
|
||||
_setAdmin(newAdmin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return adm The admin slot.
|
||||
*/
|
||||
function _admin() internal view returns (address adm) {
|
||||
bytes32 slot = ADMIN_SLOT;
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
adm := sload(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the address of the proxy admin.
|
||||
* @param newAdmin Address of the new proxy admin.
|
||||
*/
|
||||
function _setAdmin(address newAdmin) internal {
|
||||
bytes32 slot = ADMIN_SLOT;
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
sstore(slot, newAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Only fall back when the sender is not the admin.
|
||||
*/
|
||||
function _willFallback() internal override virtual {
|
||||
require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
|
||||
super._willFallback();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./Proxy.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
|
||||
/**
|
||||
* @title BaseUpgradeabilityProxy
|
||||
* @dev This contract implements a proxy that allows to change the
|
||||
* implementation address to which it will delegate.
|
||||
* Such a change is called an implementation upgrade.
|
||||
*/
|
||||
contract BaseUpgradeabilityProxy is Proxy {
|
||||
/**
|
||||
* @dev Emitted when the implementation is upgraded.
|
||||
* @param implementation Address of the new implementation.
|
||||
*/
|
||||
event Upgraded(address indexed implementation);
|
||||
|
||||
/**
|
||||
* @dev Storage slot with the address of the current implementation.
|
||||
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
|
||||
* validated in the constructor.
|
||||
*/
|
||||
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
|
||||
/**
|
||||
* @dev Returns the current implementation.
|
||||
* @return impl Address of the current implementation
|
||||
*/
|
||||
function _implementation() internal override view returns (address impl) {
|
||||
bytes32 slot = IMPLEMENTATION_SLOT;
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
impl := sload(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Upgrades the proxy to a new implementation.
|
||||
* @param newImplementation Address of the new implementation.
|
||||
*/
|
||||
function _upgradeTo(address newImplementation) internal {
|
||||
_setImplementation(newImplementation);
|
||||
emit Upgraded(newImplementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the implementation address of the proxy.
|
||||
* @param newImplementation Address of the new implementation.
|
||||
*/
|
||||
function _setImplementation(address newImplementation) internal {
|
||||
require(
|
||||
Address.isContract(newImplementation),
|
||||
"Cannot set a proxy implementation to a non-contract address"
|
||||
);
|
||||
|
||||
bytes32 slot = IMPLEMENTATION_SLOT;
|
||||
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
sstore(slot, newImplementation)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity >=0.4.24 <0.7.0;
|
||||
|
||||
/**
|
||||
* @title Initializable
|
||||
*
|
||||
* @dev Helper contract to support initializer functions. To use it, replace
|
||||
* the constructor with a function that has the `initializer` modifier.
|
||||
* WARNING: Unlike constructors, initializer functions must be manually
|
||||
* invoked. This applies both to deploying an Initializable contract, as well
|
||||
* as extending an Initializable contract via inheritance.
|
||||
* WARNING: When used with inheritance, manual care must be taken to not invoke
|
||||
* a parent initializer twice, or ensure that all initializers are idempotent,
|
||||
* because this is not dealt with automatically as with constructors.
|
||||
*/
|
||||
contract Initializable {
|
||||
/**
|
||||
* @dev Indicates that the contract has been initialized.
|
||||
*/
|
||||
bool private initialized;
|
||||
|
||||
/**
|
||||
* @dev Indicates that the contract is in the process of being initialized.
|
||||
*/
|
||||
bool private initializing;
|
||||
|
||||
/**
|
||||
* @dev Modifier to use in the initializer function of a contract.
|
||||
*/
|
||||
modifier initializer() {
|
||||
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
|
||||
|
||||
bool isTopLevelCall = !initializing;
|
||||
if (isTopLevelCall) {
|
||||
initializing = true;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
_;
|
||||
|
||||
if (isTopLevelCall) {
|
||||
initializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Returns true if and only if the function is running in the constructor
|
||||
function isConstructor() private view returns (bool) {
|
||||
// extcodesize checks the size of the code stored in an address, and
|
||||
// address returns the current address. Since the code is still not
|
||||
// deployed when running a constructor, any checks on its code size will
|
||||
// yield zero, making it an effective way to detect if a contract is
|
||||
// under construction or not.
|
||||
uint256 cs;
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
cs := extcodesize(address())
|
||||
}
|
||||
return cs == 0;
|
||||
}
|
||||
|
||||
// Reserved storage space to allow for layout changes in the future.
|
||||
uint256[50] private ______gap;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./BaseAdminUpgradeabilityProxy.sol";
|
||||
import "./InitializableUpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title InitializableAdminUpgradeabilityProxy
|
||||
* @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for
|
||||
* initializing the implementation, admin, and init data.
|
||||
*/
|
||||
contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {
|
||||
/**
|
||||
* Contract initializer.
|
||||
* @param _logic address of the initial implementation.
|
||||
* @param _admin Address of the proxy administrator.
|
||||
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
|
||||
* 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.
|
||||
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
|
||||
*/
|
||||
function initialize(address _logic, address _admin, bytes memory _data) public payable {
|
||||
require(_implementation() == address(0));
|
||||
InitializableUpgradeabilityProxy.initialize(_logic, _data);
|
||||
assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
|
||||
_setAdmin(_admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Only fall back when the sender is not the admin.
|
||||
*/
|
||||
function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
|
||||
BaseAdminUpgradeabilityProxy._willFallback();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./BaseUpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title InitializableUpgradeabilityProxy
|
||||
* @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
|
||||
* implementation and init data.
|
||||
*/
|
||||
contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
|
||||
/**
|
||||
* @dev Contract initializer.
|
||||
* @param _logic Address of the initial implementation.
|
||||
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
|
||||
* 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.
|
||||
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
|
||||
*/
|
||||
function initialize(address _logic, bytes memory _data) public payable {
|
||||
require(_implementation() == address(0));
|
||||
assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
|
||||
_setImplementation(_logic);
|
||||
if (_data.length > 0) {
|
||||
(bool success, ) = _logic.delegatecall(_data);
|
||||
require(success);
|
||||
}
|
||||
}
|
||||
}
|
72
contracts/libraries/openzeppelin-upgradeability/Proxy.sol
Normal file
72
contracts/libraries/openzeppelin-upgradeability/Proxy.sol
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
/**
|
||||
* @title Proxy
|
||||
* @dev Implements delegation of calls to other contracts, with proper
|
||||
* forwarding of return values and bubbling of failures.
|
||||
* It defines a fallback function that delegates all calls to the address
|
||||
* returned by the abstract _implementation() internal function.
|
||||
*/
|
||||
abstract contract Proxy {
|
||||
/**
|
||||
* @dev Fallback function.
|
||||
* Implemented entirely in `_fallback`.
|
||||
*/
|
||||
receive() external payable {
|
||||
_fallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Address of the implementation.
|
||||
*/
|
||||
function _implementation() internal virtual view returns (address);
|
||||
|
||||
/**
|
||||
* @dev Delegates execution to an implementation contract.
|
||||
* This is a low level function that doesn't return to its internal call site.
|
||||
* It will return to the external caller whatever the implementation returns.
|
||||
* @param implementation Address to delegate.
|
||||
*/
|
||||
function _delegate(address implementation) internal {
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
// Copy msg.data. We take full control of memory in this inline assembly
|
||||
// block because it will not return to Solidity code. We overwrite the
|
||||
// Solidity scratch pad at memory position 0.
|
||||
calldatacopy(0, 0, calldatasize())
|
||||
|
||||
// Call the implementation.
|
||||
// out and outsize are 0 because we don't know the size yet.
|
||||
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
|
||||
|
||||
// Copy the returned data.
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
|
||||
switch result
|
||||
// delegatecall returns 0 on error.
|
||||
case 0 {
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
default {
|
||||
return(0, returndatasize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function that is run as the first thing in the fallback function.
|
||||
* Can be redefined in derived contracts to add functionality.
|
||||
* Redefinitions must call super._willFallback().
|
||||
*/
|
||||
function _willFallback() internal virtual {}
|
||||
|
||||
/**
|
||||
* @dev fallback implementation.
|
||||
* Extracted to enable manual triggering.
|
||||
*/
|
||||
function _fallback() internal {
|
||||
_willFallback();
|
||||
_delegate(_implementation());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./BaseUpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title UpgradeabilityProxy
|
||||
* @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
|
||||
* implementation and init data.
|
||||
*/
|
||||
contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
|
||||
/**
|
||||
* @dev Contract constructor.
|
||||
* @param _logic Address of the initial implementation.
|
||||
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
|
||||
* 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.
|
||||
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
|
||||
*/
|
||||
constructor(address _logic, bytes memory _data) public payable {
|
||||
assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
|
||||
_setImplementation(_logic);
|
||||
if (_data.length > 0) {
|
||||
(bool success, ) = _logic.delegatecall(_data);
|
||||
require(success);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.8;
|
||||
|
||||
/**
|
||||
* @title VersionedInitializable
|
||||
*
|
||||
* @dev Helper contract to support initializer functions. To use it, replace
|
||||
* the constructor with a function that has the `initializer` modifier.
|
||||
* WARNING: Unlike constructors, initializer functions must be manually
|
||||
* invoked. This applies both to deploying an Initializable contract, as well
|
||||
* as extending an Initializable contract via inheritance.
|
||||
* WARNING: When used with inheritance, manual care must be taken to not invoke
|
||||
* a parent initializer twice, or ensure that all initializers are idempotent,
|
||||
* because this is not dealt with automatically as with constructors.
|
||||
*
|
||||
* @author Aave, inspired by the OpenZeppelin Initializable contract
|
||||
*/
|
||||
abstract contract VersionedInitializable {
|
||||
/**
|
||||
* @dev Indicates that the contract has been initialized.
|
||||
*/
|
||||
uint256 private lastInitializedRevision = 0;
|
||||
|
||||
/**
|
||||
* @dev Indicates that the contract is in the process of being initialized.
|
||||
*/
|
||||
bool private initializing;
|
||||
|
||||
/**
|
||||
* @dev Modifier to use in the initializer function of a contract.
|
||||
*/
|
||||
modifier initializer() {
|
||||
uint256 revision = getRevision();
|
||||
require(initializing || isConstructor() || revision > lastInitializedRevision, "Contract instance has already been initialized");
|
||||
|
||||
bool isTopLevelCall = !initializing;
|
||||
if (isTopLevelCall) {
|
||||
initializing = true;
|
||||
lastInitializedRevision = revision;
|
||||
}
|
||||
|
||||
_;
|
||||
|
||||
if (isTopLevelCall) {
|
||||
initializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev returns the revision number of the contract.
|
||||
/// Needs to be defined in the inherited class as a constant.
|
||||
function getRevision() internal virtual pure returns(uint256);
|
||||
|
||||
|
||||
/// @dev Returns true if and only if the function is running in the constructor
|
||||
function isConstructor() private view returns (bool) {
|
||||
// extcodesize checks the size of the code stored in an address, and
|
||||
// address returns the current address. Since the code is still not
|
||||
// deployed when running a constructor, any checks on its code size will
|
||||
// yield zero, making it an effective way to detect if a contract is
|
||||
// under construction or not.
|
||||
uint256 cs;
|
||||
//solium-disable-next-line
|
||||
assembly {
|
||||
cs := extcodesize(address())
|
||||
}
|
||||
return cs == 0;
|
||||
}
|
||||
|
||||
// Reserved storage space to allow for layout changes in the future.
|
||||
uint256[50] private ______gap;
|
||||
}
|
110
contracts/misc/ChainlinkProxyPriceProvider.sol
Normal file
110
contracts/misc/ChainlinkProxyPriceProvider.sol
Normal file
|
@ -0,0 +1,110 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
import "../interfaces/IPriceOracleGetter.sol";
|
||||
import "../interfaces/IChainlinkAggregator.sol";
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
|
||||
/// @title ChainlinkProxyPriceProvider
|
||||
/// @author Aave
|
||||
/// @notice Proxy smart contract to get the price of an asset from a price source, with Chainlink Aggregator
|
||||
/// smart contracts as primary option
|
||||
/// - If the returned price by a Chainlink aggregator is <= 0, the call is forwarded to a fallbackOracle
|
||||
/// - Owned by the Aave governance system, allowed to add sources for assets, replace them
|
||||
/// and change the fallbackOracle
|
||||
contract ChainlinkProxyPriceProvider is IPriceOracleGetter, Ownable {
|
||||
|
||||
event AssetSourceUpdated(address indexed asset, address indexed source);
|
||||
event FallbackOracleUpdated(address indexed fallbackOracle);
|
||||
|
||||
mapping(address => IChainlinkAggregator) private assetsSources;
|
||||
IPriceOracleGetter private fallbackOracle;
|
||||
|
||||
/// @notice Constructor
|
||||
/// @param _assets The addresses of the assets
|
||||
/// @param _sources The address of the source of each asset
|
||||
/// @param _fallbackOracle The address of the fallback oracle to use if the data of an
|
||||
/// aggregator is not consistent
|
||||
constructor(address[] memory _assets, address[] memory _sources, address _fallbackOracle) public {
|
||||
internalSetFallbackOracle(_fallbackOracle);
|
||||
internalSetAssetsSources(_assets, _sources);
|
||||
}
|
||||
|
||||
/// @notice External function called by the Aave governance to set or replace sources of assets
|
||||
/// @param _assets The addresses of the assets
|
||||
/// @param _sources The address of the source of each asset
|
||||
function setAssetSources(address[] calldata _assets, address[] calldata _sources) external onlyOwner {
|
||||
internalSetAssetsSources(_assets, _sources);
|
||||
}
|
||||
|
||||
/// @notice Sets the fallbackOracle
|
||||
/// - Callable only by the Aave governance
|
||||
/// @param _fallbackOracle The address of the fallbackOracle
|
||||
function setFallbackOracle(address _fallbackOracle) external onlyOwner {
|
||||
internalSetFallbackOracle(_fallbackOracle);
|
||||
}
|
||||
|
||||
/// @notice Internal function to set the sources for each asset
|
||||
/// @param _assets The addresses of the assets
|
||||
/// @param _sources The address of the source of each asset
|
||||
function internalSetAssetsSources(address[] memory _assets, address[] memory _sources) internal {
|
||||
require(_assets.length == _sources.length, "INCONSISTENT_PARAMS_LENGTH");
|
||||
for (uint256 i = 0; i < _assets.length; i++) {
|
||||
assetsSources[_assets[i]] = IChainlinkAggregator(_sources[i]);
|
||||
emit AssetSourceUpdated(_assets[i], _sources[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Internal function to set the fallbackOracle
|
||||
/// @param _fallbackOracle The address of the fallbackOracle
|
||||
function internalSetFallbackOracle(address _fallbackOracle) internal {
|
||||
fallbackOracle = IPriceOracleGetter(_fallbackOracle);
|
||||
emit FallbackOracleUpdated(_fallbackOracle);
|
||||
}
|
||||
|
||||
/// @notice Gets an asset price by address
|
||||
/// @param _asset The asset address
|
||||
function getAssetPrice(address _asset) public override view returns(uint256) {
|
||||
IChainlinkAggregator source = assetsSources[_asset];
|
||||
if (_asset == EthAddressLib.ethAddress()) {
|
||||
return 1 ether;
|
||||
} else {
|
||||
// If there is no registered source for the asset, call the fallbackOracle
|
||||
if (address(source) == address(0)) {
|
||||
return IPriceOracleGetter(fallbackOracle).getAssetPrice(_asset);
|
||||
} else {
|
||||
int256 _price = IChainlinkAggregator(source).latestAnswer();
|
||||
if (_price > 0) {
|
||||
return uint256(_price);
|
||||
} else {
|
||||
return IPriceOracleGetter(fallbackOracle).getAssetPrice(_asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Gets a list of prices from a list of assets addresses
|
||||
/// @param _assets The list of assets addresses
|
||||
function getAssetsPrices(address[] calldata _assets) external view returns(uint256[] memory) {
|
||||
uint256[] memory prices = new uint256[](_assets.length);
|
||||
for (uint256 i = 0; i < _assets.length; i++) {
|
||||
prices[i] = getAssetPrice(_assets[i]);
|
||||
}
|
||||
return prices;
|
||||
}
|
||||
|
||||
/// @notice Gets the address of the source for an asset address
|
||||
/// @param _asset The address of the asset
|
||||
/// @return address The address of the source
|
||||
function getSourceOfAsset(address _asset) external view returns(address) {
|
||||
return address(assetsSources[_asset]);
|
||||
}
|
||||
|
||||
/// @notice Gets the address of the fallback oracle
|
||||
/// @return address The addres of the fallback oracle
|
||||
function getFallbackOracle() external view returns(address) {
|
||||
return address(fallbackOracle);
|
||||
}
|
||||
}
|
8
contracts/misc/IERC20DetailedBytes.sol
Normal file
8
contracts/misc/IERC20DetailedBytes.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
contract IERC20DetailedBytes {
|
||||
bytes32 public name;
|
||||
bytes32 public symbol;
|
||||
uint256 public decimals;
|
||||
}
|
103
contracts/misc/WalletBalanceProvider.sol
Normal file
103
contracts/misc/WalletBalanceProvider.sol
Normal file
|
@ -0,0 +1,103 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
import "../configuration/LendingPoolAddressesProvider.sol";
|
||||
import "../lendingpool/LendingPoolCore.sol";
|
||||
import "../libraries/EthAddressLib.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title WalletBalanceProvider contract
|
||||
* @author Aave, influenced by https://github.com/wbobeirne/eth-balance-checker/blob/master/contracts/BalanceChecker.sol
|
||||
* @notice Implements a logic of getting multiple tokens balance for one user address
|
||||
* @dev NOTE: THIS CONTRACT IS NOT USED WITHIN THE AAVE PROTOCOL. It's an accessory contract used to reduce the number of calls
|
||||
* towards the blockchain from the Aave backend.
|
||||
**/
|
||||
contract WalletBalanceProvider {
|
||||
|
||||
using Address for address;
|
||||
|
||||
LendingPoolAddressesProvider provider;
|
||||
|
||||
constructor(LendingPoolAddressesProvider _provider) public {
|
||||
|
||||
provider = _provider;
|
||||
|
||||
}
|
||||
/**
|
||||
@dev Fallback function, don't accept any ETH
|
||||
**/
|
||||
receive() external payable {
|
||||
revert("WalletBalanceProvider does not accept payments");
|
||||
}
|
||||
|
||||
/**
|
||||
@dev Check the token balance of a wallet in a token contract
|
||||
|
||||
Returns the balance of the token for user. Avoids possible errors:
|
||||
- return 0 on non-contract address
|
||||
**/
|
||||
function balanceOf(address _user, address _token) public view returns (uint256) {
|
||||
// check if token is actually a contract
|
||||
if (_token.isContract()) {
|
||||
return IERC20(_token).balanceOf(_user);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Fetches, for a list of _users and _tokens (ETH included with mock address), the balances
|
||||
/// @param _users The list of users
|
||||
/// @param _tokens The list of tokens
|
||||
/// @return And array with the concatenation of, for each user, his/her balances
|
||||
function batchBalanceOf(address[] memory _users, address[] memory _tokens) public view returns (uint256[] memory) {
|
||||
uint256[] memory balances = new uint256[](_users.length * _tokens.length);
|
||||
|
||||
for (uint256 i = 0; i < _users.length; i++) {
|
||||
for (uint256 j = 0; j < _tokens.length; j++) {
|
||||
uint256 _offset = i * _tokens.length;
|
||||
if (_tokens[j] == EthAddressLib.ethAddress()) {
|
||||
balances[_offset + j] = _users[i].balance; // ETH balance
|
||||
} else {
|
||||
if (!_tokens[j].isContract()) {
|
||||
revert("INVALID_TOKEN");
|
||||
} else {
|
||||
balances[_offset + j] = balanceOf(_users[i], _tokens[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return balances;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@dev provides balances of user wallet for all reserves available on the pool
|
||||
*/
|
||||
function getUserWalletBalances(address _user) public view returns (address[] memory, uint256[] memory) {
|
||||
|
||||
LendingPoolCore core = LendingPoolCore(provider.getLendingPoolCore());
|
||||
|
||||
address[] memory reserves = core.getReserves();
|
||||
|
||||
uint256[] memory balances = new uint256[](reserves.length);
|
||||
|
||||
for (uint256 j = 0; j < reserves.length; j++) {
|
||||
if(!core.getReserveIsActive(reserves[j])){
|
||||
balances[j] = 0;
|
||||
continue;
|
||||
}
|
||||
if (reserves[j] != EthAddressLib.ethAddress()) {
|
||||
balances[j] = balanceOf(_user, reserves[j]);
|
||||
} else {
|
||||
balances[j] = _user.balance; // ETH balance
|
||||
}
|
||||
}
|
||||
|
||||
return (reserves, balances);
|
||||
}
|
||||
}
|
52
contracts/mocks/flashloan/MockFlashLoanReceiver.sol
Normal file
52
contracts/mocks/flashloan/MockFlashLoanReceiver.sol
Normal file
|
@ -0,0 +1,52 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
|
||||
import "../../flashloan/base/FlashLoanReceiverBase.sol";
|
||||
import "../tokens/MintableERC20.sol";
|
||||
|
||||
contract MockFlashLoanReceiver is FlashLoanReceiverBase {
|
||||
|
||||
using SafeMath for uint256;
|
||||
event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee);
|
||||
event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee);
|
||||
|
||||
|
||||
bool failExecution = false;
|
||||
|
||||
constructor(ILendingPoolAddressesProvider _provider) FlashLoanReceiverBase(_provider) public {
|
||||
}
|
||||
|
||||
function setFailExecutionTransfer(bool _fail) public {
|
||||
failExecution = _fail;
|
||||
}
|
||||
|
||||
function executeOperation(
|
||||
address _reserve,
|
||||
uint256 _amount,
|
||||
uint256 _fee,
|
||||
bytes memory _params) public override {
|
||||
//mint to this contract the specific amount
|
||||
MintableERC20 token = MintableERC20(_reserve);
|
||||
|
||||
|
||||
//check the contract has the specified balance
|
||||
require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance for the contract");
|
||||
|
||||
if(failExecution) {
|
||||
emit ExecutedWithFail(_reserve, _amount, _fee);
|
||||
return;
|
||||
}
|
||||
|
||||
//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
|
||||
|
||||
if(_reserve != EthAddressLib.ethAddress()) {
|
||||
token.mint(_fee);
|
||||
}
|
||||
//returning amount + fee to the destination
|
||||
transferFundsBackToPoolInternal(_reserve, _amount.add(_fee));
|
||||
emit ExecutedWithSuccess(_reserve, _amount, _fee);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorBAT is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorBUSD is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
17
contracts/mocks/oracle/CLAggregators/MockAggregatorBase.sol
Normal file
17
contracts/mocks/oracle/CLAggregators/MockAggregatorBase.sol
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
contract MockAggregatorBase {
|
||||
int256 private _latestAnswer;
|
||||
|
||||
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
|
||||
|
||||
constructor (int256 _initialAnswer) public {
|
||||
_latestAnswer = _initialAnswer;
|
||||
emit AnswerUpdated(_initialAnswer, 0, now);
|
||||
}
|
||||
|
||||
function latestAnswer() external view returns (int256) {
|
||||
return _latestAnswer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorDAI is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorKNC is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorLEND is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorLINK is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorMANA is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorMKR is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorREP is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorSNX is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorSUSD is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorTUSD is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_DAI_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_LEND_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_LINK_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_MKR_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_SETH_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUNI_USDC_ETH is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUSD is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUSDC is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorUSDT is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorWBTC is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "./MockAggregatorBase.sol";
|
||||
|
||||
contract MockAggregatorZRX is MockAggregatorBase {
|
||||
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
|
||||
}
|
7
contracts/mocks/oracle/ChainlinkUSDETHOracleI.sol
Normal file
7
contracts/mocks/oracle/ChainlinkUSDETHOracleI.sol
Normal file
|
@ -0,0 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface ChainlinkUSDETHOracleI {
|
||||
event AnswerUpdated(int256 indexed current, uint256 indexed answerId);
|
||||
}
|
||||
|
20
contracts/mocks/oracle/GenericOracleI.sol
Normal file
20
contracts/mocks/oracle/GenericOracleI.sol
Normal file
|
@ -0,0 +1,20 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface GenericOracleI {
|
||||
// ganache
|
||||
event AssetPriceUpdated(address _asset, uint256 _price, uint256 timestamp);
|
||||
event EthPriceUpdated(uint256 _price, uint256 timestamp);
|
||||
|
||||
// kovan
|
||||
event ProphecySubmitted(
|
||||
address indexed _sybil,
|
||||
address indexed _asset,
|
||||
uint96 _sybilProphecy,
|
||||
uint96 _oracleProphecy
|
||||
);
|
||||
|
||||
function getAssetPrice(address _asset) external view returns(uint256);
|
||||
function getEthUsdPrice() external view returns(uint256);
|
||||
}
|
||||
|
12
contracts/mocks/oracle/IExtendedPriceAggregator.sol
Normal file
12
contracts/mocks/oracle/IExtendedPriceAggregator.sol
Normal file
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface IExtendedPriceAggregator {
|
||||
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
|
||||
|
||||
function getToken() external view returns (address);
|
||||
function getTokenType() external view returns (uint256);
|
||||
function getPlatformId() external view returns (uint256);
|
||||
function getSubTokens() external view returns(address[] memory);
|
||||
function latestAnswer() external view returns (int256);
|
||||
}
|
28
contracts/mocks/oracle/LendingRateOracle.sol
Normal file
28
contracts/mocks/oracle/LendingRateOracle.sol
Normal file
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "../../interfaces/ILendingRateOracle.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
contract LendingRateOracle is ILendingRateOracle, Ownable {
|
||||
|
||||
mapping(address => uint256) borrowRates;
|
||||
mapping(address => uint256) liquidityRates;
|
||||
|
||||
|
||||
function getMarketBorrowRate(address _asset) external override view returns(uint256) {
|
||||
return borrowRates[_asset];
|
||||
}
|
||||
|
||||
function setMarketBorrowRate(address _asset, uint256 _rate) external override onlyOwner {
|
||||
borrowRates[_asset] = _rate;
|
||||
}
|
||||
|
||||
function getMarketLiquidityRate(address _asset) external view returns(uint256) {
|
||||
return liquidityRates[_asset];
|
||||
}
|
||||
|
||||
function setMarketLiquidityRate(address _asset, uint256 _rate) external onlyOwner {
|
||||
liquidityRates[_asset] = _rate;
|
||||
}
|
||||
}
|
32
contracts/mocks/oracle/PriceOracle.sol
Normal file
32
contracts/mocks/oracle/PriceOracle.sol
Normal file
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "../../interfaces/IPriceOracle.sol";
|
||||
|
||||
|
||||
contract PriceOracle is IPriceOracle {
|
||||
|
||||
mapping(address => uint256) prices;
|
||||
uint256 ethPriceUsd;
|
||||
|
||||
event AssetPriceUpdated(address _asset, uint256 _price, uint256 timestamp);
|
||||
event EthPriceUpdated(uint256 _price, uint256 timestamp);
|
||||
|
||||
function getAssetPrice(address _asset) external override view returns(uint256) {
|
||||
return prices[_asset];
|
||||
}
|
||||
|
||||
function setAssetPrice(address _asset, uint256 _price) external override {
|
||||
prices[_asset] = _price;
|
||||
emit AssetPriceUpdated(_asset, _price, block.timestamp);
|
||||
}
|
||||
|
||||
function getEthUsdPrice() external view returns(uint256) {
|
||||
return ethPriceUsd;
|
||||
}
|
||||
|
||||
function setEthUsdPrice(uint256 _price) external {
|
||||
ethPriceUsd = _price;
|
||||
emit EthPriceUpdated(_price, block.timestamp);
|
||||
}
|
||||
}
|
23
contracts/mocks/tokens/MintableERC20.sol
Normal file
23
contracts/mocks/tokens/MintableERC20.sol
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import "../../tokenization/ERC20.sol";
|
||||
|
||||
/**
|
||||
* @title ERC20Mintable
|
||||
* @dev ERC20 minting logic
|
||||
*/
|
||||
contract MintableERC20 is ERC20 {
|
||||
constructor(string memory name, string memory symbol, uint8 decimals) ERC20(name, symbol) public {
|
||||
_setupDecimals(decimals);
|
||||
}
|
||||
/**
|
||||
* @dev Function to mint tokens
|
||||
* @param value The amount of tokens to mint.
|
||||
* @return A boolean that indicates if the operation was successful.
|
||||
*/
|
||||
function mint(uint256 value) public returns (bool) {
|
||||
_mint(msg.sender, value);
|
||||
return true;
|
||||
}
|
||||
}
|
9
contracts/mocks/tokens/MockBAT.sol
Normal file
9
contracts/mocks/tokens/MockBAT.sol
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
|
||||
contract MockBAT is MintableERC20("BAT", "Basic Attention Token", 18) {}
|
8
contracts/mocks/tokens/MockBUSD.sol
Normal file
8
contracts/mocks/tokens/MockBUSD.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockBUSD is MintableERC20("BUSD", "Binance USD", 18) {}
|
9
contracts/mocks/tokens/MockDAI.sol
Normal file
9
contracts/mocks/tokens/MockDAI.sol
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
|
||||
contract MockDAI is MintableERC20("DAI", "DAI", 18) {}
|
8
contracts/mocks/tokens/MockKNC.sol
Normal file
8
contracts/mocks/tokens/MockKNC.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockKNC is MintableERC20("KNC", "Kyber Network", 18) {}
|
9
contracts/mocks/tokens/MockLEND.sol
Normal file
9
contracts/mocks/tokens/MockLEND.sol
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
|
||||
contract MockLEND is MintableERC20("LEND", "LEND", 18) {}
|
8
contracts/mocks/tokens/MockLINK.sol
Normal file
8
contracts/mocks/tokens/MockLINK.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockLINK is MintableERC20("LINK", "ChainLink", 18) {}
|
8
contracts/mocks/tokens/MockMANA.sol
Normal file
8
contracts/mocks/tokens/MockMANA.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockMANA is MintableERC20("MANA", "Decentraland", 18) {}
|
8
contracts/mocks/tokens/MockMKR.sol
Normal file
8
contracts/mocks/tokens/MockMKR.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockMKR is MintableERC20("MKR", "Maker", 18) {}
|
8
contracts/mocks/tokens/MockREP.sol
Normal file
8
contracts/mocks/tokens/MockREP.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockREP is MintableERC20("REP", "Augur", 18) {}
|
8
contracts/mocks/tokens/MockSNX.sol
Normal file
8
contracts/mocks/tokens/MockSNX.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockSNX is MintableERC20("SNX", "SNX", 18) {}
|
8
contracts/mocks/tokens/MockSUSD.sol
Normal file
8
contracts/mocks/tokens/MockSUSD.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockSUSD is MintableERC20("SUSD", "Synthetix USD", 6) {}
|
8
contracts/mocks/tokens/MockTUSD.sol
Normal file
8
contracts/mocks/tokens/MockTUSD.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockTUSD is MintableERC20("TUSD", "TrueUSD", 18) {}
|
8
contracts/mocks/tokens/MockUNI_DAI_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_DAI_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_DAI_ETH is MintableERC20("UNI_DAI_ETH", "UNI_DAI_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUNI_LEND_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_LEND_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_LEND_ETH is MintableERC20("UNI_LEND_ETH", "UNI_LEND_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUNI_LINK_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_LINK_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_LINK_ETH is MintableERC20("UNI_LINK_ETH", "UNI_LINK_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUNI_MKR_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_MKR_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_MKR_ETH is MintableERC20("UNI_MKR_ETH", "UNI_MKR_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUNI_SETH_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_SETH_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_SETH_ETH is MintableERC20("UNI_SETH_ETH", "UNI_SETH_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUNI_USDC_ETH.sol
Normal file
8
contracts/mocks/tokens/MockUNI_USDC_ETH.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUNI_USDC_ETH is MintableERC20("UNI_USDC_ETH", "UNI_USDC_ETH", 18) {}
|
8
contracts/mocks/tokens/MockUSD.sol
Normal file
8
contracts/mocks/tokens/MockUSD.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUSD is MintableERC20("USD", "USD", 18) {}
|
8
contracts/mocks/tokens/MockUSDC.sol
Normal file
8
contracts/mocks/tokens/MockUSDC.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUSDC is MintableERC20("USDC", "USD Coin", 6) {}
|
8
contracts/mocks/tokens/MockUSDT.sol
Normal file
8
contracts/mocks/tokens/MockUSDT.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockUSDT is MintableERC20("USDT", "USDT Coin", 6) {}
|
8
contracts/mocks/tokens/MockWBTC.sol
Normal file
8
contracts/mocks/tokens/MockWBTC.sol
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
|
||||
import "./MintableERC20.sol";
|
||||
|
||||
|
||||
contract MockWBTC is MintableERC20("WBTC", "WBTC Coin", 18) {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user