Optimized debt tokens

This commit is contained in:
The3D 2020-08-22 19:33:55 +02:00
parent 4d054dd56d
commit 5b7a2f2a55
3 changed files with 48 additions and 48 deletions

View File

@ -11,27 +11,17 @@ import {IStableDebtToken} from './interfaces/IStableDebtToken.sol';
/** /**
* @title contract StableDebtToken * @title contract StableDebtToken
* * @notice Implements a stable debt token to track the user positions
* @notice defines the interface for the stable debt token
*
* @dev it does not inherit from IERC20 to save in code size
*
* @author Aave * @author Aave
*
**/ **/
contract StableDebtToken is IStableDebtToken, DebtTokenBase { contract StableDebtToken is IStableDebtToken, DebtTokenBase {
using SafeMath for uint256; using SafeMath for uint256;
using WadRayMath for uint256; using WadRayMath for uint256;
uint256 public constant DEBT_TOKEN_REVISION = 0x1; uint256 public constant DEBT_TOKEN_REVISION = 0x1;
struct UserData {
uint256 currentRate;
uint40 lastUpdateTimestamp;
}
uint256 private avgStableRate; uint256 private _avgStableRate;
mapping(address => uint40) _timestamps;
mapping(address => UserData) private _usersData;
constructor( constructor(
address pool, address pool,
@ -53,7 +43,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
* @return the average stable rate * @return the average stable rate
**/ **/
function getAverageStableRate() external virtual override view returns (uint256) { 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 * @return the last update timestamp
**/ **/
function getUserLastUpdated(address user) external virtual override view returns (uint40) { 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 * @return the stable rate of user
**/ **/
function getUserStableRate(address user) external virtual override view returns (uint256) { 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 * @return the accumulated debt of the user
**/ **/
function balanceOf(address account) public virtual override view returns (uint256) { 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) { if (accountBalance == 0) {
return 0; return 0;
} }
UserData storage userData = _usersData[account];
uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest(
userData.currentRate, stableRate,
userData.lastUpdateTimestamp _timestamps[account]
); );
return accountBalance.wadToRay().rayMul(cumulatedInterest).rayToWad(); return accountBalance.wadToRay().rayMul(cumulatedInterest).rayToWad();
} }
@ -126,19 +114,20 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
vars.amountInRay = amount.wadToRay(); vars.amountInRay = amount.wadToRay();
//calculates the new stable rate for the user //calculates the new stable rate for the user
vars.newStableRate = _usersData[user] vars.newStableRate = uint256(_usersData[user]
.currentRate .dataField)
.rayMul(currentBalance.wadToRay()) .rayMul(currentBalance.wadToRay())
.add(vars.amountInRay.rayMul(rate)) .add(vars.amountInRay.rayMul(rate))
.rayDiv(currentBalance.add(amount).wadToRay()); .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 //solium-disable-next-line
_usersData[user].lastUpdateTimestamp = uint40(block.timestamp); _timestamps[user] = uint40(block.timestamp);
//calculates the updated average stable rate //calculates the updated average stable rate
avgStableRate = avgStableRate _avgStableRate = _avgStableRate
.rayMul(vars.supplyBeforeMint.wadToRay()) .rayMul(vars.supplyBeforeMint.wadToRay())
.add(rate.rayMul(vars.amountInRay)) .add(rate.rayMul(vars.amountInRay))
.rayDiv(vars.supplyAfterMint.wadToRay()); .rayDiv(vars.supplyAfterMint.wadToRay());
@ -171,20 +160,20 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
uint256 supplyAfterBurn = supplyBeforeBurn.sub(amount); uint256 supplyAfterBurn = supplyBeforeBurn.sub(amount);
if (supplyAfterBurn == 0) { if (supplyAfterBurn == 0) {
avgStableRate = 0; _avgStableRate = 0;
} else { } else {
avgStableRate = avgStableRate _avgStableRate = _avgStableRate
.rayMul(supplyBeforeBurn.wadToRay()) .rayMul(supplyBeforeBurn.wadToRay())
.sub(_usersData[user].currentRate.rayMul(amount.wadToRay())) .sub(uint256(_usersData[user].dataField).rayMul(amount.wadToRay()))
.rayDiv(supplyAfterBurn.wadToRay()); .rayDiv(supplyAfterBurn.wadToRay());
} }
if (amount == currentBalance) { if (amount == currentBalance) {
_usersData[user].currentRate = 0; _usersData[user].dataField = 0;
_usersData[user].lastUpdateTimestamp = 0; _timestamps[user] = 0;
} else { } else {
//solium-disable-next-line //solium-disable-next-line
_usersData[user].lastUpdateTimestamp = uint40(block.timestamp); _timestamps[user] = uint40(block.timestamp);
} }
if (balanceIncrease > amount) { if (balanceIncrease > amount) {

View File

@ -9,10 +9,9 @@ import {WadRayMath} from '../libraries/math/WadRayMath.sol';
import {IVariableDebtToken} from './interfaces/IVariableDebtToken.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 * @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 { contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
using SafeMath for uint256; using SafeMath for uint256;
@ -20,8 +19,6 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
uint256 public constant DEBT_TOKEN_REVISION = 0x1; uint256 public constant DEBT_TOKEN_REVISION = 0x1;
mapping(address => uint256) private _userIndexes;
constructor( constructor(
address pool, address pool,
address underlyingAsset, address underlyingAsset,
@ -42,7 +39,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
* @return the debt balance of the user * @return the debt balance of the user
**/ **/
function balanceOf(address user) public virtual override view returns (uint256) { 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) { if (userBalance == 0) {
return 0; return 0;
} }
@ -51,7 +49,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
userBalance userBalance
.wadToRay() .wadToRay()
.rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress)) .rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress))
.rayDiv(_userIndexes[user]) .rayDiv(index)
.rayToWad(); .rayToWad();
} }
@ -61,7 +59,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
**/ **/
function getUserIndex(address user) external virtual override view returns (uint256) { 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)); _mint(user, amount.add(balanceIncrease));
uint256 newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); 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); emit MintDebt(user, amount, previousBalance, currentBalance, balanceIncrease, newUserIndex);
} }
@ -106,8 +105,9 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
//if user not repaid everything //if user not repaid everything
if (currentBalance != amount) { if (currentBalance != amount) {
newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); 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); emit BurnDebt(user, amount, previousBalance, currentBalance, balanceIncrease, newUserIndex);
} }

View File

@ -27,7 +27,14 @@ abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable {
address internal immutable _underlyingAssetAddress; address internal immutable _underlyingAssetAddress;
ILendingPool internal immutable _pool; 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 * @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 * @return the debt balance of the user since the last burn/mint action
**/ **/
function principalBalanceOf(address user) public view returns (uint256) { 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 { function _mint(address user, uint256 amount) internal {
_totalSupply = _totalSupply.add(amount); _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 { function _burn(address user, uint256 amount) internal {
_totalSupply = _totalSupply.sub(amount); _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
) )
{ {
uint256 previousPrincipalBalance = _balances[user]; uint256 previousPrincipalBalance = _usersData[user].balance;
if (previousPrincipalBalance == 0) { if (previousPrincipalBalance == 0) {
return (0, 0, 0); return (0, 0, 0);