diff --git a/contracts/tokenization/StableDebtToken.sol b/contracts/tokenization/StableDebtToken.sol index 318c7e04..aa572eaf 100644 --- a/contracts/tokenization/StableDebtToken.sol +++ b/contracts/tokenization/StableDebtToken.sol @@ -11,27 +11,17 @@ import {IStableDebtToken} from './interfaces/IStableDebtToken.sol'; /** * @title contract StableDebtToken - * - * @notice defines the interface for the stable debt token - * - * @dev it does not inherit from IERC20 to save in code size - * + * @notice Implements a stable debt token to track the user positions * @author Aave - * **/ contract StableDebtToken is IStableDebtToken, DebtTokenBase { using SafeMath for uint256; using WadRayMath for uint256; uint256 public constant DEBT_TOKEN_REVISION = 0x1; - struct UserData { - uint256 currentRate; - uint40 lastUpdateTimestamp; - } - uint256 private avgStableRate; - - mapping(address => UserData) private _usersData; + uint256 private _avgStableRate; + mapping(address => uint40) _timestamps; constructor( address pool, @@ -53,7 +43,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { * @return the average stable rate **/ function getAverageStableRate() external virtual override view returns (uint256) { - return avgStableRate; + return _avgStableRate; } /** @@ -61,7 +51,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { * @return the last update timestamp **/ function getUserLastUpdated(address user) external virtual override view returns (uint40) { - return _usersData[user].lastUpdateTimestamp; + return _timestamps[user]; } /** @@ -70,7 +60,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { * @return the stable rate of user **/ function getUserStableRate(address user) external virtual override view returns (uint256) { - return _usersData[user].currentRate; + return _usersData[user].dataField; } /** @@ -78,16 +68,14 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { * @return the accumulated debt of the user **/ function balanceOf(address account) public virtual override view returns (uint256) { - uint256 accountBalance = _balances[account]; + uint256 accountBalance = _usersData[account].balance; + uint256 stableRate = _usersData[account].dataField; if (accountBalance == 0) { return 0; } - - UserData storage userData = _usersData[account]; - uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( - userData.currentRate, - userData.lastUpdateTimestamp + stableRate, + _timestamps[account] ); return accountBalance.wadToRay().rayMul(cumulatedInterest).rayToWad(); } @@ -126,19 +114,20 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { vars.amountInRay = amount.wadToRay(); //calculates the new stable rate for the user - vars.newStableRate = _usersData[user] - .currentRate + vars.newStableRate = uint256(_usersData[user] + .dataField) .rayMul(currentBalance.wadToRay()) .add(vars.amountInRay.rayMul(rate)) .rayDiv(currentBalance.add(amount).wadToRay()); - _usersData[user].currentRate = vars.newStableRate; + require(vars.newStableRate < (1 << 128), "Debt token: stable rate overflow"); + _usersData[user].dataField = uint128(vars.newStableRate); //solium-disable-next-line - _usersData[user].lastUpdateTimestamp = uint40(block.timestamp); + _timestamps[user] = uint40(block.timestamp); //calculates the updated average stable rate - avgStableRate = avgStableRate + _avgStableRate = _avgStableRate .rayMul(vars.supplyBeforeMint.wadToRay()) .add(rate.rayMul(vars.amountInRay)) .rayDiv(vars.supplyAfterMint.wadToRay()); @@ -171,20 +160,20 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { uint256 supplyAfterBurn = supplyBeforeBurn.sub(amount); if (supplyAfterBurn == 0) { - avgStableRate = 0; + _avgStableRate = 0; } else { - avgStableRate = avgStableRate + _avgStableRate = _avgStableRate .rayMul(supplyBeforeBurn.wadToRay()) - .sub(_usersData[user].currentRate.rayMul(amount.wadToRay())) + .sub(uint256(_usersData[user].dataField).rayMul(amount.wadToRay())) .rayDiv(supplyAfterBurn.wadToRay()); } if (amount == currentBalance) { - _usersData[user].currentRate = 0; - _usersData[user].lastUpdateTimestamp = 0; + _usersData[user].dataField = 0; + _timestamps[user] = 0; } else { //solium-disable-next-line - _usersData[user].lastUpdateTimestamp = uint40(block.timestamp); + _timestamps[user] = uint40(block.timestamp); } if (balanceIncrease > amount) { diff --git a/contracts/tokenization/VariableDebtToken.sol b/contracts/tokenization/VariableDebtToken.sol index 9a4ffd2c..901491b6 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -9,10 +9,9 @@ import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {IVariableDebtToken} from './interfaces/IVariableDebtToken.sol'; /** - * @title interface IVariableDebtToken + * @title contract VariableDebtToken + * @notice Implements a variable debt token to track the user positions * @author Aave - * @notice defines the basic interface for a variable debt token. - * @dev does not inherit from IERC20 to save in contract size **/ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { using SafeMath for uint256; @@ -20,8 +19,6 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { uint256 public constant DEBT_TOKEN_REVISION = 0x1; - mapping(address => uint256) private _userIndexes; - constructor( address pool, address underlyingAsset, @@ -42,7 +39,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { * @return the debt balance of the user **/ function balanceOf(address user) public virtual override view returns (uint256) { - uint256 userBalance = _balances[user]; + uint256 userBalance = _usersData[user].balance; + uint256 index = _usersData[user].dataField; if (userBalance == 0) { return 0; } @@ -51,7 +49,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { userBalance .wadToRay() .rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress)) - .rayDiv(_userIndexes[user]) + .rayDiv(index) .rayToWad(); } @@ -61,7 +59,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { **/ function getUserIndex(address user) external virtual override view returns (uint256) { - return _userIndexes[user]; + return _usersData[user].dataField; } /** @@ -79,7 +77,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { _mint(user, amount.add(balanceIncrease)); uint256 newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); - _userIndexes[user] = newUserIndex; + require(newUserIndex < (1 << 128), "Debt token: Index overflow"); + _usersData[user].dataField = uint128(newUserIndex); emit MintDebt(user, amount, previousBalance, currentBalance, balanceIncrease, newUserIndex); } @@ -106,8 +105,9 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { //if user not repaid everything if (currentBalance != amount) { newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); + require(newUserIndex < (1 << 128), "Debt token: Index overflow"); } - _userIndexes[user] = newUserIndex; + _usersData[user].dataField = uint128(newUserIndex); emit BurnDebt(user, amount, previousBalance, currentBalance, balanceIncrease, newUserIndex); } diff --git a/contracts/tokenization/base/DebtTokenBase.sol b/contracts/tokenization/base/DebtTokenBase.sol index 87b83e0e..55ded0d5 100644 --- a/contracts/tokenization/base/DebtTokenBase.sol +++ b/contracts/tokenization/base/DebtTokenBase.sol @@ -27,7 +27,14 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { address internal immutable _underlyingAssetAddress; ILendingPool internal immutable _pool; - mapping(address => uint256) internal _balances; + + struct UserData{ + uint128 balance; + //this field will store the user index for the variable debt token, and the user stable rate for the stable debt token + uint128 dataField; + } + + mapping(address => UserData) internal _usersData; /** * @dev only lending pool can call functions marked by this modifier @@ -96,7 +103,7 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { * @return the debt balance of the user since the last burn/mint action **/ function principalBalanceOf(address user) public view returns (uint256) { - return _balances[user]; + return _usersData[user].balance; } /** @@ -106,7 +113,9 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { **/ function _mint(address user, uint256 amount) internal { _totalSupply = _totalSupply.add(amount); - _balances[user] = _balances[user].add(amount); + uint256 result = amount.add(_usersData[user].balance); + require(result < (1 << 128), "Debt token: balance overflow"); + _usersData[user].balance = uint128(result); } /** @@ -116,7 +125,9 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { **/ function _burn(address user, uint256 amount) internal { _totalSupply = _totalSupply.sub(amount); - _balances[user] = _balances[user].sub(amount); + uint256 result = uint256(_usersData[user].balance).sub(amount); + require(result < (1 << 128), "Debt token: balance overflow"); + _usersData[user].balance = uint128(result); } /** @@ -176,7 +187,7 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { uint256 ) { - uint256 previousPrincipalBalance = _balances[user]; + uint256 previousPrincipalBalance = _usersData[user].balance; if (previousPrincipalBalance == 0) { return (0, 0, 0);