mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
feat: Reward aware aToken contracts
This commit is contained in:
parent
b4ed523a64
commit
55a9880c79
|
@ -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);
|
||||
}
|
||||
|
|
120
contracts/interfaces/IRewardsAwareAToken.sol
Normal file
120
contracts/interfaces/IRewardsAwareAToken.sol
Normal 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;
|
||||
}
|
104
contracts/mocks/tokens/RewardsATokenMock.sol
Normal file
104
contracts/mocks/tokens/RewardsATokenMock.sol
Normal 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();
|
||||
}
|
||||
}
|
97
contracts/mocks/tokens/RewardsToken.sol
Normal file
97
contracts/mocks/tokens/RewardsToken.sol
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
515
contracts/protocol/tokenization/RewardsAwareAToken.sol
Normal file
515
contracts/protocol/tokenization/RewardsAwareAToken.sol
Normal 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
264
package-lock.json
generated
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue
Block a user