feat: Reward aware aToken contracts

This commit is contained in:
David Racero 2021-05-12 09:47:45 +02:00
parent b4ed523a64
commit 55a9880c79
9 changed files with 979 additions and 145 deletions

View File

@ -99,9 +99,4 @@ interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
* @dev Returns the address of the incentives controller contract
**/
function getIncentivesController() external view returns (IAaveIncentivesController);
/**
* @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
**/
function UNDERLYING_ASSET_ADDRESS() external view returns (address);
}

View File

@ -0,0 +1,120 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {IAToken} from './IAToken.sol';
interface IRewardsAwareAToken is IAToken {
/**
* @dev Emitted after the update rewards state
* @param from The address which can claim rewards
* @param token The reward token address
* @param claimable The amount available to claim
**/
event UserRewardSnapshot(address indexed from, address indexed token, uint256 claimable);
/**
* @dev Emitted after the claim action
* @param from The address performing the claim
* @param token The reward token address
* @param claimed The amount claimed
* @param rewards The rewards amount, this can differ from claimed rewards if is staked
**/
event Claim(address indexed from, address indexed token, uint256 claimed, uint256 rewards);
/**
* @dev Emitted after the update of the user index
* @param from The address with updated user index
* @param token The reward token address
* @param newIndex The latest user index
**/
event UserIndexUpdate(address indexed from, address indexed token, uint256 newIndex);
/**
* @dev Emitted after the update of the reward token index
* @param token The reward token address
* @param newIndex The amount available to claim
**/
event RewardIndexUpdate(address indexed token, uint256 newIndex);
/**
* @dev Emitted after the update of the rewards reserve factor
* @param reserveFactor the new reserve factor
**/
event RewardsReserveFactorUpdate(uint256 reserveFactor);
/**
* @dev Get the current claimable rewards dinamically by calling the external rewards contract and simulate the rewards without storage
* @param token Address of the rewards token
* @param user Address of the account to get current claimable rewards
* @return The claimable rewards of the address passed at the "user" argument
*/
function getClaimableRewards(address token, address user) external view returns (uint256);
/**
* @dev Get the total lifetime rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the total lifetime rewards
* @return The total lifetime rewards of an address, this includes claimed and pending rewards
*/
function getUserRewardsAccrued(address token, address user) external view returns (uint256);
/**
* @dev Get the claimed rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The claimed rewards of an address
*/
function getUserClaimedRewards(address token, address user) external view returns (uint256);
/**
* @dev Get the total lifetime rewards of the aToken contract itself, from contract storage
* @param token Address of the rewards token
* @return The total lifetime rewards, this includes claimed and pending rewards from the aToken contract
*/
function getLifetimeRewards(address token) external view returns (uint256);
/**
* @dev Get lifetime minted rewards of a rewards token
* @param token Address of the rewards token
* @return The lifetime rewards claimed
*/
function getLifetimeClaimed(address token) external view returns (uint256);
/**
* @dev Get the user checkpoint of the aToken contract itself, from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The total lifetime rewards, this includes claimed and pending rewards from the aToken contract
*/
function getUserIndex(address token, address user) external view returns (uint256);
/**
* @dev Get the rewards ERC20 token address by index position
* @param index The position of the rewards, starting from zero up
* @return The rewards ERC20 token address
*/
function getRewardsTokenAddress(uint256 index) external view returns (address);
/**
* @dev Get all the rewards token addresses
* @return The list of rewards token addresseses
*/
function getRewardsTokenAddressList() external view returns (address[9] memory);
/**
* @dev Return the rewards reserve factor to the treasury
* @return reserve factor in percent value
*/
function getRewardsReserveFactor() external view returns (uint256);
/**
* @dev Claim the available reward from the caller and transfers to `msg.sender`
*/
function claim(address token) external;
/**
* @dev Set the rewards reserve factor to the treasury, only allowed by LendingPoolAddressesProvider pool admin
* @param reserveFactor reserve factor in percent value
*/
function setRewardsReserveFactor(uint256 reserveFactor) external;
}

