diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 517dc997..30a69084 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -108,8 +108,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.index, true); } - //minting AToken to user 1:1 with the specific exchange rate - IAToken(aToken).mint(onBehalfOf, amount); + IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); //transfer to the aToken contract IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); @@ -155,7 +154,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { _usersConfig[msg.sender].setUsingAsCollateral(reserve.index, false); } - IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw); + IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex); emit Withdraw(asset, msg.sender, amount); } @@ -616,7 +615,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { reserve.currentVariableBorrowRate, reserve.currentStableBorrowRate, IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(), - reserve.lastLiquidityIndex, + reserve.liquidityIndex, reserve.lastVariableBorrowIndex, reserve.lastUpdateTimestamp ); diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index 6c7b340b..8640761b 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -271,7 +271,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable { ); //burn the equivalent amount of atoken - vars.collateralAtoken.burn(user, msg.sender, vars.maxCollateralToLiquidate); + vars.collateralAtoken.burn(user, msg.sender, vars.maxCollateralToLiquidate, collateralReserve.liquidityIndex); } //transfers the principal currency to the aToken @@ -388,8 +388,10 @@ contract LendingPoolLiquidationManager is VersionedInitializable { if (vars.principalAmountNeeded < vars.actualAmountToLiquidate) { vars.actualAmountToLiquidate = vars.principalAmountNeeded; } + //updating collateral reserve indexes + collateralReserve.updateCumulativeIndexesAndTimestamp(); - vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate); + vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate, collateralReserve.liquidityIndex); if (vars.userCollateralBalance == vars.maxCollateralToLiquidate) { usersConfig[user].setUsingAsCollateral(collateralReserve.index, false); @@ -425,7 +427,6 @@ contract LendingPoolLiquidationManager is VersionedInitializable { } //updating collateral reserve - collateralReserve.updateCumulativeIndexesAndTimestamp(); collateralReserve.updateInterestRates( collateral, address(vars.collateralAtoken), diff --git a/contracts/libraries/helpers/Errors.sol b/contracts/libraries/helpers/Errors.sol index b7598eec..60fd2da3 100644 --- a/contracts/libraries/helpers/Errors.sol +++ b/contracts/libraries/helpers/Errors.sol @@ -48,8 +48,9 @@ library Errors { string public constant TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero' string public constant INTEREST_ALREADY_REDIRECTED = '32'; // 'Interest is already redirected to the user' string public constant NO_VALID_BALANCE_FOR_REDIRECTION = '33'; // 'Interest stream can only be redirected if there is a valid balance' - - // require error messages - ReserveLogic + string public constant INVALID_ATOKEN_BALANCE = '52'; // balance on burning is invalid + + // require error messages - ReserveLogic string public constant RESERVE_ALREADY_INITIALIZED = '34'; // 'Reserve has already been initialized' string public constant LIQUIDITY_INDEX_OVERFLOW = '47'; // Liquidity index overflows uint128 string public constant VARIABLE_BORROW_INDEX_OVERFLOW = '48'; // Variable borrow index overflows uint128 diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index 06d08499..01684225 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -56,7 +56,7 @@ library ReserveLogic { address variableDebtTokenAddress; address interestRateStrategyAddress; //the liquidity index. Expressed in ray - uint128 lastLiquidityIndex; + uint128 liquidityIndex; //the current supply rate. Expressed in ray uint128 currentLiquidityRate; //the current variable borrow rate. Expressed in ray @@ -83,12 +83,12 @@ library ReserveLogic { //solium-disable-next-line if (timestamp == uint40(block.timestamp)) { //if the index was updated in the same block, no need to perform any calculation - return reserve.lastLiquidityIndex; + return reserve.liquidityIndex; } uint256 cumulated = MathUtils .calculateLinearInterest(reserve.currentLiquidityRate, timestamp) - .rayMul(reserve.lastLiquidityIndex); + .rayMul(reserve.liquidityIndex); return cumulated; } @@ -131,10 +131,10 @@ library ReserveLogic { currentLiquidityRate, lastUpdateTimestamp ); - uint256 index = cumulatedLiquidityInterest.rayMul(reserve.lastLiquidityIndex); + uint256 index = cumulatedLiquidityInterest.rayMul(reserve.liquidityIndex); require(index < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW); - reserve.lastLiquidityIndex = uint128(index); + reserve.liquidityIndex = uint128(index); //as the liquidity rate might come only from stable rate loans, we need to ensure //that there is actual variable debt before accumulating @@ -169,10 +169,10 @@ library ReserveLogic { uint256 result = amountToLiquidityRatio.add(WadRayMath.ray()); - result = result.rayMul(reserve.lastLiquidityIndex); + result = result.rayMul(reserve.liquidityIndex); require(result < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW); - reserve.lastLiquidityIndex = uint128(result); + reserve.liquidityIndex = uint128(result); } /** @@ -189,9 +189,9 @@ library ReserveLogic { address interestRateStrategyAddress ) external { require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED); - if (reserve.lastLiquidityIndex == 0) { + if (reserve.liquidityIndex == 0) { //if the reserve has not been initialized yet - reserve.lastLiquidityIndex = uint128(WadRayMath.ray()); + reserve.liquidityIndex = uint128(WadRayMath.ray()); } if (reserve.lastVariableBorrowIndex == 0) { @@ -259,7 +259,7 @@ library ReserveLogic { vars.newStableRate, vars.currentAvgStableRate, vars.newVariableRate, - reserve.lastLiquidityIndex, + reserve.liquidityIndex, reserve.lastVariableBorrowIndex ); } diff --git a/contracts/tokenization/AToken.sol b/contracts/tokenization/AToken.sol index 53870c24..862cdda1 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -25,23 +25,15 @@ contract AToken is VersionedInitializable, ERC20, IAToken { uint256 public constant UINT_MAX_VALUE = uint256(-1); address public immutable UNDERLYING_ASSET_ADDRESS; + LendingPool public immutable POOL; - mapping(address => uint256) private _userIndexes; - mapping(address => address) private _interestRedirectionAddresses; - mapping(address => uint256) private _redirectedBalances; - mapping(address => address) private _interestRedirectionAllowances; + mapping(address => uint256) private _scaledRedirectedBalances; - LendingPool private immutable _pool; uint256 public constant ATOKEN_REVISION = 0x1; modifier onlyLendingPool { - require(msg.sender == address(_pool), Errors.CALLER_MUST_BE_LENDING_POOL); - _; - } - - modifier whenTransferAllowed(address from, uint256 amount) { - require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED); + require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL); _; } @@ -51,7 +43,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken { string memory tokenName, string memory tokenSymbol ) public ERC20(tokenName, tokenSymbol, 18) { - _pool = pool; + POOL = pool; UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; } @@ -69,56 +61,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken { _setDecimals(underlyingAssetDecimals); } - /** - * @notice ERC20 implementation internal function backing transfer() and transferFrom() - * @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) { - _executeTransfer(from, to, amount); - } - - /** - * @dev redirects the interest generated to a target address. - * when the interest is redirected, the user balance is added to - * the recepient redirected balance. - * @param to the address to which the interest will be redirected - **/ - function redirectInterestStream(address to) external override { - _redirectInterestStream(msg.sender, to); - } - - /** - * @dev redirects the interest generated by from to a target address. - * when the interest is redirected, the user balance is added to - * the recepient redirected balance. The caller needs to have allowance on - * the interest redirection to be able to execute the function. - * @param from the address of the user whom interest is being redirected - * @param to the address to which the interest will be redirected - **/ - function redirectInterestStreamOf(address from, address to) external override { - require( - msg.sender == _interestRedirectionAllowances[from], - Errors.INTEREST_REDIRECTION_NOT_ALLOWED - ); - _redirectInterestStream(from, to); - } - - /** - * @dev gives allowance to an address to execute the interest redirection - * on behalf of the caller. - * @param to the address to which the interest will be redirected. Pass address(0) to reset - * the allowance. - **/ - function allowInterestRedirectionTo(address to) external override { - require(to != msg.sender, Errors.CANNOT_GIVE_ALLOWANCE_TO_HIMSELF); - _interestRedirectionAllowances[msg.sender] = to; - emit InterestRedirectionAllowanceChanged(msg.sender, to); - } - /** * @dev burns the aTokens and sends the equivalent amount of underlying to the target. * only lending pools can call this function @@ -126,37 +68,24 @@ contract AToken is VersionedInitializable, ERC20, IAToken { **/ function burn( address user, - address underlyingTarget, - uint256 amount + address receiverOfUnderlying, + uint256 amount, + uint256 index ) external override onlyLendingPool { - //cumulates the balance of the user - (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(user); - //if the user is redirecting his interest towards someone else, - //we update the redirected balance of the redirection address by adding the accrued interest, - //and removing the amount to redeem - _updateRedirectedBalanceOfRedirectionAddress(user, balanceIncrease, amount); + uint256 currentBalance = balanceOf(user); - if (balanceIncrease > amount) { - _mint(user, balanceIncrease.sub(amount)); - } else { - _burn(user, amount.sub(balanceIncrease)); - } + require(amount <= currentBalance, Errors.INVALID_ATOKEN_BALANCE); - uint256 userIndex = 0; + uint256 scaledAmount = amount.rayDiv(index); - //reset the user data if the remaining balance is 0 - if (currentBalance.sub(amount) == 0) { - _resetDataOnZeroBalance(user); - } else { - //updates the user index - userIndex = _userIndexes[user] = _pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); - } + _burn(user, scaledAmount); //transfers the underlying to the target - ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(underlyingTarget, amount); + ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); - emit Burn(msg.sender, underlyingTarget, amount, balanceIncrease, userIndex); + + emit Burn(msg.sender, receiverOfUnderlying, amount, index); } /** @@ -165,22 +94,15 @@ contract AToken is VersionedInitializable, ERC20, IAToken { * @param user the address receiving the minted tokens * @param amount the amount of tokens to mint */ - function mint(address user, uint256 amount) external override onlyLendingPool { - //cumulates the balance of the user - (, , uint256 balanceIncrease) = _calculateBalanceIncrease(user); + function mint(address user, uint256 amount, uint256 index) external override onlyLendingPool { - //updates the user index - uint256 index = _userIndexes[user] = _pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); - - //if the user is redirecting his interest towards someone else, - //we update the redirected balance of the redirection address by adding the accrued interest - //and the amount deposited - _updateRedirectedBalanceOfRedirectionAddress(user, balanceIncrease.add(amount), 0); + uint256 scaledAmount = amount.rayDiv(index); + //mint an equivalent amount of tokens to cover the new deposit - _mint(user, amount.add(balanceIncrease)); + _mint(user,scaledAmount); - emit Mint(user, amount, balanceIncrease, index); + emit Mint(user, amount, index); } /** @@ -197,52 +119,28 @@ contract AToken is VersionedInitializable, ERC20, IAToken { ) external override onlyLendingPool { //being a normal transfer, the Transfer() and BalanceTransfer() are emitted //so no need to emit a specific event here - _executeTransfer(from, to, value); + _transfer(from, to, value, false); } /** * @dev calculates the balance of the user, which is the - * principal balance + interest generated by the principal balance + interest generated by the redirected balance + * principal balance + interest generated by the principal balance * @param user the user for which the balance is being calculated * @return the total balance of the user **/ function balanceOf(address user) public override(ERC20, IERC20) view returns (uint256) { - //current principal balance of the user - uint256 currentPrincipalBalance = super.balanceOf(user); - //balance redirected by other users to user for interest rate accrual - uint256 redirectedBalance = _redirectedBalances[user]; - if (currentPrincipalBalance == 0 && redirectedBalance == 0) { - return 0; - } - //if the user is not redirecting the interest to anybody, accrues - //the interest for himself + return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); - if (_interestRedirectionAddresses[user] == address(0)) { - //accruing for himself means that both the principal balance and - //the redirected balance partecipate in the interest - return - _calculateCumulatedBalance(user, currentPrincipalBalance.add(redirectedBalance)).sub( - redirectedBalance - ); - } else { - //if the user redirected the interest, then only the redirected - //balance generates interest. In that case, the interest generated - //by the redirected balance is added to the current principal balance. - return - currentPrincipalBalance.add( - _calculateCumulatedBalance(user, redirectedBalance).sub(redirectedBalance) - ); - } } /** - * @dev returns the principal balance of the user. The principal balance is the last - * updated stored balance, which does not consider the perpetually accruing interest. + * @dev returns the scaled balance of the user. The scaled balance is the sum of all the + * updated stored balance divided the reserve index at the moment of the update * @param user the address of the user - * @return the principal balance of the user + * @return the scaled balance of the user **/ - function principalBalanceOf(address user) external override view returns (uint256) { + function scaledBalanceOf(address user) external override view returns (uint256) { return super.balanceOf(user); } @@ -253,17 +151,15 @@ contract AToken is VersionedInitializable, ERC20, IAToken { * @return the current total supply **/ function totalSupply() public override(ERC20, IERC20) view returns (uint256) { - uint256 currentSupplyPrincipal = super.totalSupply(); + uint256 currentSupplyScaled = super.totalSupply(); - if (currentSupplyPrincipal == 0) { + if (currentSupplyScaled == 0) { return 0; } return - currentSupplyPrincipal - .wadToRay() - .rayMul(_pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)) - .rayToWad(); + currentSupplyScaled + .rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); } /** @@ -273,284 +169,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken { * @return true if the user can transfer amount, false otherwise **/ function isTransferAllowed(address user, uint256 amount) public override view returns (bool) { - return _pool.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount); + return POOL.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount); } /** - * @dev returns the last index of the user, used to calculate the balance of the user - * @param user address of the user - * @return the last user index - **/ - function getUserIndex(address user) external override view returns (uint256) { - return _userIndexes[user]; - } - - /** - * @dev returns the address to which the interest is redirected - * @param user address of the user - * @return 0 if there is no redirection, an address otherwise - **/ - function getInterestRedirectionAddress(address user) external override view returns (address) { - return _interestRedirectionAddresses[user]; - } - - /** - * @dev returns the redirected balance of the user. The redirected balance is the balance - * redirected by other accounts to the user, that is accrueing interest for him. - * @param user address of the user - * @return the total redirected balance - **/ - function getRedirectedBalance(address user) external override view returns (uint256) { - return _redirectedBalances[user]; - } - - /** - * @dev calculates the increase in balance since the last user action - * @param user the address of the user - * @return the last user principal balance, the current balance and the balance increase - **/ - function _calculateBalanceIncrease(address user) - internal - view - returns ( - uint256, - uint256, - uint256 - ) - { - uint256 currentBalance = balanceOf(user); - uint256 balanceIncrease = 0; - uint256 previousBalance = 0; - - if (currentBalance != 0) { - previousBalance = super.balanceOf(user); - //calculate the accrued interest since the last accumulation - balanceIncrease = currentBalance.sub(previousBalance); - } - - return (previousBalance, currentBalance, balanceIncrease); - } - - /** - * @dev accumulates the accrued interest of the user to the principal balance - * @param user the address of the user for which the interest is being accumulated - * @return the previous principal balance, the new principal balance, the balance increase - * and the new user index - **/ - function _cumulateBalance(address user) - internal - returns ( - uint256, - uint256, - uint256, - uint256 - ) - { - ( - uint256 previousBalance, - uint256 currentBalance, - uint256 balanceIncrease - ) = _calculateBalanceIncrease(user); - - _mint(user, balanceIncrease); - - //updates the user index - uint256 index = _userIndexes[user] = _pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); - - return (previousBalance, currentBalance, balanceIncrease, index); - } - - /** - * @dev updates the redirected balance of the user. If the user is not redirecting his - * interest, nothing is executed. - * @param user the address of the user for which the interest is being accumulated - * @param balanceToAdd the amount to add to the redirected balance - * @param balanceToRemove the amount to remove from the redirected balance - **/ - function _updateRedirectedBalanceOfRedirectionAddress( - address user, - uint256 balanceToAdd, - uint256 balanceToRemove - ) internal { - address redirectionAddress = _interestRedirectionAddresses[user]; - //if there isn't any redirection, nothing to be done - if (redirectionAddress == address(0)) { - return; - } - - //compound balances of the redirected address - (, , uint256 balanceIncrease, uint256 index) = _cumulateBalance(redirectionAddress); - - //updating the redirected balance - _redirectedBalances[redirectionAddress] = _redirectedBalances[redirectionAddress] - .add(balanceToAdd) - .sub(balanceToRemove); - - //if the interest of redirectionAddress is also being redirected, we need to update - //the redirected balance of the redirection target by adding the balance increase - address targetOfRedirectionAddress = _interestRedirectionAddresses[redirectionAddress]; - - // if the redirection address is also redirecting the interest, we accumulate his balance - // and update his chain of redirection - if (targetOfRedirectionAddress != address(0)) { - _updateRedirectedBalanceOfRedirectionAddress(redirectionAddress, balanceIncrease, 0); - } - - emit RedirectedBalanceUpdated( - redirectionAddress, - balanceIncrease, - index, - balanceToAdd, - balanceToRemove - ); - } - - /** - * @dev calculate the interest accrued by user on a specific balance - * @param user the address of the user for which the interest is being accumulated - * @param balance the balance on which the interest is calculated - * @return the interest rate accrued - **/ - function _calculateCumulatedBalance(address user, uint256 balance) - internal - view - returns (uint256) - { - return - balance - .wadToRay() - .rayMul(_pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)) - .rayDiv(_userIndexes[user]) - .rayToWad(); - } - - /** - * @dev executes the transfer of aTokens, invoked by both _transfer() and - * transferOnLiquidation() - * @param from the address from which transfer the aTokens - * @param to the destination address - * @param value the amount to transfer - **/ - function _executeTransfer( - address from, - address to, - uint256 value - ) internal { - require(value > 0, Errors.TRANSFER_AMOUNT_NOT_GT_0); - - //cumulate the balance of the sender - (, uint256 fromBalance, uint256 fromBalanceIncrease, uint256 fromIndex) = _cumulateBalance( - from - ); - - //cumulate the balance of the receiver - (, , uint256 toBalanceIncrease, uint256 toIndex) = _cumulateBalance(to); - - //if the sender is redirecting his interest towards someone else, - //adds to the redirected balance the accrued interest and removes the amount - //being transferred - _updateRedirectedBalanceOfRedirectionAddress(from, fromBalanceIncrease, value); - - //if the receiver is redirecting his interest towards someone else, - //adds to the redirected balance the accrued interest and the amount - //being transferred - _updateRedirectedBalanceOfRedirectionAddress(to, toBalanceIncrease.add(value), 0); - - //performs the transfer - super._transfer(from, to, value); - - bool fromIndexReset = false; - //reset the user data if the remaining balance is 0 - if (fromBalance.sub(value) == 0 && from != to) { - fromIndexReset = _resetDataOnZeroBalance(from); - } - - emit BalanceTransfer( - from, - to, - value, - fromBalanceIncrease, - toBalanceIncrease, - fromIndexReset ? 0 : fromIndex, - toIndex - ); - } - - /** - * @dev executes the redirection of the interest from one address to another. - * immediately after redirection, the destination address will start to accrue interest. - * @param from the address from which transfer the aTokens - * @param to the destination address - **/ - function _redirectInterestStream(address from, address to) internal { - address currentRedirectionAddress = _interestRedirectionAddresses[from]; - - require(to != currentRedirectionAddress, Errors.INTEREST_ALREADY_REDIRECTED); - - //accumulates the accrued interest to the principal - ( - uint256 previousPrincipalBalance, - uint256 fromBalance, - uint256 balanceIncrease, - uint256 fromIndex - ) = _cumulateBalance(from); - - require(fromBalance > 0, Errors.NO_VALID_BALANCE_FOR_REDIRECTION); - - //if the user is already redirecting the interest to someone, before changing - //the redirection address we substract the redirected balance of the previous - //recipient - if (currentRedirectionAddress != address(0)) { - _updateRedirectedBalanceOfRedirectionAddress(from, 0, previousPrincipalBalance); - } - - //if the user is redirecting the interest back to himself, - //we simply set to 0 the interest redirection address - if (to == from) { - _interestRedirectionAddresses[from] = address(0); - emit InterestStreamRedirected(from, address(0), fromBalance, balanceIncrease, fromIndex); - return; - } - - //first set the redirection address to the new recipient - _interestRedirectionAddresses[from] = to; - - //adds the user balance to the redirected balance of the destination - _updateRedirectedBalanceOfRedirectionAddress(from, fromBalance, 0); - - emit InterestStreamRedirected(from, to, fromBalance, balanceIncrease, fromIndex); - } - - /** - * @dev function to reset the interest stream redirection and the user index, if the - * user has no balance left. - * @param user the address of the user - * @return true if the user index has also been reset, false otherwise. useful to emit the proper user index value - **/ - function _resetDataOnZeroBalance(address user) internal returns (bool) { - //if the user has 0 principal balance, the interest stream redirection gets reset - _interestRedirectionAddresses[user] = address(0); - - //emits a InterestStreamRedirected event to notify that the redirection has been reset - emit InterestStreamRedirected(user, address(0), 0, 0, 0); - - //if the redirected balance is also 0, we clear up the user index - if (_redirectedBalances[user] == 0) { - _userIndexes[user] = 0; - return true; - } else { - return false; - } - } - - /** - * @dev transfers the underlying asset to the target. Used by the lendingpool to transfer - * assets in borrow(), redeem() and flashLoan() - * @param target the target of the transfer - * @param amount the amount to transfer - * @return the amount transferred - **/ - + * @dev transfers the underlying asset to the target. Used by the lendingpool to transfer + * assets in borrow(), redeem() and flashLoan() + * @param target the target of the transfer + * @param amount the amount to transfer + * @return the amount transferred + **/ function transferUnderlyingTo(address target, uint256 amount) external override @@ -561,6 +189,34 @@ contract AToken is VersionedInitializable, ERC20, IAToken { return amount; } + function _transfer( + address from, + address to, + uint256 amount, + bool validate + ) internal { + if(validate){ + require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED); + } + + uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); + + uint256 scaledAmount = amount.rayDiv(index); + + super._transfer(from, to, scaledAmount); + + emit BalanceTransfer(from, to, amount, index); + + } + + function _transfer( + address from, + address to, + uint256 amount + ) internal override { + + _transfer(from, to, amount, true); + } /** * @dev aTokens should not receive ETH **/ diff --git a/contracts/tokenization/interfaces/IAToken.sol b/contracts/tokenization/interfaces/IAToken.sol index 65ad0cfa..3aa5e83d 100644 --- a/contracts/tokenization/interfaces/IAToken.sol +++ b/contracts/tokenization/interfaces/IAToken.sol @@ -8,115 +8,48 @@ interface IAToken is IERC20 { * @dev emitted after aTokens are burned * @param from the address performing the redeem * @param value the amount to be redeemed - * @param fromBalanceIncrease the cumulated balance since the last update of the user - * @param fromIndex the last index of the user + * @param index the last index of the reserve **/ event Burn( address indexed from, address indexed target, uint256 value, - uint256 fromBalanceIncrease, - uint256 fromIndex + uint256 index ); /** * @dev emitted after the mint action * @param from the address performing the mint * @param value the amount to be minted - * @param fromBalanceIncrease the cumulated balance since the last update of the user - * @param fromIndex the last index of the user + * @param index the last index of the reserve **/ - event Mint(address indexed from, uint256 value, uint256 fromBalanceIncrease, uint256 fromIndex); + event Mint(address indexed from, uint256 value, uint256 index); /** * @dev emitted during the transfer action * @param from the address from which the tokens are being transferred * @param to the adress of the destination * @param value the amount to be minted - * @param fromBalanceIncrease the cumulated balance since the last update of the user - * @param toBalanceIncrease the cumulated balance since the last update of the destination - * @param fromIndex the last index of the user - * @param toIndex the last index of the liquidator + * @param index the last index of the reserve **/ event BalanceTransfer( address indexed from, address indexed to, uint256 value, - uint256 fromBalanceIncrease, - uint256 toBalanceIncrease, - uint256 fromIndex, - uint256 toIndex + uint256 index ); - /** - * @dev emitted when the accumulation of the interest - * by an user is redirected to another user - * @param from the address from which the interest is being redirected - * @param to the adress of the destination - * @param fromBalanceIncrease the cumulated balance since the last update of the user - * @param fromIndex the last index of the user - **/ - event InterestStreamRedirected( - address indexed from, - address indexed to, - uint256 redirectedBalance, - uint256 fromBalanceIncrease, - uint256 fromIndex - ); - - /** - * @dev emitted when the redirected balance of an user is being updated - * @param targetAddress the address of which the balance is being updated - * @param targetBalanceIncrease the cumulated balance since the last update of the target - * @param targetIndex the last index of the user - * @param redirectedBalanceAdded the redirected balance being added - * @param redirectedBalanceRemoved the redirected balance being removed - **/ - event RedirectedBalanceUpdated( - address indexed targetAddress, - uint256 targetBalanceIncrease, - uint256 targetIndex, - uint256 redirectedBalanceAdded, - uint256 redirectedBalanceRemoved - ); - - event InterestRedirectionAllowanceChanged(address indexed from, address indexed to); - - /** - * @dev redirects the interest generated to a target address. - * when the interest is redirected, the user balance is added to - * the recepient redirected balance. - * @param to the address to which the interest will be redirected - **/ - function redirectInterestStream(address to) external; - - /** - * @dev redirects the interest generated by from to a target address. - * when the interest is redirected, the user balance is added to - * the recepient redirected balance. The caller needs to have allowance on - * the interest redirection to be able to execute the function. - * @param from the address of the user whom interest is being redirected - * @param to the address to which the interest will be redirected - **/ - function redirectInterestStreamOf(address from, address to) external; - - /** - * @dev gives allowance to an address to execute the interest redirection - * on behalf of the caller. - * @param to the address to which the interest will be redirected. Pass address(0) to reset - * the allowance. - **/ - function allowInterestRedirectionTo(address to) external; - /** * @dev burns the aTokens and sends the equivalent amount of underlying to the target. * only lending pools can call this function * @param amount the amount being burned + * @param index the liquidity index **/ function burn( address user, address underlyingTarget, - uint256 amount + uint256 amount, + uint256 index ) external; /** @@ -124,8 +57,9 @@ interface IAToken is IERC20 { * only lending pools can call this function * @param user the address receiving the minted tokens * @param amount the amount of tokens to mint + * @param index the liquidity index */ - function mint(address user, uint256 amount) external; + function mint(address user, uint256 amount, uint256 index) external; /** * @dev transfers tokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken @@ -146,7 +80,7 @@ interface IAToken is IERC20 { * @param user the address of the user * @return the principal balance of the user **/ - function principalBalanceOf(address user) external view returns (uint256); + function scaledBalanceOf(address user) external view returns (uint256); /** * @dev Used to validate transfers before actually executing them. @@ -157,34 +91,11 @@ interface IAToken is IERC20 { function isTransferAllowed(address user, uint256 amount) external view returns (bool); /** - * @dev returns the last index of the user, used to calculate the balance of the user + * @dev transfer the amount of the underlying asset to the user * @param user address of the user - * @return the last user index - **/ - function getUserIndex(address user) external view returns (uint256); - - /** - * @dev returns the address to which the interest is redirected - * @param user address of the user - * @return 0 if there is no redirection, an address otherwise - **/ - function getInterestRedirectionAddress(address user) external view returns (address); - - /** - * @dev returns the redirected balance of the user. The redirected balance is the balance - * redirected by other accounts to the user, that is accrueing interest for him. - * @param user address of the user - * @return the total redirected balance - **/ - function getRedirectedBalance(address user) external view returns (uint256); - - /** - * @dev transfers the underlying asset to the target. Used by the lendingpool to transfer - * assets in borrow(), redeem() and flashLoan() - * @param target the target of the transfer * @param amount the amount to transfer - * @return the amount transferred + * @return the redirected balance index **/ - function transferUnderlyingTo(address target, uint256 amount) external returns (uint256); + function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); } diff --git a/deployed-contracts.json b/deployed-contracts.json index cb373f29..b9d54d60 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -5,7 +5,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xf8c6eB390cDc5C08717bC2268aa0c1169A9B5deE", + "address": "0x209bb253C2f894D3Cc53b9dC23d308Eb8593613A", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -15,7 +15,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x4a716924Dad0c0d0E558844F304548814e7089F1", + "address": "0xa2eDbC6b9E7EBA4b66f6A0B8Af3065CaC5611A6E", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -25,7 +25,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x798c5b4b62b1eA9D64955D6751B03075A003F123", + "address": "0x6424b49739C3fC1d390Cea7A6bafa5B32A7B47b8", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -53,7 +53,7 @@ "address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8" }, "localhost": { - "address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8" + "address": "0x8Be07B1e05bCB4091344ff2356D40EBf7e0c9b6E" } }, "LendingPoolDataProvider": { @@ -66,7 +66,7 @@ "address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e" }, "localhost": { - "address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e" + "address": "0xB44f879C781DfFF5E07aF7d338449E767Aa1c1d2" } }, "PriceOracle": { @@ -75,7 +75,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x1750499D05Ed1674d822430FB960d5F6731fDf64", + "address": "0x34c94f172B5eAcb53230AE62e41e1828B1a4B0F8", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -85,7 +85,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xEC1C93A9f6a9e18E97784c76aC52053587FcDB89", + "address": "0x01C5292e57aB25E38152ceE4A45C2038f233D531", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -95,7 +95,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x7B6C3e5486D9e6959441ab554A889099eed76290", + "address": "0x3D6bB48D5988B0D8B1d920cef50f59ED7d527F8c", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -105,7 +105,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xD83D2773a7873ae2b5f8Fb92097e20a8C64F691E", + "address": "0xC414d0323C57aF313F570A09c1D6e691F3C1c195", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -115,7 +115,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x626FdE749F9d499d3777320CAf29484B624ab84a", + "address": "0xbEed026d89A715F28b32135A6C3e3c060234f40d", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -169,7 +169,7 @@ "address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA" }, "localhost": { - "address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA" + "address": "0x045Da1BcEB75D2E0064a66Da1Ad606350CD9730A" } }, "WalletBalanceProvider": { @@ -178,7 +178,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2", + "address": "0xd54dbF2a2D88aFeCA7E288D43e16150b57C6bcd9", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -188,7 +188,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x11df1AF606b85226Ab9a8B1FDa90395298e7494F", + "address": "0xa17a59441D6c39D21F2Ff03e7b21f8d2BCAA6023", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -198,7 +198,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x8f9A92c125FFEb83d8eC808Cd9f8cb80084c1E37", + "address": "0x36B6f7e34d651DC7fd7EeEc53bf26594209915A8", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -208,7 +208,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xc4007844AE6bBe168cE8D692C86a7A4414FBcD26", + "address": "0x099E8e561f4cfCe0bbDaD063d973eBcf1A1d92B5", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -218,7 +218,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xAb768C858C33DfcB6651d1174AFb750433a87Be0", + "address": "0x8473D688815861639F6e4442F4433047c2F5571b", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -228,7 +228,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xA089557D64DAE4b4FcB65aB7C8A520AABb213e37", + "address": "0xBcf29d6fd8EB2d95b5Ad0Ffdd7ee5272cc49b26c", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -238,7 +238,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x20FAE2042b362E3FaB2806820b9A43CC116e2846", + "address": "0x6e77C7e0Fd33A53351335E768593ba56A6C43594", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -248,7 +248,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x8880F314112f15C2AfF674c3B27f9a44Ca86e4d0", + "address": "0x67a87Be0A08F955EfC629b1cdaaE1eaC48043E6a", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -258,7 +258,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xDcb10C2e15110Db4B02C0a1df459768E680ce245", + "address": "0xEDf104A35B3293F4BdB987be9D57EFe3b69C19c7", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -268,7 +268,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xfD408ec64Da574b1859814F810564f73ea2Ff003", + "address": "0xD8212F51E19A269B8fCc327BF91ede79e218EF17", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -278,7 +278,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x0006F7c3542BEE76Dd887f54eD22405Ac4ae905a", + "address": "0x1712cE132Cc5E2A5b63e6AF4Ee551070f7Bc4487", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -288,7 +288,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x6ca94a51c644eca3F9CA315bcC41CbA6940A66Eb", + "address": "0x6DC0873546006Ce00eC8AA9e97706125D75E3ab6", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -298,7 +298,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x6765291Cab755B980F377445eFd0F9F945CDA6C4", + "address": "0x133EA40EA9975d53D34417F9164a54A635110AE9", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -308,7 +308,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xa7dB4d25Fc525d19Fbda4E74AAF447B88420FbcB", + "address": "0xfe5E2ac37e1cf3f1dFA55De53780692846eD199A", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -318,7 +318,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x273D60904A8DBa3Ae6B20505c59902644124fF0E", + "address": "0xcf33a1c13D1599399B3581c3f3cf39e943013A73", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -328,7 +328,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xfc37dE87C1Ee39cc856782BF96fEdcB6FA5c5A7f", + "address": "0x6B4Fef015Ea5D2A23C5E5906b41f206c79E36316", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -338,7 +338,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x049228dFFEdf91ff224e9F96247aEBA700e3590c", + "address": "0x556f80053f02Ee04a4f13820AE7a30f787A7A630", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -348,7 +348,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xA410D1f3fEAF300842142Cd7AA1709D84944DCb7", + "address": "0x222C21A948139f016EBbd1979250194049b28473", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -358,7 +358,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x835973768750b3ED2D5c3EF5AdcD5eDb44d12aD4", + "address": "0xb0c6bAc77c65588a5c47d18545D3d265b0030B7e", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -368,7 +368,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x1181FC27dbF04B5105243E60BB1936c002e9d5C8", + "address": "0x9197B2985256CD8a0B41796ab5794D502012766c", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -378,7 +378,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x6F96975e2a0e1380b6e2e406BB33Ae96e4b6DB65", + "address": "0x89E72D113048277a670222d9bcACF4FA2c7b20A6", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -388,7 +388,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xc032930653da193EDE295B4DcE3DD093a695c3b3", + "address": "0x6f4689b37FCC44f24e8dE9Cf2B61f81E71fB9dc0", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -398,7 +398,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xb3363f4349b1160DbA55ec4D82fDe874A4123A2a", + "address": "0x77183A4B7c0375bA9A5090Ae68c32A5C567d77c6", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -408,7 +408,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xf8c6eB390cDc5C08717bC2268aa0c1169A9B5deE", + "address": "0x209bb253C2f894D3Cc53b9dC23d308Eb8593613A", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -417,7 +417,7 @@ "address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4" }, "localhost": { - "address": "0xDf73fC454FA018051D4a1509e63D11530A59DE10" + "address": "0xc47cF1C70618CB94e6B1D218468D3E16AE35Fff4" } }, "StableDebtToken": { @@ -426,7 +426,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6", + "address": "0x3888B5ac0089C12cDF21DD8B0234029f80645324", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -436,13 +436,13 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d", + "address": "0xB76Ea4df0263F99daf33765541b1933AD5bB4410", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, "AToken": { "localhost": { - "address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E", + "address": "0x4d39D68f5a2A43E79e7B3A859014cc4233D0EEA1", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "buidlerevm": { @@ -452,11 +452,11 @@ }, "MockAToken": { "buidlerevm": { - "address": "0x3bDA11B584dDff7F66E0cFe1da1562c92B45db60", + "address": "0x392E5355a0e88Bd394F717227c752670fb3a8020", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x1203D1b97BF6E546c00C45Cda035D3010ACe1180", + "address": "0x5efEaaE02a5E2BdA1aDAc7aad29D9B4bFFDD90E8", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, @@ -466,33 +466,36 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x2cc20bE530F92865c2ed8CeD0b020a11bFe62Fe7", + "address": "0xd334C51Ad3167554876f19F9575394F1cfbc96AF", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, "MockStableDebtToken": { "buidlerevm": { - "address": "0x392E5355a0e88Bd394F717227c752670fb3a8020", + "address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0x8733AfE8174BA7c04c6CD694bD673294079b7E10", + "address": "0x59A442D1DbAE607fD3cd97859dc14Ff400F7C2ed", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, "MockVariableDebtToken": { "buidlerevm": { - "address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460", + "address": "0xEBAB67ee3ef604D5c250A53b4b8fcbBC6ec3007C", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { - "address": "0xA8083d78B6ABC328b4d3B714F76F384eCC7147e1", + "address": "0xE8F349DB32821021520BBe11b7927279BC3BEC6b", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, "MockSwapAdapter": { "buidlerevm": { "address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2" + }, + "localhost": { + "address": "0xcB70821E9dDE40dc23E973280991A8cdBFD4EC2c" } } -} +} \ No newline at end of file diff --git a/test.log b/test.log index 64ab456c..73e89e84 100644 --- a/test.log +++ b/test.log @@ -2483,7 +2483,7 @@ gas used: 6117750 28) AToken: interest rate redirection negative test cases User 0 tries to redirect the interest to user 2 twice (revert expected): - AssertionError: expected '3000000004839170420641' to be almost equal or equal '3000002810040899373373' for property redirectionAddressRedirectedBalance + AssertionError: expected '3000000004839170420641' to be almost equal or equal '3000002810040899373373' for property redirectionAddressScaledRedirectedBalance + expected - actual -3000000004839170420641 @@ -2550,7 +2550,7 @@ gas used: 6117750 32) AToken: interest rate redirection User 0 deposits 1000 DAI, redirects interest to user 2, user 1 borrows 100 DAI. After one year, user 0 redirects interest back to himself, user 1 borrows another 100 DAI and after another year repays the whole amount. Users 0 and User 2 withdraw: - AssertionError: expected '1020673496610825275870' to be almost equal or equal '1020673496616475023834' for property redirectionAddressRedirectedBalance + AssertionError: expected '1020673496610825275870' to be almost equal or equal '1020673496616475023834' for property redirectionAddressScaledRedirectedBalance + expected - actual -1020673496610825275870 diff --git a/test/atoken-modifiers.spec.ts b/test/atoken-modifiers.spec.ts index 406d910a..7560970c 100644 --- a/test/atoken-modifiers.spec.ts +++ b/test/atoken-modifiers.spec.ts @@ -7,12 +7,12 @@ makeSuite('AToken: Modifiers', (testEnv: TestEnv) => { it('Tries to invoke mint not being the LendingPool', async () => { const {deployer, aDai} = testEnv; - await expect(aDai.mint(deployer.address, '1')).to.be.revertedWith(CALLER_MUST_BE_LENDING_POOL); + await expect(aDai.mint(deployer.address, '1', '1')).to.be.revertedWith(CALLER_MUST_BE_LENDING_POOL); }); it('Tries to invoke burn not being the LendingPool', async () => { const {deployer, aDai} = testEnv; - await expect(aDai.burn(deployer.address, deployer.address, '1')).to.be.revertedWith( + await expect(aDai.burn(deployer.address, deployer.address, '1', '1')).to.be.revertedWith( CALLER_MUST_BE_LENDING_POOL ); }); diff --git a/test/atoken-transfer.spec.ts b/test/atoken-transfer.spec.ts index bd113ce3..b75db650 100644 --- a/test/atoken-transfer.spec.ts +++ b/test/atoken-transfer.spec.ts @@ -49,50 +49,6 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { ); }); - it('User 1 redirects interest to user 2, transfers 500 DAI back to user 0', async () => { - const {users, aDai, dai} = testEnv; - await aDai.connect(users[1].signer).redirectInterestStream(users[2].address); - - const aDAIRedirected = await convertToCurrencyDecimals(dai.address, '1000'); - - const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '500'); - - const user2RedirectedBalanceBefore = await aDai.getRedirectedBalance(users[2].address); - expect(user2RedirectedBalanceBefore.toString()).to.be.equal( - aDAIRedirected, - INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER - ); - - await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer); - - const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address); - const user1RedirectionAddress = await aDai.getInterestRedirectionAddress(users[1].address); - - expect(user2RedirectedBalanceAfter.toString()).to.be.equal( - aDAItoTransfer, - INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER - ); - expect(user1RedirectionAddress.toString()).to.be.equal( - users[2].address, - INVALID_REDIRECTION_ADDRESS - ); - }); - - it('User 0 transfers back to user 1', async () => { - const {users, aDai, dai, weth} = testEnv; - const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '500'); - - await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer); - - const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address); - - const user1BalanceAfter = await aDai.balanceOf(users[1].address); - - expect(user2RedirectedBalanceAfter.toString()).to.be.equal( - user1BalanceAfter.toString(), - INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER - ); - }); it('User 0 deposits 1 WETH and user 1 tries to borrow, but the aTokens received as a transfer are not available as collateral (revert expected)', async () => { const {users, pool, weth} = testEnv; @@ -129,87 +85,4 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { ).to.be.revertedWith(TRANSFER_NOT_ALLOWED); }); - it('User 0 tries to transfer 0 balance (revert expected)', async () => { - const {users, pool, aDai, dai, weth} = testEnv; - await expect( - aDai.connect(users[0].signer).transfer(users[1].address, '0'), - TRANSFER_AMOUNT_NOT_GT_0 - ).to.be.revertedWith(TRANSFER_AMOUNT_NOT_GT_0); - }); - - it('User 1 repays the borrow, transfers aDAI back to user 0', async () => { - const {users, pool, aDai, dai, weth} = testEnv; - - await weth.connect(users[1].signer).mint(await convertToCurrencyDecimals(weth.address, '2')); - - await weth.connect(users[1].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); - - await pool - .connect(users[1].signer) - .repay(weth.address, MAX_UINT_AMOUNT, RateMode.Stable, users[1].address); - - const aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '1000'); - - await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer); - - const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address); - - const user1RedirectionAddress = await aDai.getInterestRedirectionAddress(users[1].address); - - expect(user2RedirectedBalanceAfter.toString()).to.be.equal( - '0', - INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER - ); - - expect(user1RedirectionAddress.toString()).to.be.equal( - ZERO_ADDRESS, - INVALID_REDIRECTION_ADDRESS - ); - }); - - it('User 0 redirects interest to user 2, transfers 500 aDAI to user 1. User 1 redirects to user 3. User 0 transfers another 100 aDAI', async () => { - const {users, pool, aDai, dai, weth} = testEnv; - - let aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '500'); - - await aDai.connect(users[0].signer).redirectInterestStream(users[2].address); - - await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer); - - await aDai.connect(users[1].signer).redirectInterestStream(users[3].address); - - aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '100'); - - await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer); - - const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address); - const user3RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[3].address); - - const expectedUser2Redirected = await convertToCurrencyDecimals(aDai.address, '400'); - const expectedUser3Redirected = await convertToCurrencyDecimals(aDai.address, '600'); - - expect(user2RedirectedBalanceAfter.toString()).to.be.equal( - expectedUser2Redirected, - INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER - ); - expect(user3RedirectedBalanceAfter.toString()).to.be.equal( - expectedUser3Redirected, - INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER - ); - }); - - it('User 1 transfers the whole amount to himself', async () => { - const {users, pool, aDai, dai} = testEnv; - - const user1BalanceBefore = await aDai.balanceOf(users[1].address); - - await aDai.connect(users[1].signer).transfer(users[1].address, user1BalanceBefore); - - const user1BalanceAfter = await aDai.balanceOf(users[1].address); - - expect(user1BalanceAfter.toString()).to.be.equal( - user1BalanceBefore, - INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER - ); - }); }); diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index 3942fdf3..1e8dd6db 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -14,7 +14,6 @@ import { calcExpectedUserDataAfterStableRateRebalance, calcExpectedUserDataAfterSwapRateMode, calcExpectedUserDataAfterWithdraw, - calcExpectedUsersDataAfterRedirectInterest, } from './utils/calculations'; import {getReserveAddressFromSymbol, getReserveData, getUserData} from './utils/helpers'; @@ -49,23 +48,25 @@ const almostEqualOrEqual = function ( key === 'marketStableRate' || key === 'symbol' || key === 'aTokenAddress' || - key === 'initialATokenExchangeRate' || key === 'decimals' ) { // skipping consistency check on accessory data return; } + this.assert(actual[key] != undefined, `Property ${key} is undefined in the actual data`); expect(expected[key] != undefined, `Property ${key} is undefined in the expected data`); + if (expected[key] == null || actual[key] == null) { + console.log('Found a undefined value for Key ', key, ' value ', expected[key], actual[key]); + } + if (actual[key] instanceof BigNumber) { - if (!expected[key]) { - console.log('Key ', key, ' value ', expected[key], actual[key]); - } + const actualValue = (actual[key]).decimalPlaces(0, BigNumber.ROUND_DOWN); const expectedValue = (expected[key]).decimalPlaces(0, BigNumber.ROUND_DOWN); - + this.assert( actualValue.eq(expectedValue) || actualValue.plus(1).eq(expectedValue) || @@ -654,153 +655,6 @@ export const rebalanceStableBorrowRate = async ( } }; -export const redirectInterestStream = async ( - reserveSymbol: string, - user: SignerWithAddress, - to: tEthereumAddress, - expectedResult: string, - testEnv: TestEnv, - revertMessage?: string -) => { - const { - aTokenInstance, - reserve, - userData: fromDataBefore, - reserveData: reserveDataBefore, - } = await getDataBeforeAction(reserveSymbol, user.address, testEnv); - - const {userData: toDataBefore} = await getContractsData(reserve, to, testEnv); - - if (expectedResult === 'success') { - const txResult = await waitForTx( - await aTokenInstance.connect(user.signer).redirectInterestStream(to) - ); - - const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult); - - const {userData: fromDataAfter} = await getContractsData(reserve, user.address, testEnv); - - const {userData: toDataAfter} = await getContractsData(reserve, to, testEnv); - - const [expectedFromData, expectedToData] = calcExpectedUsersDataAfterRedirectInterest( - reserveDataBefore, - fromDataBefore, - toDataBefore, - user.address, - to, - true, - txCost, - txTimestamp - ); - - expectEqual(fromDataAfter, expectedFromData); - expectEqual(toDataAfter, expectedToData); - - // truffleAssert.eventEmitted(txResult, 'InterestStreamRedirected', (ev: any) => { - // const {_from, _to} = ev; - // return _from === user - // && _to === (to === user ? NIL_ADDRESS : to); - // }); - } else if (expectedResult === 'revert') { - await expect(aTokenInstance.connect(user.signer).redirectInterestStream(to), revertMessage).to - .be.reverted; - } -}; - -export const redirectInterestStreamOf = async ( - reserveSymbol: string, - user: SignerWithAddress, - from: tEthereumAddress, - to: tEthereumAddress, - expectedResult: string, - testEnv: TestEnv, - revertMessage?: string -) => { - const { - aTokenInstance, - reserve, - userData: fromDataBefore, - reserveData: reserveDataBefore, - } = await getDataBeforeAction(reserveSymbol, from, testEnv); - - const {userData: toDataBefore} = await getContractsData(reserve, user.address, testEnv); - - if (expectedResult === 'success') { - const txResult = await waitForTx( - await aTokenInstance.connect(user.signer).redirectInterestStreamOf(from, to) - ); - - const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult); - - const {userData: fromDataAfter} = await getContractsData(reserve, from, testEnv); - - const {userData: toDataAfter} = await getContractsData(reserve, to, testEnv); - - const [expectedFromData, exptectedToData] = calcExpectedUsersDataAfterRedirectInterest( - reserveDataBefore, - fromDataBefore, - toDataBefore, - from, - to, - from === user.address, - txCost, - txTimestamp - ); - - expectEqual(fromDataAfter, expectedFromData); - expectEqual(toDataAfter, exptectedToData); - - // truffleAssert.eventEmitted( - // txResult, - // "InterestStreamRedirected", - // (ev: any) => { - // const {_from, _to} = ev; - // return ( - // _from.toLowerCase() === from.toLowerCase() && - // _to.toLowerCase() === to.toLowerCase() - // ); - // } - // ); - } else if (expectedResult === 'revert') { - await expect( - aTokenInstance.connect(user.signer).redirectInterestStreamOf(from, to), - revertMessage - ).to.be.reverted; - } -}; - -export const allowInterestRedirectionTo = async ( - reserveSymbol: string, - user: SignerWithAddress, - to: tEthereumAddress, - expectedResult: string, - testEnv: TestEnv, - revertMessage?: string -) => { - const {aTokenInstance} = await getDataBeforeAction(reserveSymbol, user.address, testEnv); - - if (expectedResult === 'success') { - const txResult = await waitForTx( - await aTokenInstance.connect(user.signer).allowInterestRedirectionTo(to) - ); - - // truffleAssert.eventEmitted( - // txResult, - // "InterestRedirectionAllowanceChanged", - // (ev: any) => { - // const {_from, _to} = ev; - // return ( - // _from.toLowerCase() === user.toLowerCase() && - // _to.toLowerCase() === to.toLowerCase() - // ); - // } - // ); - } else if (expectedResult === 'revert') { - await expect(aTokenInstance.connect(user.signer).allowInterestRedirectionTo(to), revertMessage) - .to.be.reverted; - } -}; - const expectEqual = ( actual: UserReserveData | ReserveData, expected: UserReserveData | ReserveData diff --git a/test/helpers/scenario-engine.ts b/test/helpers/scenario-engine.ts index 260e87e5..8618de3d 100644 --- a/test/helpers/scenario-engine.ts +++ b/test/helpers/scenario-engine.ts @@ -8,10 +8,7 @@ import { repay, setUseAsCollateral, swapBorrowRateMode, - rebalanceStableBorrowRate, - redirectInterestStream, - redirectInterestStreamOf, - allowInterestRedirectionTo, + rebalanceStableBorrowRate } from './actions'; import {RateMode} from '../../helpers/types'; @@ -197,71 +194,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv await rebalanceStableBorrowRate(reserve, user, target, expected, testEnv, revertMessage); } break; - - case 'redirectInterestStream': - { - const {to: toIndex} = action.args; - - if (!toIndex || toIndex === '') { - throw `A target must be selected when trying to redirect the interest`; - } - const toUser = users[parseInt(toIndex)]; - - await redirectInterestStream( - reserve, - user, - toUser.address, - expected, - testEnv, - revertMessage - ); - } - break; - - case 'redirectInterestStreamOf': - { - const {from: fromIndex, to: toIndex} = action.args; - - if (!fromIndex || fromIndex === '') { - throw `A from address must be specified when trying to redirect the interest`; - } - if (!toIndex || toIndex === '') { - throw `A target must be selected when trying to redirect the interest`; - } - const toUser = users[parseInt(toIndex)]; - const fromUser = users[parseInt(fromIndex)]; - - await redirectInterestStreamOf( - reserve, - user, - fromUser.address, - toUser.address, - expected, - testEnv, - revertMessage - ); - } - break; - - case 'allowInterestRedirectionTo': - { - const {to: toIndex} = action.args; - - if (!toIndex || toIndex === '') { - throw `A target must be selected when trying to redirect the interest`; - } - const toUser = users[parseInt(toIndex)]; - - await allowInterestRedirectionTo( - reserve, - user, - toUser.address, - expected, - testEnv, - revertMessage - ); - } - break; + default: throw `Invalid action requested: ${name}`; } diff --git a/test/helpers/scenarios/interest-redirection-negatives.json b/test/helpers/scenarios/interest-redirection-negatives.json deleted file mode 100644 index 91b6a56f..00000000 --- a/test/helpers/scenarios/interest-redirection-negatives.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "title": "AToken: interest rate redirection negative test cases", - "description": "Test cases for the aToken interest rate redirection.", - "stories": [ - { - "description": "User 0 deposits 1000 DAI, tries to give allowance to redirect interest to himself (revert expected)", - "actions": [ - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "0" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "allowInterestRedirectionTo", - "args": { - "reserve": "DAI", - "user": "0", - "to": "0" - }, - "expected": "revert", - "revertMessage": "User cannot give allowance to himself" - } - ] - }, - { - "description": "User 1 tries to redirect the interest of user 0 without allowance (revert expected)", - "actions": [ - { - "name": "redirectInterestStreamOf", - "args": { - "reserve": "DAI", - "user": "1", - "from": "0", - "to": "2" - }, - "expected": "revert", - "revertMessage": "Caller is not allowed to redirect the interest of the user" - } - ] - }, - { - "description": "User 0 tries to redirect the interest to user 2 twice (revert expected)", - "actions": [ - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "2" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "2" - }, - "expected": "revert", - "revertMessage": "Interest is already redirected to the user" - } - ] - }, - { - "description": "User 3 with 0 balance tries to redirect the interest to user 2 (revert expected)", - "actions": [ - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "3", - "to": "2" - }, - "expected": "revert", - "revertMessage": "Interest stream can only be redirected if there is a valid balance" - } - ] - } - ] -} diff --git a/test/helpers/scenarios/interest-redirection.json b/test/helpers/scenarios/interest-redirection.json deleted file mode 100644 index 8ed00d1f..00000000 --- a/test/helpers/scenarios/interest-redirection.json +++ /dev/null @@ -1,405 +0,0 @@ -{ - "title": "AToken: interest rate redirection", - "description": "Test cases for the aToken interest rate redirection.", - "stories": [ - { - "description": "User 0 deposits 1000 DAI, redirects the interest to user 2", - "actions": [ - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "0" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "2" - }, - "expected": "success" - } - ] - }, - { - "description": "User 1 deposits 1 ETH, borrows 100 DAI, repays after one year. Users 0 deposits another 1000 DAI. Redirected balance of user 2 is updated", - "actions": [ - { - "name": "mint", - "args": { - "reserve": "WETH", - "amount": "2", - "user": "1" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "WETH", - "user": "1" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "WETH", - - "amount": "2", - "user": "1" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - } - ] - }, - { - "description": "User 1 borrows another 100 DAI, repay the whole amount. Users 0 and User 2 withdraw", - "actions": [ - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "100", - "user": "1" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "1" - }, - "expected": "success" - }, - { - "name": "repay", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "1", - "onBehalfOf": "1", - "borrowRateMode": "stable" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "0" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "2" - }, - "expected": "success" - } - ] - }, - { - "description": "User 0 deposits 1000 DAI, redirects interest to user 2, user 1 borrows 100 DAI. After one year, user 0 redirects interest back to himself, user 1 borrows another 100 DAI and after another year repays the whole amount. Users 0 and User 2 withdraw", - "actions": [ - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "2" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "0" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "100", - "user": "1" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "1" - }, - "expected": "success" - }, - { - "name": "repay", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "1", - "onBehalfOf": "1", - "borrowRateMode": "stable" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "0" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "2" - }, - "expected": "success" - } - ] - }, - { - "description": "User 0 deposits 1000 DAI, redirects interest to user 2, user 1 borrows 100 DAI. After one year, user 2 redirects interest to user 3. user 1 borrows another 100 DAI, user 0 deposits another 100 DAI. User 1 repays the whole amount. Users 0, 2 first 1 DAI, then everything. User 3 withdraws", - "actions": [ - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "0", - "to": "2" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "redirectInterestStream", - "args": { - "reserve": "DAI", - "user": "2", - "to": "3" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "DAI", - "amount": "100", - "borrowRateMode": "stable", - "user": "1", - "timeTravel": "365" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "100", - "user": "0" - }, - "expected": "success" - }, - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "100", - "user": "1" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "1" - }, - "expected": "success" - }, - { - "name": "repay", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "1", - "onBehalfOf": "1", - "borrowRateMode": "stable" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "1", - "user": "0" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "1", - "user": "2" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "0" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "2" - }, - "expected": "success" - }, - { - "name": "withdraw", - "args": { - "reserve": "DAI", - "amount": "-1", - "user": "3" - }, - "expected": "success" - } - ] - } - ] -} diff --git a/test/helpers/utils/calculations.ts b/test/helpers/utils/calculations.ts index 13c8da78..ff4c64be 100644 --- a/test/helpers/utils/calculations.ts +++ b/test/helpers/utils/calculations.ts @@ -41,7 +41,7 @@ export const calcExpectedUserDataAfterDeposit = ( txTimestamp ); - expectedUserData.principalATokenBalance = userDataBeforeAction.principalStableDebt; + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; @@ -49,38 +49,26 @@ export const calcExpectedUserDataAfterDeposit = ( expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate; - expectedUserData.currentATokenBalance = userDataBeforeAction.currentATokenBalance.plus( - amountDeposited + expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance( + userDataBeforeAction, + reserveDataAfterAction.liquidityIndex, + new BigNumber(amountDeposited), + new BigNumber(0) ); - - if (userDataBeforeAction.currentATokenBalance.eq(0)) { - expectedUserData.usageAsCollateralEnabled = true; - } else { - //if the user is withdrawing everything, usageAsCollateralEnabled must be false - if (expectedUserData.currentATokenBalance.eq(0)) { - expectedUserData.usageAsCollateralEnabled = false; - } else { - expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; - } - } - - expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; - expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(amountDeposited); - - expectedUserData.principalATokenBalance = expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( reserveDataBeforeAction, userDataBeforeAction, txTimestamp ).plus(amountDeposited); - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedUserData.currentATokenBalance, - expectedUserData.redirectedBalance, - txTimestamp - ); + if (userDataBeforeAction.currentATokenBalance.eq(0)) { + expectedUserData.usageAsCollateralEnabled = true; + } else { + expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; + } + + expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(amountDeposited); expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance( userDataBeforeAction, @@ -93,14 +81,6 @@ export const calcExpectedUserDataAfterDeposit = ( txTimestamp ); - expectedUserData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance( - userDataBeforeAction, - expectedUserData, - userDataBeforeAction.redirectionAddressRedirectedBalance, - new BigNumber(amountDeposited), - new BigNumber(0) - ); - return expectedUserData; }; @@ -125,23 +105,29 @@ export const calcExpectedUserDataAfterWithdraw = ( amountWithdrawn = aTokenBalance.toFixed(0); } - expectedUserData.principalATokenBalance = expectedUserData.currentATokenBalance = aTokenBalance.minus( - amountWithdrawn + expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance( + userDataBeforeAction, + reserveDataAfterAction.liquidityIndex, + new BigNumber(0), + new BigNumber(amountWithdrawn) ); - expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance( + expectedUserData.currentATokenBalance = aTokenBalance.minus(amountWithdrawn); + + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; + expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; + + expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( userDataBeforeAction, txTimestamp ); - expectedUserData.currentVariableDebt = expectedUserData.principalStableDebt = calcExpectedVariableDebtTokenBalance( + expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( reserveDataBeforeAction, userDataBeforeAction, txTimestamp ); - expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; - expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; @@ -159,31 +145,8 @@ export const calcExpectedUserDataAfterWithdraw = ( } } - expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountWithdrawn); - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - - if (expectedUserData.currentATokenBalance.eq(0) && expectedUserData.redirectedBalance.eq(0)) { - expectedUserData.interestRedirectionAddress = ZERO_ADDRESS; - } else { - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - } - expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedUserData.currentATokenBalance, - expectedUserData.redirectedBalance, - txTimestamp - ); - - expectedUserData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance( - userDataBeforeAction, - expectedUserData, - userDataBeforeAction.redirectionAddressRedirectedBalance, - new BigNumber(0), - new BigNumber(amountWithdrawn) - ); - return expectedUserData; }; @@ -575,13 +538,8 @@ export const calcExpectedUserDataAfterBorrow = ( userDataBeforeAction, currentTimestamp ); - expectedUserData.principalATokenBalance = userDataBeforeAction.principalATokenBalance; - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - expectedUserData.redirectionAddressRedirectedBalance = - userDataBeforeAction.redirectionAddressRedirectedBalance; - expectedUserData.currentATokenUserIndex = userDataBeforeAction.currentATokenUserIndex; - + expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance; + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountBorrowed); return expectedUserData; @@ -664,13 +622,8 @@ export const calcExpectedUserDataAfterRepay = ( userDataBeforeAction, txTimestamp ); - expectedUserData.principalATokenBalance = userDataBeforeAction.principalATokenBalance; - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - expectedUserData.redirectionAddressRedirectedBalance = - userDataBeforeAction.redirectionAddressRedirectedBalance; - expectedUserData.currentATokenUserIndex = userDataBeforeAction.currentATokenUserIndex; - + expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance; + if (user === onBehalfOf) { expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(totalRepaid); } else { @@ -809,18 +762,6 @@ export const calcExpectedUserDataAfterSwapRateMode = ( txTimestamp ); - expectedUserData.principalATokenBalance = userDataBeforeAction.principalATokenBalance; - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - expectedUserData.redirectionAddressRedirectedBalance = - userDataBeforeAction.redirectionAddressRedirectedBalance; - expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedUserData.currentATokenBalance, - expectedUserData.redirectedBalance, - txTimestamp - ); - if (rateMode === RateMode.Stable) { // swap to variable expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = new BigNumber(0); @@ -912,6 +853,7 @@ export const calcExpectedReserveDataAfterStableRateRebalance = ( expectedReserveData.totalBorrowsVariable, expectedReserveData.averageStableBorrowRate ); + expectedReserveData.liquidityRate = rates[0]; expectedReserveData.stableBorrowRate = rates[1]; @@ -953,10 +895,6 @@ export const calcExpectedUserDataAfterStableRateRebalance = ( expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; - const debtAccrued = expectedUserData.currentStableDebt.minus( - userDataBeforeAction.principalStableDebt - ); - expectedUserData.stableBorrowRate = reserveDataBeforeAction.stableBorrowRate; expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; @@ -966,114 +904,19 @@ export const calcExpectedUserDataAfterStableRateRebalance = ( userDataBeforeAction, txTimestamp ); - expectedUserData.principalATokenBalance = userDataBeforeAction.principalATokenBalance; - expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance; - expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress; - expectedUserData.redirectionAddressRedirectedBalance = - userDataBeforeAction.redirectionAddressRedirectedBalance; - - expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedUserData.currentATokenBalance, - expectedUserData.redirectedBalance, - txTimestamp - ); return expectedUserData; }; -export const calcExpectedUsersDataAfterRedirectInterest = ( - reserveDataBeforeAction: ReserveData, - fromDataBeforeAction: UserReserveData, - toDataBeforeAction: UserReserveData, - fromAddress: string, - toAddress: string, - isFromExecutingTx: boolean, - txCost: BigNumber, - txTimestamp: BigNumber -): UserReserveData[] => { - const expectedFromData = {...fromDataBeforeAction}; - const expectedToData = {...toDataBeforeAction}; - - expectedFromData.currentStableDebt = calcExpectedStableDebtTokenBalance( - fromDataBeforeAction, - txTimestamp - ); - - expectedToData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( - reserveDataBeforeAction, - toDataBeforeAction, - txTimestamp - ); - - expectedFromData.variableBorrowIndex = fromDataBeforeAction.variableBorrowIndex; - expectedToData.variableBorrowIndex = toDataBeforeAction.variableBorrowIndex; - - expectedFromData.stableBorrowRate = fromDataBeforeAction.stableBorrowRate; - expectedToData.stableBorrowRate = toDataBeforeAction.stableBorrowRate; - - expectedFromData.principalATokenBalance = expectedFromData.currentATokenBalance = calcExpectedATokenBalance( - reserveDataBeforeAction, - fromDataBeforeAction, - txTimestamp - ); - - expectedToData.principalATokenBalance = expectedToData.currentATokenBalance = calcExpectedATokenBalance( - reserveDataBeforeAction, - toDataBeforeAction, - txTimestamp - ); - - expectedToData.redirectedBalance = toDataBeforeAction.redirectedBalance.plus( - expectedFromData.currentATokenBalance - ); - - if (fromAddress === toAddress) { - expectedFromData.interestRedirectionAddress = ZERO_ADDRESS; - expectedFromData.redirectedBalance = new BigNumber(0); - expectedFromData.redirectionAddressRedirectedBalance = new BigNumber(0); - expectedToData.interestRedirectionAddress = ZERO_ADDRESS; - expectedToData.redirectedBalance = new BigNumber(0); - expectedToData.redirectionAddressRedirectedBalance = new BigNumber(0); - } else { - expectedFromData.interestRedirectionAddress = toAddress; - - expectedFromData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance( - toDataBeforeAction, - expectedFromData, - toDataBeforeAction.redirectedBalance, - expectedFromData.currentATokenBalance, - new BigNumber(0) - ); - } - - expectedFromData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedFromData.currentATokenBalance, - expectedFromData.redirectedBalance, - txTimestamp - ); - - expectedToData.currentATokenUserIndex = calcExpectedATokenUserIndex( - reserveDataBeforeAction, - expectedToData.currentATokenBalance, - expectedToData.redirectedBalance, - txTimestamp - ); - - return [expectedFromData, expectedToData]; -}; - -const calcExpectedATokenUserIndex = ( - reserveDataBeforeAction: ReserveData, - expectedUserBalanceAfterAction: BigNumber, - expectedUserRedirectedBalanceAterAction: BigNumber, - currentTimestamp: BigNumber +const calcExpectedScaledATokenBalance = ( + userDataBeforeAction: UserReserveData, + index: BigNumber, + amountAdded: BigNumber, + amountTaken: BigNumber ) => { - if (expectedUserBalanceAfterAction.eq(0) && expectedUserRedirectedBalanceAterAction.eq(0)) { - return new BigNumber(0); - } - return calcExpectedReserveNormalizedIncome(reserveDataBeforeAction, currentTimestamp); + return userDataBeforeAction.scaledATokenBalance + .plus(amountAdded.rayDiv(index)) + .minus(amountTaken.rayDiv(index)); }; const calcExpectedATokenBalance = ( @@ -1081,51 +924,24 @@ const calcExpectedATokenBalance = ( userDataBeforeAction: UserReserveData, currentTimestamp: BigNumber ) => { - const income = calcExpectedReserveNormalizedIncome(reserveDataBeforeAction, currentTimestamp); + const index = calcExpectedReserveNormalizedIncome(reserveDataBeforeAction, currentTimestamp); const { - interestRedirectionAddress, - currentATokenUserIndex: userIndexBeforeAction, - redirectedBalance, - principalATokenBalance: principalBalanceBeforeAction, + scaledATokenBalance: scaledBalanceBeforeAction, } = userDataBeforeAction; - if (userIndexBeforeAction.eq(0)) { - return principalBalanceBeforeAction; - } - if (interestRedirectionAddress === ZERO_ADDRESS) { - return principalBalanceBeforeAction - .plus(redirectedBalance) - .wadToRay() - .rayMul(income) - .rayDiv(userIndexBeforeAction) - .rayToWad() - .minus(redirectedBalance); - } else { - return principalBalanceBeforeAction.plus( - redirectedBalance - .wadToRay() - .rayMul(income) - .rayDiv(userIndexBeforeAction) - .rayToWad() - .minus(redirectedBalance) - ); - } + return scaledBalanceBeforeAction.rayMul(index); }; const calcExpectedRedirectedBalance = ( - userDataBeforeAction: UserReserveData, expectedUserDataAfterAction: UserReserveData, + index: BigNumber, redirectedBalanceBefore: BigNumber, amountToAdd: BigNumber, amountToSubstract: BigNumber ): BigNumber => { - const balanceIncrease = userDataBeforeAction.currentATokenBalance.minus( - userDataBeforeAction.principalATokenBalance - ); - return expectedUserDataAfterAction.interestRedirectionAddress !== ZERO_ADDRESS - ? redirectedBalanceBefore.plus(balanceIncrease).plus(amountToAdd).minus(amountToSubstract) + ? redirectedBalanceBefore.plus(amountToAdd.rayDiv(index)).minus(amountToSubstract.rayDiv(index)) : new BigNumber('0'); }; const calcExpectedAverageStableBorrowRate = ( diff --git a/test/helpers/utils/helpers.ts b/test/helpers/utils/helpers.ts index 9dac685b..7d3ccee5 100644 --- a/test/helpers/utils/helpers.ts +++ b/test/helpers/utils/helpers.ts @@ -64,28 +64,17 @@ export const getUserData = async ( user: tEthereumAddress, sender?: tEthereumAddress ): Promise => { - const [userData, aTokenData] = await Promise.all([ + const [userData, scaledATokenBalance] = await Promise.all([ pool.getUserReserveData(reserve, user), getATokenUserData(reserve, user, pool), ]); - const [ - userIndex, - redirectedBalance, - principalATokenBalance, - redirectionAddressRedirectedBalance, - interestRedirectionAddress, - ] = aTokenData; - + const token = await getMintableErc20(reserve); const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString()); return { - principalATokenBalance: new BigNumber(principalATokenBalance), - interestRedirectionAddress, - redirectionAddressRedirectedBalance: new BigNumber(redirectionAddressRedirectedBalance), - redirectedBalance: new BigNumber(redirectedBalance), - currentATokenUserIndex: new BigNumber(userIndex), + scaledATokenBalance: new BigNumber(scaledATokenBalance), currentATokenBalance: new BigNumber(userData.currentATokenBalance.toString()), currentStableDebt: new BigNumber(userData.currentStableDebt.toString()), currentVariableDebt: new BigNumber(userData.currentVariableDebt.toString()), @@ -115,28 +104,8 @@ const getATokenUserData = async (reserve: string, user: string, pool: LendingPoo const aTokenAddress: string = (await pool.getReserveTokensAddresses(reserve)).aTokenAddress; const aToken = await getAToken(aTokenAddress); - const [ - userIndex, - interestRedirectionAddress, - redirectedBalance, - principalTokenBalance, - ] = await Promise.all([ - aToken.getUserIndex(user), - aToken.getInterestRedirectionAddress(user), - aToken.getRedirectedBalance(user), - aToken.principalBalanceOf(user), - ]); - const redirectionAddressRedirectedBalance = - interestRedirectionAddress !== ZERO_ADDRESS - ? new BigNumber((await aToken.getRedirectedBalance(interestRedirectionAddress)).toString()) - : new BigNumber('0'); + const scaledBalance = await aToken.scaledBalanceOf(user); + return scaledBalance.toString(); - return [ - userIndex.toString(), - redirectedBalance.toString(), - principalTokenBalance.toString(), - redirectionAddressRedirectedBalance.toString(), - interestRedirectionAddress, - ]; }; diff --git a/test/helpers/utils/interfaces/index.ts b/test/helpers/utils/interfaces/index.ts index aa84ad3d..7042bbea 100644 --- a/test/helpers/utils/interfaces/index.ts +++ b/test/helpers/utils/interfaces/index.ts @@ -1,12 +1,8 @@ import BigNumber from 'bignumber.js'; export interface UserReserveData { - principalATokenBalance: BigNumber; + scaledATokenBalance: BigNumber; currentATokenBalance: BigNumber; - currentATokenUserIndex: BigNumber; - interestRedirectionAddress: string; - redirectionAddressRedirectedBalance: BigNumber; - redirectedBalance: BigNumber; currentStableDebt: BigNumber; currentVariableDebt: BigNumber; principalStableDebt: BigNumber;