Added transfer of the funds to aTokens

This commit is contained in:
The3D 2020-07-10 19:16:04 +02:00
parent e5f0206f87
commit edd7ad3b15
9 changed files with 664 additions and 674 deletions

View File

@ -21,14 +21,13 @@ abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
receive() external payable {} receive() external payable {}
function transferFundsBackToPoolInternal(address _reserve, uint256 _amount) internal { function transferFundsBackInternal(address _reserve, address _destination, uint256 _amount) internal {
address payable pool = payable(addressesProvider.getLendingPool()); transferInternal(payable(_destination),_reserve, _amount);
transferInternal(pool,_reserve, _amount);
} }
function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal { function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal {
if(_reserve == EthAddressLib.ethAddress()) { if(_reserve == EthAddressLib.ethAddress()) {
//solium-disable-next-line //solium-disable-next-line
_destination.call{value: _amount}(""); _destination.call{value: _amount}("");

View File

@ -9,5 +9,5 @@ pragma solidity ^0.6.8;
**/ **/
interface IFlashLoanReceiver { interface IFlashLoanReceiver {
function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external; function executeOperation(address _reserve, address _destination, uint256 _amount, uint256 _fee, bytes calldata _params) external;
} }

View File

@ -261,12 +261,13 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
uint256 _amount, uint256 _amount,
uint16 _referralCode uint16 _referralCode
) external payable nonReentrant { ) external payable nonReentrant {
ReserveLogic.ReserveData storage reserve = reserves[_reserve]; ReserveLogic.ReserveData storage reserve = reserves[_reserve];
UserLogic.UserReserveData storage user = usersReserveData[msg.sender][_reserve]; UserLogic.UserReserveData storage user = usersReserveData[msg.sender][_reserve];
ValidationLogic.validateDeposit(reserve, _amount); ValidationLogic.validateDeposit(reserve, _amount);
AToken aToken = AToken(reserve.aTokenAddress); AToken aToken = AToken(payable(reserve.aTokenAddress));
bool isFirstDeposit = aToken.balanceOf(msg.sender) == 0; bool isFirstDeposit = aToken.balanceOf(msg.sender) == 0;
@ -280,8 +281,8 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
//minting AToken to user 1:1 with the specific exchange rate //minting AToken to user 1:1 with the specific exchange rate
aToken.mintOnDeposit(msg.sender, _amount); aToken.mintOnDeposit(msg.sender, _amount);
//transfer to the core contract //transfer to the aToken contract
IERC20(_reserve).universalTransferFromSenderToThis(_amount, true); IERC20(_reserve).universalTransferFrom(msg.sender, address(aToken), _amount, true);
//solium-disable-next-line //solium-disable-next-line
emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp); emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp);
@ -303,16 +304,20 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
ReserveLogic.ReserveData storage reserve = reserves[_reserve]; ReserveLogic.ReserveData storage reserve = reserves[_reserve];
UserLogic.UserReserveData storage user = usersReserveData[_user][_reserve]; UserLogic.UserReserveData storage user = usersReserveData[_user][_reserve];
AToken aToken = AToken(payable(reserve.aTokenAddress));
ValidationLogic.validateRedeem(reserve, _reserve, _amount); ValidationLogic.validateRedeem(reserve, _reserve, _amount);
reserve.updateCumulativeIndexesAndTimestamp();
reserve.updateInterestRates(_reserve, 0, _amount);
if (_aTokenBalanceAfterRedeem == 0) { if (_aTokenBalanceAfterRedeem == 0) {
user.useAsCollateral = false; user.useAsCollateral = false;
} }
reserve.updateCumulativeIndexesAndTimestamp();
reserve.updateInterestRates(_reserve, 0, _amount);
IERC20(_reserve).universalTransfer(_user, _amount); AToken(reserve.aTokenAddress).transferUnderlyingTo(_user, _amount);
//solium-disable-next-line //solium-disable-next-line
emit RedeemUnderlying(_reserve, _user, _amount, block.timestamp); emit RedeemUnderlying(_reserve, _user, _amount, block.timestamp);
@ -372,7 +377,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
reserve.updateInterestRates(_reserve, 0, _amount); reserve.updateInterestRates(_reserve, 0, _amount);
//if we reached this point, we can transfer //if we reached this point, we can transfer
IERC20(_reserve).universalTransfer(msg.sender, _amount); AToken(reserve.aTokenAddress).transferUnderlyingTo(msg.sender, _amount);
emit Borrow( emit Borrow(
_reserve, _reserve,
@ -453,7 +458,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
reserve.updateInterestRates(_reserve, vars.paybackAmount, 0); reserve.updateInterestRates(_reserve, vars.paybackAmount, 0);
IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmount, false); IERC20(_reserve).universalTransferFrom(msg.sender, reserve.aTokenAddress, vars.paybackAmount, false);
if (IERC20(_reserve).isETH()) { if (IERC20(_reserve).isETH()) {
//send excess ETH back to the caller if needed //send excess ETH back to the caller if needed
@ -651,6 +656,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
uint256 protocolFeeBips; uint256 protocolFeeBips;
uint256 amountFee; uint256 amountFee;
uint256 protocolFee; uint256 protocolFee;
address payable aTokenAddress;
} }
/** /**
@ -667,12 +673,15 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
uint256 _amount, uint256 _amount,
bytes memory _params bytes memory _params
) public nonReentrant { ) public nonReentrant {
FlashLoanLocalVars memory vars; FlashLoanLocalVars memory vars;
ReserveLogic.ReserveData storage reserve = reserves[_reserve]; ReserveLogic.ReserveData storage reserve = reserves[_reserve];
vars.aTokenAddress = payable(reserve.aTokenAddress);
//check that the reserve has enough available liquidity //check that the reserve has enough available liquidity
vars.availableLiquidityBefore = IERC20(_reserve).universalBalanceOf(address(this)); vars.availableLiquidityBefore = IERC20(_reserve).universalBalanceOf(vars.aTokenAddress);
//calculate amount fee //calculate amount fee
vars.amountFee = _amount.mul(FLASHLOAN_FEE_TOTAL).div(10000); vars.amountFee = _amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
@ -680,6 +689,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
//protocol fee is the part of the amountFee reserved for the protocol - the rest goes to depositors //protocol fee is the part of the amountFee reserved for the protocol - the rest goes to depositors
vars.protocolFee = vars.amountFee.mul(FLASHLOAN_FEE_PROTOCOL).div(10000); vars.protocolFee = vars.amountFee.mul(FLASHLOAN_FEE_PROTOCOL).div(10000);
require( require(
vars.availableLiquidityBefore >= _amount, vars.availableLiquidityBefore >= _amount,
'There is not enough liquidity available to borrow' 'There is not enough liquidity available to borrow'
@ -695,13 +705,13 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
address payable userPayable = address(uint160(_receiver)); address payable userPayable = address(uint160(_receiver));
//transfer funds to the receiver //transfer funds to the receiver
IERC20(_reserve).universalTransfer(userPayable, _amount); AToken(vars.aTokenAddress).transferUnderlyingTo(userPayable, _amount);
//execute action of the receiver //execute action of the receiver
receiver.executeOperation(_reserve, _amount, vars.amountFee, _params); receiver.executeOperation(_reserve, vars.aTokenAddress, _amount, vars.amountFee, _params);
//check that the actual balance of the core contract includes the returned amount //check that the actual balance of the core contract includes the returned amount
uint256 availableLiquidityAfter = IERC20(_reserve).universalBalanceOf(address(this)); uint256 availableLiquidityAfter = IERC20(_reserve).universalBalanceOf(vars.aTokenAddress);
require( require(
availableLiquidityAfter == vars.availableLiquidityBefore.add(vars.amountFee), availableLiquidityAfter == vars.availableLiquidityBefore.add(vars.amountFee),
@ -791,7 +801,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
{ {
ReserveLogic.ReserveData memory reserve = reserves[_reserve]; ReserveLogic.ReserveData memory reserve = reserves[_reserve];
return ( return (
IERC20(_reserve).universalBalanceOf(address(this)), IERC20(_reserve).universalBalanceOf(reserve.aTokenAddress),
IERC20(reserve.stableDebtTokenAddress).totalSupply(), IERC20(reserve.stableDebtTokenAddress).totalSupply(),
IERC20(reserve.variableDebtTokenAddress).totalSupply(), IERC20(reserve.variableDebtTokenAddress).totalSupply(),
reserve.currentLiquidityRate, reserve.currentLiquidityRate,

View File

@ -192,6 +192,9 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
vars.userCollateralBalance vars.userCollateralBalance
); );
vars.collateralAtoken = AToken(payable(collateralReserve.aTokenAddress));
//if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough //if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough
//of _collateral to cover the actual amount that is being liquidated, hence we liquidate //of _collateral to cover the actual amount that is being liquidated, hence we liquidate
//a smaller amount //a smaller amount
@ -202,7 +205,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
//if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve //if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve
if (!_receiveAToken) { if (!_receiveAToken) {
uint256 currentAvailableCollateral = IERC20(_collateral).universalBalanceOf(address(this)); uint256 currentAvailableCollateral = IERC20(_collateral).universalBalanceOf(address(vars.collateralAtoken));
if (currentAvailableCollateral < vars.maxCollateralToLiquidate) { if (currentAvailableCollateral < vars.maxCollateralToLiquidate) {
return ( return (
uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY), uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY),
@ -220,8 +223,6 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt)); IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt));
} }
vars.collateralAtoken = AToken(collateralReserve.aTokenAddress);
//if liquidator reclaims the aToken, he receives the equivalent atoken amount //if liquidator reclaims the aToken, he receives the equivalent atoken amount
if (_receiveAToken) { if (_receiveAToken) {
vars.collateralAtoken.transferOnLiquidation( vars.collateralAtoken.transferOnLiquidation(
@ -233,13 +234,11 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
//otherwise receives the underlying asset //otherwise receives the underlying asset
//burn the equivalent amount of atoken //burn the equivalent amount of atoken
vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate); vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate);
vars.collateralAtoken.transferUnderlyingTo(msg.sender, vars.maxCollateralToLiquidate);
IERC20(_collateral).universalTransfer(msg.sender, vars.maxCollateralToLiquidate);
} }
//transfers the principal currency to the pool //transfers the principal currency to the aToken
IERC20(_reserve).universalTransferFromSenderToThis(vars.actualAmountToLiquidate, true); IERC20(_reserve).universalTransferFrom(msg.sender, principalReserve.aTokenAddress, vars.actualAmountToLiquidate, true);
emit LiquidationCall( emit LiquidationCall(
_collateral, _collateral,

View File

@ -15,6 +15,7 @@ import '../interfaces/ILendingRateOracle.sol';
import '../interfaces/IReserveInterestRateStrategy.sol'; import '../interfaces/IReserveInterestRateStrategy.sol';
import '../tokenization/AToken.sol'; import '../tokenization/AToken.sol';
import './WadRayMath.sol'; import './WadRayMath.sol';
import '@nomiclabs/buidler/console.sol';
/** /**
* @title ReserveLogic library * @title ReserveLogic library
@ -56,7 +57,7 @@ library ReserveLogic {
/** /**
* @dev address of the aToken representing the asset * @dev address of the aToken representing the asset
**/ **/
address aTokenAddress; address payable aTokenAddress;
address stableDebtTokenAddress; address stableDebtTokenAddress;
address variableDebtTokenAddress; address variableDebtTokenAddress;
/** /**
@ -193,7 +194,7 @@ library ReserveLogic {
_self.lastVariableBorrowCumulativeIndex = WadRayMath.ray(); _self.lastVariableBorrowCumulativeIndex = WadRayMath.ray();
} }
_self.aTokenAddress = _aTokenAddress; _self.aTokenAddress = payable(_aTokenAddress);
_self.stableDebtTokenAddress = _stableDebtAddress; _self.stableDebtTokenAddress = _stableDebtAddress;
_self.variableDebtTokenAddress = _variableDebtAddress; _self.variableDebtTokenAddress = _variableDebtAddress;
_self.decimals = _decimals; _self.decimals = _decimals;
@ -353,15 +354,11 @@ library ReserveLogic {
uint256 _liquidityAdded, uint256 _liquidityAdded,
uint256 _liquidityTaken uint256 _liquidityTaken
) internal { ) internal {
uint256 currentAvgStableRate = IStableDebtToken(_reserve.stableDebtTokenAddress) uint256 currentAvgStableRate = IStableDebtToken(_reserve.stableDebtTokenAddress)
.getAverageStableRate(); .getAverageStableRate();
uint256 balance = IERC20(_reserveAddress).universalBalanceOf(address(this)); uint256 balance = IERC20(_reserveAddress).universalBalanceOf(_reserve.aTokenAddress);
//if the reserve is ETH, the msg.value has already been cumulated to the balance of the reserve
if (IERC20(_reserveAddress).isETH()) {
balance = balance.sub(msg.value);
}
( (
uint256 newLiquidityRate, uint256 newLiquidityRate,

View File

@ -53,7 +53,8 @@ library ValidationLogic {
require(msg.sender == _reserve.aTokenAddress, '31'); require(msg.sender == _reserve.aTokenAddress, '31');
uint256 currentAvailableLiquidity = IERC20(_reserveAddress).universalBalanceOf(address(this)); uint256 currentAvailableLiquidity = IERC20(_reserveAddress).universalBalanceOf(address(_reserve.aTokenAddress));
require(currentAvailableLiquidity >= _amount, '4'); require(currentAvailableLiquidity >= _amount, '4');
} }
@ -117,7 +118,7 @@ library ValidationLogic {
); );
//check that the amount is available in the reserve //check that the amount is available in the reserve
vars.availableLiquidity = IERC20(_reserveAddress).universalBalanceOf(address(this)); vars.availableLiquidity = IERC20(_reserveAddress).universalBalanceOf(address(_reserve.aTokenAddress));
require(vars.availableLiquidity >= _amount, '7'); require(vars.availableLiquidity >= _amount, '7');

View File

@ -38,7 +38,7 @@ contract AaveProtocolTestHelpers {
for (uint256 i = 0; i < reserves.length; i++) { for (uint256 i = 0; i < reserves.length; i++) {
(address aTokenAddress,,) = pool.getReserveTokensAddresses(reserves[i]); (address aTokenAddress,,) = pool.getReserveTokensAddresses(reserves[i]);
aTokens[i] = TokenData({ aTokens[i] = TokenData({
symbol: AToken(aTokenAddress).symbol(), symbol: AToken(payable(aTokenAddress)).symbol(),
tokenAddress: aTokenAddress tokenAddress: aTokenAddress
}); });
} }

View File

@ -24,6 +24,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
function executeOperation( function executeOperation(
address _reserve, address _reserve,
address _destination,
uint256 _amount, uint256 _amount,
uint256 _fee, uint256 _fee,
bytes memory _params) public override { bytes memory _params) public override {
@ -46,7 +47,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
token.mint(_fee); token.mint(_fee);
} }
//returning amount + fee to the destination //returning amount + fee to the destination
transferFundsBackToPoolInternal(_reserve, _amount.add(_fee)); transferFundsBackInternal(_reserve, _destination, _amount.add(_fee));
emit ExecutedWithSuccess(_reserve, _amount, _fee); emit ExecutedWithSuccess(_reserve, _amount, _fee);
} }
} }

View File

@ -1,11 +1,12 @@
// SPDX-License-Identifier: agpl-3.0 // SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8; pragma solidity ^0.6.8;
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {ERC20} from './ERC20.sol';
import {ERC20} from "./ERC20.sol"; import {LendingPoolAddressesProvider} from '../configuration/LendingPoolAddressesProvider.sol';
import {LendingPoolAddressesProvider} from "../configuration/LendingPoolAddressesProvider.sol"; import {LendingPool} from '../lendingpool/LendingPool.sol';
import {LendingPool} from "../lendingpool/LendingPool.sol"; import {WadRayMath} from '../libraries/WadRayMath.sol';
import {WadRayMath} from "../libraries/WadRayMath.sol"; import {UniversalERC20} from '../libraries/UniversalERC20.sol';
import '@nomiclabs/buidler/console.sol';
/** /**
* @title Aave ERC20 AToken * @title Aave ERC20 AToken
@ -15,7 +16,7 @@ import {WadRayMath} from "../libraries/WadRayMath.sol";
*/ */
contract AToken is ERC20 { contract AToken is ERC20 {
using WadRayMath for uint256; using WadRayMath for uint256;
using SafeERC20 for ERC20; using UniversalERC20 for ERC20;
uint256 public constant UINT_MAX_VALUE = uint256(-1); uint256 public constant UINT_MAX_VALUE = uint256(-1);
@ -114,10 +115,7 @@ contract AToken is ERC20 {
uint256 _redirectedBalanceRemoved uint256 _redirectedBalanceRemoved
); );
event InterestRedirectionAllowanceChanged( event InterestRedirectionAllowanceChanged(address indexed _from, address indexed _to);
address indexed _from,
address indexed _to
);
address public underlyingAssetAddress; address public underlyingAssetAddress;
@ -130,15 +128,12 @@ contract AToken is ERC20 {
LendingPool private pool; LendingPool private pool;
modifier onlyLendingPool { modifier onlyLendingPool {
require( require(msg.sender == address(pool), 'The caller of this function must be a lending pool');
msg.sender == address(pool),
"The caller of this function must be a lending pool"
);
_; _;
} }
modifier whenTransferAllowed(address _from, uint256 _amount) { modifier whenTransferAllowed(address _from, uint256 _amount) {
require(isTransferAllowed(_from, _amount), "Transfer cannot be allowed."); require(isTransferAllowed(_from, _amount), 'Transfer cannot be allowed.');
_; _;
} }
@ -153,19 +148,20 @@ contract AToken is ERC20 {
addressesProvider = _addressesProvider; addressesProvider = _addressesProvider;
pool = LendingPool(payable(addressesProvider.getLendingPool())); pool = LendingPool(payable(addressesProvider.getLendingPool()));
underlyingAssetAddress = _underlyingAsset; underlyingAssetAddress = _underlyingAsset;
ERC20(underlyingAssetAddress).safeApprove(address(pool), type(uint256).max);
} }
/** /**
* @notice ERC20 implementation internal function backing transfer() and transferFrom() * @notice ERC20 implementation internal function backing transfer() and transferFrom()
* @dev validates the transfer before allowing it. NOTE: This is not standard ERC20 behavior * @dev validates the transfer before allowing it. NOTE: This is not standard ERC20 behavior
**/ **/
function _transfer(address _from, address _to, uint256 _amount) internal override whenTransferAllowed(_from, _amount) { function _transfer(
address _from,
address _to,
uint256 _amount
) internal override whenTransferAllowed(_from, _amount) {
executeTransferInternal(_from, _to, _amount); executeTransferInternal(_from, _to, _amount);
} }
/** /**
* @dev redirects the interest generated to a target address. * @dev redirects the interest generated to a target address.
* when the interest is redirected, the user balance is added to * when the interest is redirected, the user balance is added to
@ -187,7 +183,7 @@ contract AToken is ERC20 {
function redirectInterestStreamOf(address _from, address _to) external { function redirectInterestStreamOf(address _from, address _to) external {
require( require(
msg.sender == interestRedirectionAllowances[_from], msg.sender == interestRedirectionAllowances[_from],
"Caller is not allowed to redirect the interest of the user" 'Caller is not allowed to redirect the interest of the user'
); );
redirectInterestStreamInternal(_from, _to); redirectInterestStreamInternal(_from, _to);
} }
@ -199,12 +195,9 @@ contract AToken is ERC20 {
* the allowance. * the allowance.
**/ **/
function allowInterestRedirectionTo(address _to) external { function allowInterestRedirectionTo(address _to) external {
require(_to != msg.sender, "User cannot give allowance to himself"); require(_to != msg.sender, 'User cannot give allowance to himself');
interestRedirectionAllowances[msg.sender] = _to; interestRedirectionAllowances[msg.sender] = _to;
emit InterestRedirectionAllowanceChanged( emit InterestRedirectionAllowanceChanged(msg.sender, _to);
msg.sender,
_to
);
} }
/** /**
@ -212,14 +205,12 @@ contract AToken is ERC20 {
* @param _amount the amount being redeemed * @param _amount the amount being redeemed
**/ **/
function redeem(uint256 _amount) external { function redeem(uint256 _amount) external {
require(_amount > 0, 'Amount to redeem needs to be > 0');
require(_amount > 0, "Amount to redeem needs to be > 0");
//cumulates the balance of the user //cumulates the balance of the user
(, (, uint256 currentBalance, uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(
uint256 currentBalance, msg.sender
uint256 balanceIncrease, );
uint256 index) = cumulateBalanceInternal(msg.sender);
uint256 amountToRedeem = _amount; uint256 amountToRedeem = _amount;
@ -228,15 +219,19 @@ contract AToken is ERC20 {
amountToRedeem = currentBalance; amountToRedeem = currentBalance;
} }
require(amountToRedeem <= currentBalance, "User cannot redeem more than the available balance"); require(amountToRedeem <= currentBalance, 'User cannot redeem more than the available balance');
//check that the user is allowed to redeem the amount //check that the user is allowed to redeem the amount
require(isTransferAllowed(msg.sender, amountToRedeem), "Transfer cannot be allowed."); require(isTransferAllowed(msg.sender, amountToRedeem), 'Transfer cannot be allowed.');
//if the user is redirecting his interest towards someone else, //if the user is redirecting his interest towards someone else,
//we update the redirected balance of the redirection address by adding the accrued interest, //we update the redirected balance of the redirection address by adding the accrued interest,
//and removing the amount to redeem //and removing the amount to redeem
updateRedirectedBalanceOfRedirectionAddressInternal(msg.sender, balanceIncrease, amountToRedeem); updateRedirectedBalanceOfRedirectionAddressInternal(
msg.sender,
balanceIncrease,
amountToRedeem
);
// burns tokens equivalent to the amount requested // burns tokens equivalent to the amount requested
_burn(msg.sender, amountToRedeem); _burn(msg.sender, amountToRedeem);
@ -265,12 +260,8 @@ contract AToken is ERC20 {
* @param _amount the amount of tokens to mint * @param _amount the amount of tokens to mint
*/ */
function mintOnDeposit(address _account, uint256 _amount) external onlyLendingPool { function mintOnDeposit(address _account, uint256 _amount) external onlyLendingPool {
//cumulates the balance of the user //cumulates the balance of the user
(, (, , uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(_account);
,
uint256 balanceIncrease,
uint256 index) = cumulateBalanceInternal(_account);
//if the user is redirecting his interest towards someone else, //if the user is redirecting his interest towards someone else,
//we update the redirected balance of the redirection address by adding the accrued interest //we update the redirected balance of the redirection address by adding the accrued interest
@ -291,9 +282,10 @@ contract AToken is ERC20 {
* @param _value the amount to burn * @param _value the amount to burn
**/ **/
function burnOnLiquidation(address _account, uint256 _value) external onlyLendingPool { function burnOnLiquidation(address _account, uint256 _value) external onlyLendingPool {
//cumulates the balance of the user being liquidated //cumulates the balance of the user being liquidated
(,uint256 accountBalance,uint256 balanceIncrease,uint256 index) = cumulateBalanceInternal(_account); (, uint256 accountBalance, uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(
_account
);
//adds the accrued interest and substracts the burned amount to //adds the accrued interest and substracts the burned amount to
//the redirected balance //the redirected balance
@ -318,8 +310,11 @@ contract AToken is ERC20 {
* @param _to the destination address * @param _to the destination address
* @param _value the amount to transfer * @param _value the amount to transfer
**/ **/
function transferOnLiquidation(address _from, address _to, uint256 _value) external onlyLendingPool { function transferOnLiquidation(
address _from,
address _to,
uint256 _value
) external onlyLendingPool {
//being a normal transfer, the Transfer() and BalanceTransfer() are emitted //being a normal transfer, the Transfer() and BalanceTransfer() are emitted
//so no need to emit a specific event here //so no need to emit a specific event here
executeTransferInternal(_from, _to, _value); executeTransferInternal(_from, _to, _value);
@ -332,7 +327,6 @@ contract AToken is ERC20 {
* @return the total balance of the user * @return the total balance of the user
**/ **/
function balanceOf(address _user) public override view returns (uint256) { function balanceOf(address _user) public override view returns (uint256) {
//current principal balance of the user //current principal balance of the user
uint256 currentPrincipalBalance = super.balanceOf(_user); uint256 currentPrincipalBalance = super.balanceOf(_user);
//balance redirected by other users to _user for interest rate accrual //balance redirected by other users to _user for interest rate accrual
@ -345,25 +339,18 @@ contract AToken is ERC20 {
//the interest for himself //the interest for himself
if (interestRedirectionAddresses[_user] == address(0)) { if (interestRedirectionAddresses[_user] == address(0)) {
//accruing for himself means that both the principal balance and //accruing for himself means that both the principal balance and
//the redirected balance partecipate in the interest //the redirected balance partecipate in the interest
return calculateCumulatedBalanceInternal( return
_user, calculateCumulatedBalanceInternal(_user, currentPrincipalBalance.add(redirectedBalance))
currentPrincipalBalance.add(redirectedBalance)
)
.sub(redirectedBalance); .sub(redirectedBalance);
} } else {
else {
//if the user redirected the interest, then only the redirected //if the user redirected the interest, then only the redirected
//balance generates interest. In that case, the interest generated //balance generates interest. In that case, the interest generated
//by the redirected balance is added to the current principal balance. //by the redirected balance is added to the current principal balance.
return currentPrincipalBalance.add( return
calculateCumulatedBalanceInternal( currentPrincipalBalance.add(
_user, calculateCumulatedBalanceInternal(_user, redirectedBalance).sub(redirectedBalance)
redirectedBalance
)
.sub(redirectedBalance)
); );
} }
} }
@ -378,7 +365,6 @@ contract AToken is ERC20 {
return super.balanceOf(_user); return super.balanceOf(_user);
} }
/** /**
* @dev calculates the total supply of the specific aToken * @dev calculates the total supply of the specific aToken
* since the balance of every single user increases over time, the total supply * since the balance of every single user increases over time, the total supply
@ -386,20 +372,19 @@ contract AToken is ERC20 {
* @return the current total supply * @return the current total supply
**/ **/
function totalSupply() public override view returns (uint256) { function totalSupply() public override view returns (uint256) {
uint256 currentSupplyPrincipal = super.totalSupply(); uint256 currentSupplyPrincipal = super.totalSupply();
if (currentSupplyPrincipal == 0) { if (currentSupplyPrincipal == 0) {
return 0; return 0;
} }
return currentSupplyPrincipal return
currentSupplyPrincipal
.wadToRay() .wadToRay()
.rayMul(pool.getReserveNormalizedIncome(underlyingAssetAddress)) .rayMul(pool.getReserveNormalizedIncome(underlyingAssetAddress))
.rayToWad(); .rayToWad();
} }
/** /**
* @dev Used to validate transfers before actually executing them. * @dev Used to validate transfers before actually executing them.
* @param _user address of the user to check * @param _user address of the user to check
@ -419,7 +404,6 @@ contract AToken is ERC20 {
return userIndexes[_user]; return userIndexes[_user];
} }
/** /**
* @dev returns the address to which the interest is redirected * @dev returns the address to which the interest is redirected
* @param _user address of the user * @param _user address of the user
@ -447,8 +431,13 @@ contract AToken is ERC20 {
**/ **/
function cumulateBalanceInternal(address _user) function cumulateBalanceInternal(address _user)
internal internal
returns(uint256, uint256, uint256, uint256) { returns (
uint256,
uint256,
uint256,
uint256
)
{
uint256 previousPrincipalBalance = super.balanceOf(_user); uint256 previousPrincipalBalance = super.balanceOf(_user);
//calculate the accrued interest since the last accumulation //calculate the accrued interest since the last accumulation
@ -477,7 +466,6 @@ contract AToken is ERC20 {
uint256 _balanceToAdd, uint256 _balanceToAdd,
uint256 _balanceToRemove uint256 _balanceToRemove
) internal { ) internal {
address redirectionAddress = interestRedirectionAddresses[_user]; address redirectionAddress = interestRedirectionAddresses[_user];
//if there isn't any redirection, nothing to be done //if there isn't any redirection, nothing to be done
if (redirectionAddress == address(0)) { if (redirectionAddress == address(0)) {
@ -497,7 +485,8 @@ contract AToken is ERC20 {
address targetOfRedirectionAddress = interestRedirectionAddresses[redirectionAddress]; address targetOfRedirectionAddress = interestRedirectionAddresses[redirectionAddress];
if (targetOfRedirectionAddress != address(0)) { if (targetOfRedirectionAddress != address(0)) {
redirectedBalances[targetOfRedirectionAddress] = redirectedBalances[targetOfRedirectionAddress].add(balanceIncrease); redirectedBalances[targetOfRedirectionAddress] = redirectedBalances[targetOfRedirectionAddress]
.add(balanceIncrease);
} }
emit RedirectedBalanceUpdated( emit RedirectedBalanceUpdated(
@ -515,11 +504,13 @@ contract AToken is ERC20 {
* @param _balance the balance on which the interest is calculated * @param _balance the balance on which the interest is calculated
* @return the interest rate accrued * @return the interest rate accrued
**/ **/
function calculateCumulatedBalanceInternal( function calculateCumulatedBalanceInternal(address _user, uint256 _balance)
address _user, internal
uint256 _balance view
) internal view returns (uint256) { returns (uint256)
return _balance {
return
_balance
.wadToRay() .wadToRay()
.rayMul(pool.getReserveNormalizedIncome(underlyingAssetAddress)) .rayMul(pool.getReserveNormalizedIncome(underlyingAssetAddress))
.rayDiv(userIndexes[_user]) .rayDiv(userIndexes[_user])
@ -538,22 +529,18 @@ contract AToken is ERC20 {
address _to, address _to,
uint256 _value uint256 _value
) internal { ) internal {
require(_value > 0, 'Transferred amount needs to be greater than zero');
require(_value > 0, "Transferred amount needs to be greater than zero");
//cumulate the balance of the sender //cumulate the balance of the sender
(, (
,
uint256 fromBalance, uint256 fromBalance,
uint256 fromBalanceIncrease, uint256 fromBalanceIncrease,
uint256 fromIndex uint256 fromIndex
) = cumulateBalanceInternal(_from); ) = cumulateBalanceInternal(_from);
//cumulate the balance of the receiver //cumulate the balance of the receiver
(, (, , uint256 toBalanceIncrease, uint256 toIndex) = cumulateBalanceInternal(_to);
,
uint256 toBalanceIncrease,
uint256 toIndex
) = cumulateBalanceInternal(_to);
//if the sender is redirecting his interest towards someone else, //if the sender is redirecting his interest towards someone else,
//adds to the redirected balance the accrued interest and removes the amount //adds to the redirected balance the accrued interest and removes the amount
@ -591,22 +578,20 @@ contract AToken is ERC20 {
* @param _from the address from which transfer the aTokens * @param _from the address from which transfer the aTokens
* @param _to the destination address * @param _to the destination address
**/ **/
function redirectInterestStreamInternal( function redirectInterestStreamInternal(address _from, address _to) internal {
address _from,
address _to
) internal {
address currentRedirectionAddress = interestRedirectionAddresses[_from]; address currentRedirectionAddress = interestRedirectionAddresses[_from];
require(_to != currentRedirectionAddress, "Interest is already redirected to the user"); require(_to != currentRedirectionAddress, 'Interest is already redirected to the user');
//accumulates the accrued interest to the principal //accumulates the accrued interest to the principal
(uint256 previousPrincipalBalance, (
uint256 previousPrincipalBalance,
uint256 fromBalance, uint256 fromBalance,
uint256 balanceIncrease, uint256 balanceIncrease,
uint256 fromIndex) = cumulateBalanceInternal(_from); uint256 fromIndex
) = cumulateBalanceInternal(_from);
require(fromBalance > 0, "Interest stream can only be redirected if there is a valid balance"); require(fromBalance > 0, 'Interest stream can only be redirected if there is a valid balance');
//if the user is already redirecting the interest to someone, before changing //if the user is already redirecting the interest to someone, before changing
//the redirection address we substract the redirected balance of the previous //the redirection address we substract the redirected balance of the previous
@ -619,13 +604,7 @@ contract AToken is ERC20 {
//we simply set to 0 the interest redirection address //we simply set to 0 the interest redirection address
if (_to == _from) { if (_to == _from) {
interestRedirectionAddresses[_from] = address(0); interestRedirectionAddresses[_from] = address(0);
emit InterestStreamRedirected( emit InterestStreamRedirected(_from, address(0), fromBalance, balanceIncrease, fromIndex);
_from,
address(0),
fromBalance,
balanceIncrease,
fromIndex
);
return; return;
} }
@ -635,13 +614,7 @@ contract AToken is ERC20 {
//adds the user balance to the redirected balance of the destination //adds the user balance to the redirected balance of the destination
updateRedirectedBalanceOfRedirectionAddressInternal(_from, fromBalance, 0); updateRedirectedBalanceOfRedirectionAddressInternal(_from, fromBalance, 0);
emit InterestStreamRedirected( emit InterestStreamRedirected(_from, _to, fromBalance, balanceIncrease, fromIndex);
_from,
_to,
fromBalance,
balanceIncrease,
fromIndex
);
} }
/** /**
@ -651,7 +624,6 @@ contract AToken is ERC20 {
* @return true if the user index has also been reset, false otherwise. useful to emit the proper user index value * @return true if the user index has also been reset, false otherwise. useful to emit the proper user index value
**/ **/
function resetDataOnZeroBalanceInternal(address _user) internal returns (bool) { function resetDataOnZeroBalanceInternal(address _user) internal returns (bool) {
//if the user has 0 principal balance, the interest stream redirection gets reset //if the user has 0 principal balance, the interest stream redirection gets reset
interestRedirectionAddresses[_user] = address(0); interestRedirectionAddresses[_user] = address(0);
@ -662,9 +634,20 @@ contract AToken is ERC20 {
if (redirectedBalances[_user] == 0) { if (redirectedBalances[_user] == 0) {
userIndexes[_user] = 0; userIndexes[_user] = 0;
return true; return true;
} } else {
else{
return false; return false;
} }
} }
function transferUnderlyingTo(address _user, uint256 _amount)
external
onlyLendingPool
returns (uint256)
{
ERC20(underlyingAssetAddress).universalTransfer(_user, _amount);
}
receive() external payable{
require(ERC20(underlyingAssetAddress).isETH(), "Transfers are only allowed if the underlying asset is ETH");
}
} }