View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
import {RewardsAwareAToken} from '../../protocol/tokenization/RewardsAwareAToken.sol';
import {RewardsToken} from './RewardsToken.sol';
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
/*
* @dev A Simple mockup of RewardsAwareAToken that manages only one reward token. The token have a constant emission per second.
* In this scenario, the rewards ERC20 token is also the Rewards Controller for simplicity.
*/
contract RewardsATokenMock is RewardsAwareAToken {
RewardsToken public _rewardsController;
/**
* @dev Initializes the aToken
* @param pool The address of the lending pool where this aToken will be used
* @param treasury The address of the Aave treasury, receiving the fees on this aToken
* @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
* @param incentivesController The smart contract managing potential incentives distribution
* @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
* @param aTokenName The name of the aToken
* @param aTokenSymbol The symbol of the aToken
*/
function initialize(
ILendingPool pool,
address treasury,
address underlyingAsset,
IAaveIncentivesController incentivesController,
uint8 aTokenDecimals,
string calldata aTokenName,
string calldata aTokenSymbol,
bytes calldata params
) external virtual override initializer {
uint256 chainId;
//solium-disable-next-line
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256(bytes(aTokenName)),
keccak256(EIP712_REVISION),
chainId,
address(this)
)
);
_setName(aTokenName);
_setSymbol(aTokenSymbol);
_setDecimals(aTokenDecimals);
_pool = pool;
_treasury = treasury;
_underlyingAsset = underlyingAsset;
_incentivesController = incentivesController;
// Specific RewardAToken initialization
_rewardsController = RewardsToken(underlyingAsset);
_rewardTokens[0] = underlyingAsset;
emit Initialized(
underlyingAsset,
address(pool),
treasury,
address(incentivesController),
aTokenDecimals,
aTokenName,
aTokenSymbol,
params
);
}
/**
* @dev Due this aToken only holds one reward, is not needed to differentiate the input token
*/
function _computeExternalLifetimeRewards(address token) internal override returns (uint256) {
return _getExternalLifetimeRewards(token);
}
/**
* @dev Due this aToken only holds one reward, is not needed to differentiate the input token
*/
function _getExternalLifetimeRewards(address token) internal view override returns (uint256) {
require(token == address(_rewardsController), 'Reward token mismatch');
uint256 externalLifetime = _rewardsController.getLifetimeRewards(address(this));
return (externalLifetime);
}
/**
* @dev Due this aToken only holds one reward, is not needed to differentiate the input token
*/
function _claimRewardsFromController() internal override {
_rewardsController.claimRewards();
}
}

View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
import {WadRayMath} from '../../protocol/libraries/math/WadRayMath.sol';
import {PercentageMath} from '../../protocol/libraries/math/PercentageMath.sol';
/*
* @dev Mocked token with linear distribution via emission by time distributed to token holders
*/
contract RewardsToken is ERC20 {
using WadRayMath for uint256;
using PercentageMath for uint256;
uint256 private constant EMISION_PER_SECOND = 1 ether;
uint256 public immutable INIT_TIMESTAMP;
uint256 internal lifetimeMintable;
mapping(address => uint256) userMinted;
mapping(address => uint256) userLifetimeRewards;
mapping(address => uint256) userCheckpoint;
constructor() public ERC20('Rewards', 'REW') {
INIT_TIMESTAMP = block.timestamp;
}
/*
* @dev Get accumulated rewards of a user between
*/
function getAccruedRewards(address user, uint256 totalMintable) internal view returns (uint256) {
uint256 balance = balanceOf(user);
if (balance == 0) return 0;
return balance.rayDiv(this.totalSupply()).rayMul(totalMintable.sub(userCheckpoint[user]));
}
/*
* @dev Update lifetimeMintable state and user lifeTimeRewards
*/
function updateMintableEmission(address user) public {
if (user == address(0)) return;
lifetimeMintable = (block.timestamp.sub(INIT_TIMESTAMP)).mul(EMISION_PER_SECOND);
userLifetimeRewards[user] = userLifetimeRewards[user].add(
getAccruedRewards(user, lifetimeMintable)
);
userCheckpoint[user] = lifetimeMintable;
}
/*
* @dev Claim rewards from the extra emission by time
*/
function claimRewards() public returns (bool) {
updateMintableEmission(msg.sender);
uint256 claimableRewards = userLifetimeRewards[msg.sender].sub(userMinted[msg.sender]);
userMinted[msg.sender] = claimableRewards.add(userMinted[msg.sender]);
_mint(msg.sender, claimableRewards);
return true;
}
/*
* @dev Getter for retrieving the expected claimable rewards
*/
function getLifetimeRewards(address user) public view returns (uint256) {
uint256 totalMintable = (block.timestamp.sub(INIT_TIMESTAMP)).mul(EMISION_PER_SECOND);
return userLifetimeRewards[user].add(getAccruedRewards(user, totalMintable));
}
/*
* @dev Getter for retrieving the expected claimable rewards
*/
function getClaimableRewards(address user) external view returns (uint256) {
return getLifetimeRewards(user).sub(userMinted[user]);
}
/*
* @dev Mint an arbitrary amount of REW to the msg.sender and start rewards to msg.sender
*/
function mint(uint256 value) external returns (bool) {
_mint(msg.sender, value);
return true;
}
/*
* @dev Update user distribution state at transfer/mint hook
*/
function _beforeTokenTransfer(
address from,
address to,
uint256
) internal override {
updateMintableEmission(from);
updateMintableEmission(to);
}
}

