Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

New York Blockchain Week Hackathon GitHub Prize: Write rDAI allocation strategy for Aave #64

Merged
merged 14 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions packages/contracts/contracts/AaveAllocationStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
pragma solidity >=0.5.10 <0.6.0;

import {IAllocationStrategy} from "./IAllocationStrategy.sol";
import {Ownable} from "@openzeppelin/contracts/ownership/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AToken} from "./aave/contracts/tokenization/AToken.sol";
import {LendingPool} from "./aave/contracts/lendingpool/LendingPool.sol";
import {LendingPoolCore} from "./aave/contracts/lendingpool/LendingPoolCore.sol";
import {LendingPoolAddressesProvider} from "./aave/contracts/configuration/LendingPoolAddressesProvider.sol";

contract AaveAllocationStrategy is IAllocationStrategy, Ownable {

AToken private aToken;
LendingPool private lendingPool;
LendingPoolCore private lendingPoolCore;
IERC20 private token;
uint256 private totalInvested;

event TotalInvested(uint indexed totalInvested);
constructor(AToken aToken_, LendingPoolAddressesProvider lendingPoolAddressesProvider_) public {
// Aave aToken
aToken = aToken_;
// Aave Lending Pool
lendingPool = LendingPool(lendingPoolAddressesProvider_.getLendingPool());
// Aave Lending Pool Core
lendingPoolCore = LendingPoolCore(lendingPoolAddressesProvider_.getLendingPoolCore());
// The aTokens underlying asset
token = IERC20(aToken.underlyingAssetAddress());
}

/// @dev Returns the address of the underlying token
function underlying() external view returns (address) {
return address(token);
}

/// @dev Returnss the exchange rate from aToken to the underlying asset
function exchangeRateStored() public view returns (uint256) {
// Aave has a fixed exchange rate of 1:1 for aToken <-> underlying
// Interest is modeled by increasing balance of aTokens
// We calculate a virtual exchange rate based on the aToken balance and invested amount

if(totalInvested == 0) {
return 10**18;
}

// Safe Math not needed. aToken balance would need to be unfathomably high for the multiplication to overflow
return (aToken.balanceOf(address(this)) * 10**18) / totalInvested;
}

/// @dev Accrues interest. Not required for Aave protocol. Always returns true
function accrueInterest() external returns (bool) {
// Aaves interest accrual does not need to be called explicitly
// aToken.balanceOf() already contains the accrued interest
return true;
}

/// @dev Invest investAmount of underlying asset into Aave
function investUnderlying(uint256 investAmount) external onlyOwner returns (uint256) {
// Transfer underlying from caller to this contract
token.transferFrom(msg.sender, address(this), investAmount);
// Approve the Aave Lending Pool to access the underlying tokens
token.approve(address(lendingPoolCore), investAmount);
// Store the aToken balance of aTokens before deposit
uint256 aTotalBefore = aToken.balanceOf(address(this));
// Deposit investAmount of underlying asset into Aave
lendingPool.deposit(address(token), investAmount, 0);
// Calculate the difference in aToken balance after deposit
uint256 aTotalAfter = aToken.balanceOf(address(this));
// Check the aToken balance has increased after deposit
require (aTotalAfter >= aTotalBefore, "Aave minted negative amount!?");

// Update totalInvested. We want to keep the exchange rate while updating the totalInvested.
// We calculate the newTotalInvested value we need to have the same exchange rate as before
// oldExchangeRate = newExchangeRate
// oldATokenBalance / oldTotalInvested = newATokenBalance / newTotalInvested // solve for newTotalInvested
// newATokenBalance / (oldATokenBalance / oldTotalInvested) = newTotalInvested
// newTotalInvested = (newATokenBalance * oldTotalInvested) / oldATokenBalance
if(aTotalBefore > 0) {
totalInvested = (aTotalAfter * totalInvested) / aTotalBefore;
} else {
totalInvested = investAmount;
}
emit TotalInvested(totalInvested);

// Return the difference in aToken balance
return (investAmount * 10**18) / exchangeRateStored();
}

/// @dev Redeem redeemAmount from Aave
function redeemUnderlying(uint256 redeemAmount) external onlyOwner returns (uint256) {
// Store the aToken balance of aTokens before deposit
uint256 aTotalBefore = aToken.balanceOf(address(this));
// Redeem redeemAmount of underlying asset from Aave
aToken.redeem(redeemAmount);
// Calculate the difference in aToken balance after redeem
uint256 aTotalAfter = aToken.balanceOf(address(this));
// Check the aToken balance has decreased after redeem
require(aTotalAfter <= aTotalBefore, "Aave redeemed negative amount!?");

// Update totalInvested. We want to keep the exchange rate while updating the totalInvested.
// We calculate the newTotalInvested value we need to have the same exchange rate as before
// oldExchangeRate = newExchangeRate
// oldATokenBalance / oldTotalInvested = newATokenBalance / newTotalInvested // solve for newTotalInvested
// newATokenBalance / (oldATokenBalance / oldTotalInvested) = newTotalInvested
// newTotalInvested = (newATokenBalance * oldTotalInvested) / oldATokenBalance
totalInvested = (aTotalAfter * totalInvested) / aTotalBefore;
hellwolf marked this conversation as resolved.
Show resolved Hide resolved
emit TotalInvested(totalInvested);

// Transfer redeemed underlying assets to caller
token.transfer(msg.sender, redeemAmount);
// Return the difference in aToken balance
return (redeemAmount * 10**18) / exchangeRateStored();
}

/// @dev Redeem the entire balance of aToken from Aave
function redeemAll() external onlyOwner
returns (uint256 savingsAmount, uint256 underlyingAmount) {
// Store the aToken balance of aTokens before deposit
savingsAmount = aToken.balanceOf(address(this));
// Redeem the entire aToken balance from Aave
aToken.redeem(savingsAmount);
underlyingAmount = token.balanceOf(address(this));
// Update total invested amount
totalInvested = 0;
emit TotalInvested(totalInvested);
// Transfer redeemed underlying assets to caller
token.transfer(msg.sender, underlyingAmount);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity >=0.5.10 <0.6.0;
import {IAllocationStrategy} from "./IAllocationStrategy.sol";
import {Ownable} from "@openzeppelin/contracts/ownership/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {CErc20Interface} from "../compound/contracts/CErc20Interface.sol";
import {CErc20Interface} from "./compound/contracts/CErc20Interface.sol";

contract CompoundAllocationStrategy is IAllocationStrategy, Ownable {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma solidity ^0.5.0;

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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
pragma solidity ^0.5.0;

import "@openzeppelin/contracts/ownership/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 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 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 view returns (address payable) {
address payable core = address(uint160(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 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 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 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 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 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 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 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 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 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 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 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 view returns (address) {
return getAddress(LENDING_POOL_MANAGER);
}

function setLendingPoolManager(address _lendingPoolManager) public onlyOwner {
_setAddress(LENDING_POOL_MANAGER, _lendingPoolManager);
emit LendingPoolManagerUpdated(_lendingPoolManager);
}

function getPriceOracle() public view returns (address) {
return getAddress(PRICE_ORACLE);
}

function setPriceOracle(address _priceOracle) public onlyOwner {
_setAddress(PRICE_ORACLE, _priceOracle);
emit PriceOracleUpdated(_priceOracle);
}

function getLendingRateOracle() public view returns (address) {
return getAddress(LENDING_RATE_ORACLE);
}

function setLendingRateOracle(address _lendingRateOracle) public onlyOwner {
_setAddress(LENDING_RATE_ORACLE, _lendingRateOracle);
emit LendingRateOracleUpdated(_lendingRateOracle);
}


function getTokenDistributor() public view returns (address) {
return getAddress(TOKEN_DISTRIBUTOR);
}

function setTokenDistributor(address _tokenDistributor) public 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);
}

}
}
Loading