View File

@ -120,6 +120,7 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
)
external
view
virtual
override
returns (
uint256,
@ -172,6 +173,7 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
)
public
view
virtual
override
returns (
uint256,

View File

@ -103,6 +103,7 @@ library Errors {
string public constant LP_NOT_CONTRACT = '78';
string public constant SDT_STABLE_DEBT_OVERFLOW = '79';
string public constant SDT_BURN_EXCEEDS_BALANCE = '80';
string public constant AT_CALLER_MUST_BE_POOL_ADMIN = '81';
enum CollateralManagerErrors {
NO_ERROR,

View File

@ -70,7 +70,7 @@ contract AToken is
string calldata aTokenName,
string calldata aTokenSymbol,
bytes calldata params
) external override initializer {
) external virtual override initializer {
uint256 chainId;
//solium-disable-next-line
@ -122,7 +122,7 @@ contract AToken is
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyLendingPool {
) public virtual override onlyLendingPool {
uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
_burn(user, amountScaled);
@ -145,7 +145,7 @@ contract AToken is
address user,
uint256 amount,
uint256 index
) external override onlyLendingPool returns (bool) {
) public virtual override onlyLendingPool returns (bool) {
uint256 previousBalance = super.balanceOf(user);
uint256 amountScaled = amount.rayDiv(index);
@ -273,7 +273,7 @@ contract AToken is
/**
* @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
**/
function UNDERLYING_ASSET_ADDRESS() public override view returns (address) {
function UNDERLYING_ASSET_ADDRESS() public view returns (address) {
return _underlyingAsset;
}
@ -307,6 +307,7 @@ contract AToken is
**/
function transferUnderlyingTo(address target, uint256 amount)
external
virtual
override
onlyLendingPool
returns (uint256)
@ -320,7 +321,12 @@ contract AToken is
* @param user The user executing the repayment
* @param amount The amount getting repaid
**/
function handleRepayment(address user, uint256 amount) external override onlyLendingPool {}
function handleRepayment(address user, uint256 amount)
external
virtual
override
onlyLendingPool
{}
/**
* @dev implements the permit function as for

View File

@ -0,0 +1,515 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
import {IDelegationToken} from '../../interfaces/IDelegationToken.sol';
import {Errors} from '../libraries/helpers/Errors.sol';
import {AToken} from './AToken.sol';
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IRewardsAwareAToken} from '../../interfaces/IRewardsAwareAToken.sol';
import {IAToken} from '../../interfaces/IRewardsAwareAToken.sol';
import {WadRayMath} from '../../protocol/libraries/math/WadRayMath.sol';
import {PercentageMath} from '../../protocol/libraries/math/PercentageMath.sol';
/**
* @title Rewards Aware AToken
* @notice AToken aware to claim and distribute rewards from an external rewards controller.
* @author Aave
*/
abstract contract RewardsAwareAToken is AToken, IRewardsAwareAToken {
using SafeERC20 for IERC20;
using WadRayMath for uint256;
using PercentageMath for uint256;
// Precision of the multiplier for calculating distribution percentages
uint256 public constant PRECISION = 24;
// Max rewards allowed
uint256 public constant MAX_REWARD_TOKENS = 9;
// Multiplier for calculating share indexes
uint256 public constant MULTIPLIER = 10**PRECISION;
// Max rewards reserve factor
uint256 public constant MAX_VALID_RESERVE_FACTOR = 10000;
// Rewards Reserve Factor
uint256 internal rewardsReserveFactor;
// token => user => userIndex
mapping(address => mapping(address => uint256)) private _userIndex;
// token => user => userRewardsAccrued
mapping(address => mapping(address => uint256)) private _userRewardsAccrued;
// token => user => userRewardsClaimed
mapping(address => mapping(address => uint256)) private _userRewardsClaimed;
// token => rewardIndex
mapping(address => uint256) private _rewardIndex;
// reward address => lifetime rewards
mapping(address => uint256) private _lifetimeRewards;
// token => lifetimeMinted
mapping(address => uint256) private _lifetimeClaimed;
// reward tokens
address[MAX_REWARD_TOKENS] internal _rewardTokens;
modifier onlyPoolAdmin {
address poolAdmin =
ILendingPoolAddressesProvider(ILendingPool(_pool).getAddressesProvider()).getPoolAdmin();
require(_msgSender() == poolAdmin, Errors.AT_CALLER_MUST_BE_POOL_ADMIN);
_;
}
/**
* @dev Get the current claimable rewards dinamically by calling the external rewards contract and simulate the rewards without storage
* @param token Address of the rewards token
* @param user Address of the account to get current claimable rewards
* @return The claimable rewards of the address passed at the "user" argument
*/
function getClaimableRewards(address token, address user) public view override returns (uint256) {
return _getPendingRewards(token, user);
}
/**
* @dev Get the total lifetime rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the total lifetime rewards
* @return The total lifetime rewards of an address, this includes claimed and pending rewards
*/
function getUserRewardsAccrued(address token, address user)
external
view
override
returns (uint256)
{
return _userRewardsAccrued[token][user];
}
/**
* @dev Get the claimed rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The claimed rewards of an address
*/
function getUserClaimedRewards(address token, address user)
external
view
override
returns (uint256)
{
return _userRewardsClaimed[token][user];
}
/**
* @dev Get lifetime rewards of a rewards token
* @param token Address of the rewards token
* @return The total of lifetime rewards
*/
function getLifetimeRewards(address token) external view override returns (uint256) {
return _getLifetimeRewards(token);
}
/**
* @dev Get lifetime minted rewards of a rewards token
* @param token Address of the rewards token
* @return The lifetime rewards claimed
*/
function getLifetimeClaimed(address token) external view override returns (uint256) {
return _lifetimeClaimed[token];
}
/**
* @dev Get the user checkpoint of the aToken contract itself, from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The total lifetime rewards, this includes claimed and pending rewards from the aToken contract
*/
function getUserIndex(address token, address user) external view override returns (uint256) {
return _userIndex[token][user];
}
/**
* @dev Get the rewards ERC20 token address by index position
* @param index The position of the rewards, starting from zero up
* @return The rewards ERC20 token address
*/
function getRewardsTokenAddress(uint256 index) external view override returns (address) {
return _getRewardsTokenAddress(index);
}
/**
* @dev Get all the rewards token addresses
* @return The list of rewards token addresseses
*/
function getRewardsTokenAddressList()
external
view
override
returns (address[MAX_REWARD_TOKENS] memory)
{
return _rewardTokens;
}
/**
* @dev Claim the available token rewards from the caller and transfers to `msg.sender`
*/
function claim(address token) external override {
_claim(token);
}
/**
* @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
* - Only callable by the LendingPool, as extra state updates there need to be managed
* @param user The owner of the aTokens, getting them burned
* @param receiverOfUnderlying The address that will receive the underlying
* @param amount The amount being burned
* @param index The new liquidity index of the reserve
**/
function burn(
address user,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) public virtual override(AToken, IAToken) onlyLendingPool {
// Unstake
_unstake(UNDERLYING_ASSET_ADDRESS(), amount);
// Update distribution of rewards
_updateDistribution(user);
// burns aTokens
return super.burn(user, receiverOfUnderlying, amount, index);
}
/**
* @dev Mints `amount` aTokens to `user`
* - Only callable by the LendingPool, as extra state updates there need to be managed
* @param user The address receiving the minted tokens
* @param amount The amount of tokens getting minted
* @param index The new liquidity index of the reserve
* @return `true` if the the previous balance of the user was 0
*/
function mint(
address user,
uint256 amount,
uint256 index
) public virtual override(AToken, IAToken) onlyLendingPool returns (bool) {
// Stake
_stake(UNDERLYING_ASSET_ADDRESS(), amount);
// Update distribution of rewards
_updateDistribution(user);
// mint aTokens
return super.mint(user, amount, index);
}
/**
* @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
* assets in borrow(), withdraw() and flashLoan()
* @param target The recipient of the aTokens
* @param amount The amount getting transferred
* @return The amount transferred
**/
function transferUnderlyingTo(address target, uint256 amount)
external
virtual
override(AToken, IAToken)
onlyLendingPool
returns (uint256)
{
_unstake(UNDERLYING_ASSET_ADDRESS(), amount);
_updateDistribution(target);
IERC20(UNDERLYING_ASSET_ADDRESS()).safeTransfer(target, amount);
return amount;
}
/**
* @dev Invoked to execute actions on the aToken side after a repayment.
* @param user The user executing the repayment
* @param amount The amount getting repaid
**/
function handleRepayment(address user, uint256 amount)
external
virtual
override(AToken, IAToken)
onlyLendingPool
{
_stake(UNDERLYING_ASSET_ADDRESS(), amount);
_updateDistribution(user);
}
/**
* @dev Set the rewards reserve factor to the treasury, only allowed by LendingPoolAddressesProvider pool admin
* @param reserveFactor reserve factor in percent value
*/
function setRewardsReserveFactor(uint256 reserveFactor) external override onlyPoolAdmin {
_setRewardsReserveFactor(reserveFactor);
}
/**
* @dev Return the rewards reserve factor to the treasury
* @return reserve factor in percent value
*/
function getRewardsReserveFactor() external view override returns (uint256) {
return rewardsReserveFactor;
}
/**
* @dev Hook to update distributions before token transfer. Called at "burn" and "transferUnderlyingTo".
* @param from address of the `from`
* @param to address of the `to`
*/
function _beforeTokenTransfer(
address from,
address to,
uint256
) internal override {
_updateDistribution(from);
_updateDistribution(to);
}
/**
* @dev Get lifetime rewards of a rewards token
* @param token Address of the rewards token
* @return The total of lifetime rewards
*/
function _getLifetimeRewards(address token) internal view returns (uint256) {
return _lifetimeRewards[token];
}
/**
* @dev Get the rewards ERC20 token address by index position
* @param index The position of the rewards, starting from zero up
* @return The rewards ERC20 token address
*/
function _getRewardsTokenAddress(uint256 index) internal view returns (address) {
if (index > MAX_REWARD_TOKENS) return address(0);
return _rewardTokens[index];
}
/**
* @dev Calculate rewards distribution in proportion of aTokens balance during a timeframe.
* @param aTokensBalance The amounts of aTokens of the user
* @param lastRewardIndex The last reward token index of the distribution percentage
* @param lastUserIndex The last user index of the distribution percentage
* @return The proportional rewards between two distribution percentages indexes
*/
function _getRewards(
uint256 aTokensBalance,
uint256 lastRewardIndex,
uint256 lastUserIndex
) internal pure returns (uint256) {
return aTokensBalance.mul(lastRewardIndex.sub(lastUserIndex)).div(MULTIPLIER);
}
/**
* @dev Calculate the next reward token index, that contains the sums of the percentages of the distribution of the token rewards.
* @param latestRewardIndex The latest reward token index
* @param accruedRewards The accrued rewards during the current distribution
* @param aTokenSupply The total supply of this aToken contract
* @return The sums of the reward indexes plus the latest percentage distribution index of the reward
*/
function _getRewardIndex(
uint256 latestRewardIndex,
uint256 accruedRewards,
uint256 aTokenSupply
) internal pure returns (uint256) {
return latestRewardIndex.add(accruedRewards.mul(MULTIPLIER).div(aTokenSupply));
}
/**
* @dev Getter for virtually calculating the pending rewards of an user without altering the storage of the contract
* @param user Address of the user to lookup for rewards
* @param token The rewards token to lookup for user rewards
* @return Expected token rewards for an user
*/
function _getPendingRewards(address token, address user) internal view returns (uint256) {
uint256 aTokenTotalSupply = totalSupply();
uint256 externalLifetimeRewards = _getExternalLifetimeRewards(token);
uint256 rewardAccrued = externalLifetimeRewards.sub(_lifetimeRewards[token]);
uint256 rewardAssetIndex = _rewardIndex[token];
if (aTokenTotalSupply != 0 && rewardAccrued != 0) {
rewardAssetIndex = _getRewardIndex(_rewardIndex[token], rewardAccrued, aTokenTotalSupply);
}
uint256 userLatestRewards =
_getRewards(balanceOf(user), rewardAssetIndex, _userIndex[token][user]);
return
_userRewardsAccrued[token][user].add(userLatestRewards).sub(_userRewardsClaimed[token][user]);
}
/**
* @dev Retrieve the latest rewards distribution to an user and update the distribution of a token rewards at storage
* @param user The `user` address to update the rewards index and retrieve latest reward distribution
* @param token The reward token to lookup the distribution
* @param aTokenBalance The aToken balance of the user determines his share during the distribution
* @param aTokenSupply The current total supply of this aToken
*/
function _updateRewardDistribution(
address user,
address token,
uint256 aTokenBalance,
uint256 aTokenSupply
) private {
uint256 userRewardsIndex = _userIndex[token][user];
uint256 externalLifetimeRewards = _computeExternalLifetimeRewards(token);
uint256 rewardAccrued = externalLifetimeRewards.sub(_lifetimeRewards[token]);
uint256 previousRewardIndex = _rewardIndex[token];
uint256 rewardIndex = _getRewardIndex(previousRewardIndex, rewardAccrued, aTokenSupply);
// Check reward index
if (rewardIndex != previousRewardIndex) {
_rewardIndex[token] = rewardIndex;
_setLifetimeRewards(token, externalLifetimeRewards);
emit RewardIndexUpdate(token, rewardIndex);
}
// Check user reward index
if (userRewardsIndex != rewardIndex) {
_userIndex[token][user] = rewardIndex;
emit UserIndexUpdate(user, token, rewardIndex);
uint256 userRewardAccrued = _getRewards(aTokenBalance, rewardIndex, userRewardsIndex);
if (userRewardAccrued != 0) {
_userRewardsAccrued[token][user] = _userRewardsAccrued[token][user].add(userRewardAccrued);
emit UserRewardSnapshot(user, token, userRewardAccrued);
}
}
}
/**
* @dev Update the distribution of each rewards configured at this aToken
* @param user The `user` address to update the rewards index and retrieve latest reward distribution
*/
function _updateDistribution(address user) internal {
uint256 aTokenBalance = balanceOf(user);
if (aTokenBalance == 0) {
return;
}
uint256 aTokenSupply = totalSupply();
if (aTokenSupply == 0) {
return;
}
for (uint256 index; index < MAX_REWARD_TOKENS; index++) {
address rewardToken = _rewardTokens[index];
if (rewardToken == address(0)) break;
_updateRewardDistribution(user, rewardToken, aTokenBalance, aTokenSupply);
}
}
/**
* @dev Set token rewards claimed from an `user` address
* @param user The `user` address to sum and set the claimed rewards
* @param token The claimed reward `token` address
* @param claimed The amount of `claimed` rewards by the `user`
*/
function _setUserRewardsClaimed(
address user,
address token,
uint256 claimed
) private {
_userRewardsClaimed[token][user] = _userRewardsClaimed[token][user].add(claimed);
_lifetimeClaimed[token] = _lifetimeClaimed[token].add(claimed);
}
/**
* @dev Claim the available rewards from the caller and transfers to `msg.sender`
* @param token Reward adresss to lookup rewards and claim
*/
function _claim(address token) private {
// Mint the available rewards from the external rewards controller to this aToken contract implementation
_claimRewardsFromController();
// Update aToken and user rewards
_updateDistribution(msg.sender);
// Get the remaining tokens to claim
uint256 accruedRewards =
_userRewardsAccrued[token][msg.sender].sub(_userRewardsClaimed[token][msg.sender]);
if (accruedRewards > 0) {
// Track the claimed rewards
_setUserRewardsClaimed(msg.sender, token, accruedRewards);
// Unstake reward token, if needed
uint256 unstaked = _unstake(token, accruedRewards);
uint256 userRewards = unstaked;
// Transfer rewards to treasury
if (_treasury != address(0)) {
uint256 reserveRewards = unstaked.percentMul(rewardsReserveFactor);
if (reserveRewards > 0) {
userRewards = unstaked.sub(reserveRewards);
IERC20(token).transfer(_treasury, reserveRewards);
}
}
// Transfer rewards to user
IERC20(token).transfer(msg.sender, userRewards);
emit Claim(msg.sender, token, accruedRewards, userRewards);
}
}
/**
* @dev Set the lifetime token lifetime rewards
* @param token The reward token address
* @param amount The amount of lifetime rewards
*/
function _setLifetimeRewards(address token, uint256 amount) private {
_lifetimeRewards[token] = amount;
}
/**
* @dev External call to retrieve the lifetime rewards of the aToken contract to the external Rewards Controller contract
* @notice Should only mutate the state outside RewardsAwareAToken abstract contract
* @param token Reward adresss to lookup available rewards
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
*/
function _computeExternalLifetimeRewards(address token) internal virtual returns (uint256);
/**
* @dev External view call to retrieve the lifetime rewards of the aToken contract to the external Rewards Controller contract
* @param token Reward adresss to lookup available rewards
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
*/
function _getExternalLifetimeRewards(address token) internal view virtual returns (uint256);
/**
* @dev External call to claim the lifetime accrued rewards of the aToken contract to the external Rewards Controller contract
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
* @notice WIP pending to check virtual balance versus claimed
*/
function _claimRewardsFromController() internal virtual;
/**
* @dev External call to stake a token of the aToken contract
* @notice Optional, to be implemented by the contract that inherits this abstract contract RewardsAwareAToken,if needed.
* @param amount to stake
*/
function _stake(address, uint256 amount) internal virtual returns (uint256) {
return amount;
}
/**
* @dev External call to unstake a token of the aToken contract
* @notice Optional, to be implemented by the contract that inherits this abstract contract RewardsAwareAToken, if needed.
* @param amount to unstake
*/
function _unstake(address, uint256 amount) internal virtual returns (uint256) {
return amount;
}
/**
* @dev Set the rewards reserve factor to the treasury
* @param reserveFactor reserve factor in percent value
*/
function _setRewardsReserveFactor(uint256 reserveFactor) internal {
require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR);
rewardsReserveFactor = reserveFactor;
emit RewardsReserveFactorUpdate(rewardsReserveFactor);
}
}

264
package-lock.json generated
View File

@ -3123,6 +3123,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
@ -5534,7 +5535,8 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"functional-red-black-tree": {
"version": "1.0.1",
@ -6373,6 +6375,36 @@
"@ethersproject/strings": ">=5.0.0-beta.130"
}
},
"@ethersproject/abstract-provider": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.0.8.tgz",
"integrity": "sha512-fqJXkewcGdi8LogKMgRyzc/Ls2js07yor7+g9KfPs09uPOcQLg7cc34JN+lk34HH9gg2HU0DIA5797ZR8znkfw==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/bignumber": "^5.0.13",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/networks": "^5.0.7",
"@ethersproject/properties": "^5.0.7",
"@ethersproject/transactions": "^5.0.9",
"@ethersproject/web": "^5.0.12"
}
},
"@ethersproject/abstract-signer": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.10.tgz",
"integrity": "sha512-irx7kH7FDAeW7QChDPW19WsxqeB1d3XLyOLSXm0bfPqL1SS07LXWltBJUBUxqC03ORpAOcM3JQj57DU8JnVY2g==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/abstract-provider": "^5.0.8",
"@ethersproject/bignumber": "^5.0.13",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/properties": "^5.0.7"
}
},
"@ethersproject/address": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.9.tgz",
@ -6387,6 +6419,16 @@
"@ethersproject/rlp": "^5.0.7"
}
},
"@ethersproject/base64": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.0.7.tgz",
"integrity": "sha512-S5oh5DVfCo06xwJXT8fQC68mvJfgScTl2AXvbYMsHNfIBTDb084Wx4iA9MNlEReOv6HulkS+gyrUM/j3514rSw==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/bytes": "^5.0.9"
}
},
"@ethersproject/bignumber": {
"version": "5.0.13",
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.13.tgz",
@ -6454,6 +6496,16 @@
"dev": true,
"optional": true
},
"@ethersproject/networks": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.0.7.tgz",
"integrity": "sha512-dI14QATndIcUgcCBL1c5vUr/YsI5cCHLN81rF7PU+yS7Xgp2/Rzbr9+YqpC6NBXHFUASjh6GpKqsVMpufAL0BQ==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/logger": "^5.0.8"
}
},
"@ethersproject/properties": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.7.tgz",
@ -6518,6 +6570,20 @@
"@ethersproject/signing-key": "^5.0.8"
}
},
"@ethersproject/web": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.0.12.tgz",
"integrity": "sha512-gVxS5iW0bgidZ76kr7LsTxj4uzN5XpCLzvZrLp8TP+4YgxHfCeetFyQkRPgBEAJdNrexdSBayvyJvzGvOq0O8g==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/base64": "^5.0.7",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/properties": "^5.0.7",
"@ethersproject/strings": "^5.0.8"
}
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -7830,14 +7896,6 @@
"dev": true,
"requires": {
"node-gyp-build": "^4.2.0"
},
"dependencies": {
"node-gyp-build": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
"dev": true
}
}
},
"bytes": {
@ -7938,6 +7996,16 @@
}
}
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"caniuse-lite": {
"version": "1.0.30001174",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz",
@ -8456,6 +8524,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
@ -8681,6 +8750,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
@ -10850,7 +10920,8 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"functional-red-black-tree": {
"version": "1.0.1",
@ -10858,6 +10929,17 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"get-intrinsic": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz",
"integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@ -10965,6 +11047,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@ -11002,7 +11085,8 @@
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"has-to-string-tag-x": {
"version": "1.4.1",
@ -11275,7 +11359,8 @@
"is-callable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
"dev": true
},
"is-ci": {
"version": "2.0.0",
@ -11298,7 +11383,8 @@
"is-date-object": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
"dev": true
},
"is-descriptor": {
"version": "1.0.2",
@ -11347,7 +11433,8 @@
"is-negative-zero": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
"integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w=="
"integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
"dev": true
},
"is-object": {
"version": "1.0.2",
@ -11376,6 +11463,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
@ -11391,6 +11479,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
@ -12233,7 +12322,8 @@
"object-inspect": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
"integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw=="
"integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
"dev": true
},
"object-is": {
"version": "1.1.4",
@ -12243,33 +12333,13 @@
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
},
"object-visit": {
"version": "1.0.1",
@ -12284,32 +12354,12 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
"integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"object.getownpropertydescriptors": {
@ -12870,47 +12920,16 @@
"unbox-primitive": "^1.0.0"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
}
},
"string.prototype.trimend": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
}
}
}
@ -12999,15 +13018,6 @@
"uuid": "^3.3.2"
}
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -13666,34 +13676,13 @@
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"string.prototype.trimend": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz",
"integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3"
@ -13703,6 +13692,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz",
"integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3"
@ -13889,6 +13879,15 @@
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
}
}
},
@ -14250,14 +14249,6 @@
"dev": true,
"requires": {
"node-gyp-build": "^4.2.0"
},
"dependencies": {
"node-gyp-build": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
"dev": true
}
}
},
"utf8": {
@ -15236,6 +15227,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
@ -15792,6 +15784,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@ -15817,7 +15810,8 @@
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"has-to-string-tag-x": {
"version": "1.4.1",